为问题添加了方法,但是获取了未定义的方法错误

sno*_*ler 10 ruby ruby-on-rails ruby-on-rails-4

我在轨道问题中有以下代码:

module DestroyExclusion
  extend ActiveSupport::Concern

  def self.destroy_exclusion_on_category_deletion(academy_id,product_id,category_id)
    if(category_id == nil)
      if exists?(:academy_id => academy_id, :product_id => product_id)
        where(:academy_id => academy_id, :product_id => product_id).first.destroy
      end
    elsif(product_id == nil)
      if exists?(:academy_id => academy_id, :category_id => category_id)
        where(:academy_id => academy_id, :category_id => category_id).first.destroy
      end
    end

  end
end
Run Code Online (Sandbox Code Playgroud)

在这些类中使用:

class ExcludeProduct < ActiveRecord::Base
  include DestroyExclusion
  belongs_to :academy
  belongs_to :product


end

class ExcludeCategory < ActiveRecord::Base
  include DestroyExclusion
  belongs_to :academy
  belongs_to :category

end
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试调用该方法时,我得到一个未定义的方法错误.任何想法为什么会这样?这是我调用方法的示例:

  def destroy_exclusions
    category_record = Category.find(self.category_id)
    if !self.product_id.blank?
      academies = category_record.parent_academies
      academies.each do |academy|
        ExcludeProduct.destroy_exclusion_on_category_deletion(academy.id, self.product_id,nil)
      end
    else
      products = category_record.child_products
      products.each do |product|
        ExcludeProduct.destroy_exclusion_on_category_deletion(self.academy_id, product.id,nil)
      end
    end
  end
Run Code Online (Sandbox Code Playgroud)

这是我得到的错误的一个例子(从运行我的规范):

 1) AcademyCategoryProduct successfully destroys exclusions
     Failure/Error: category_product.destroy_exclusions
     NoMethodError:
       undefined method `destroy_exclusion_on_category_deletion' for #<Class:0x007fc532c0ae50>
     # /Library/Ruby/Gems/2.0.0/gems/activerecord-4.1.6/lib/active_record/dynamic_matchers.rb:26:in `method_missing'
     # ./app/models/academy_category_product.rb:13:in `block in destroy_exclusions'
     # /Library/Ruby/Gems/2.0.0/gems/activerecord-4.1.6/lib/active_record/relation/delegation.rb:46:in `each'
     # /Library/Ruby/Gems/2.0.0/gems/activerecord-4.1.6/lib/active_record/relation/delegation.rb:46:in `each'
     # ./app/models/academy_category_product.rb:12:in `destroy_exclusions'
     # ./spec/models/academy_category_product_spec.rb:25:in `block (2 levels) in <top (required)>'
Run Code Online (Sandbox Code Playgroud)

有问题的specfile:

require 'rails_helper'

describe AcademyCategoryProduct do

  let(:product) { FactoryGirl.create(:academy_product) }
  let(:category) { FactoryGirl.create(:category) }
  let(:subcategory) {FactoryGirl.create(:category, :parent_id => category.id)}
  let(:academy) { FactoryGirl.create(:academy) }
  let(:category_product) { FactoryGirl.create(:academy_category_product, :category_id => subcategory.id, :product_id => product.id, :academy_id => nil) }
  let(:academy_category) { FactoryGirl.create(:academy_category_product, :category_id => category.id, :academy_id => academy.id, :product_id => nil ) }
  let(:exclude_product) { FactoryGirl.create(:exclude_product, :product_id => product.id, :academy_id => academy.id) }
  let(:exclude_category) { FactoryGirl.create(:exclude_category, :category_id => subcategory.id, :academy_id => academy.id) }

  before(:each) do
    category.reload
    academy.reload
    exclude_category.reload
    exclude_product.reload
    category_product.reload
    academy_category.reload
    exclude_product.reload
  end

  it "successfully destroys exclusions" do
    category_product.destroy_exclusions
    expect(ExcludeProduct.first).to eql(nil)
  end

  it "successfully destroys category exclusions" do
    academy_category.destroy_category_exclusions
    expect(ExcludeCategory.first).to eql(nil)
  end
end
Run Code Online (Sandbox Code Playgroud)

编辑 - 根据goddamnyouryan的回答,我对我的关注进行了以下更改:

require 'active_support/concern'
module DestroyExclusion
  extend ActiveSupport::Concern

  class_methods do

    def self.destroy_exclusion_on_category_deletion(academy_id,product_id,category_id)
      if(category_id == nil)
        if exists?(:academy_id => academy_id, :product_id => product_id)
          where(:academy_id => academy_id, :product_id => product_id).first.destroy
        end
      elsif(product_id == nil)
        if exists?(:academy_id => academy_id, :category_id => category_id)
          where(:academy_id => academy_id, :category_id => category_id).first.destroy
        end
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

但是我收到以下错误:

Users/karuna/Documents/Projects/catalog/app/models/concerns/destroy_exclusion.rb:5:in `<module:DestroyExclusion>': undefined method `class_methods' for DestroyExclusion:Module (NoMethodError)
Run Code Online (Sandbox Code Playgroud)

K M*_*lam 14

在Rails 4(和Rails 5)中,如果你想在你关注的内部定义一个类方法,那么你必须在一个内部定义该方法:

module ClassMethods
 . . . 
end
Run Code Online (Sandbox Code Playgroud)

这就是你要找的东西.此module ClassMethods块中包含的代码将添加到Class本身.

因此,您修改后的关注代码应如下所示:

module DestroyExclusion
  extend ActiveSupport::Concern

  module ClassMethods
    def destroy_exclusion_on_category_deletion(academy_id,product_id,category_id)
      if(category_id == nil)
        if exists?(:academy_id => academy_id, :product_id => product_id)
          where(:academy_id => academy_id, :product_id => product_id).first.destroy
        end
      elsif(product_id == nil)
        if exists?(:academy_id => academy_id, :category_id => category_id)
          where(:academy_id => academy_id, :category_id => category_id).first.destroy
        end
      end

    end
  end
end
Run Code Online (Sandbox Code Playgroud)

本教程对Rails 4中的问题有一个简单而好的解释!

  • 这是错误的,即使它有效.如果你决定使用ActiveSupport,你应该使用`class_methods`而不是使用`module ClassMethods`.@goddamnyouryan给出了正确答案.http://api.rubyonrails.org/classes/ActiveSupport/Concern.html - 在这里你可以看到有和没有ActiveSupport的两个例子. (3认同)

god*_*yan 8

您需要将这些方法嵌套在一个class_methods do块中.请参阅ActiveSupport :: Concern文档

就像是:

module DestroyExclusion
  extend ActiveSupport::Concern

  class_methods do
    def destroy_exclusion_on_category_deletion(etc)
      # omitted
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

  • Rails 4,@ KM Rakibul Islam的答案奏效了.谢谢你的帮助 (2认同)
  • `class_methods`仅适用于rails 4.2.0及更高版本 (2认同)

Shi*_*aur 5

如果你想在你的关注点中定义类方法:

对于 Rails 版本 < 4.2.0,

module DestroyExclusion
  extend ActiveSupport::Concern

  module ClassMethods
    def destroy_exclusion_on_category_deletion(etc)
      # some code
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

对于 Rails 版本 >= 4.2.0

module DestroyExclusion
  extend ActiveSupport::Concern

  class_methods do
    def destroy_exclusion_on_category_deletion(etc)
      # some code
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

Ruby 的做事方式

module DestroyExclusion
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def destroy_exclusion_on_category_deletion(etc)
      # some code
    end
  end
end
Run Code Online (Sandbox Code Playgroud)