Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Added method to a concern, but getting undefined method errors

I have the following code in a rails concern:

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

which is used in these classes:

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

However, when I try to call the method, I get an undefined method error. Any ideas why this may be? Here is an example of where I am calling the method:

  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

here is an example of the error I am getting (from running my specs):

 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)>'

The specfile in question:

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

EDIT - I made the following changes to my concern as per goddamnyouryan's answer:

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

but I get the following error:

Users/karuna/Documents/Projects/catalog/app/models/concerns/destroy_exclusion.rb:5:in `<module:DestroyExclusion>': undefined method `class_methods' for DestroyExclusion:Module (NoMethodError)
like image 677
snowflakekiller Avatar asked Aug 12 '15 07:08

snowflakekiller


2 Answers

If you want to define class methods inside your concern:

For Rails version < 4.2.0,

module DestroyExclusion
  extend ActiveSupport::Concern

  module ClassMethods
    def destroy_exclusion_on_category_deletion(etc)
      # some code
    end
  end
end

For Rails version >= 4.2.0

module DestroyExclusion
  extend ActiveSupport::Concern

  class_methods do
    def destroy_exclusion_on_category_deletion(etc)
      # some code
    end
  end
end

Ruby way of doing thing

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

  module ClassMethods
    def destroy_exclusion_on_category_deletion(etc)
      # some code
    end
  end
end
like image 199
Shivansh Gaur Avatar answered Sep 20 '22 16:09

Shivansh Gaur


In Rails 4 (and Rails 5), if you want to define a class method inside your concern, then you have to just define that method inside a:

module ClassMethods
 . . . 
end

which is what you are looking for. The code contained within this module ClassMethods block will be added to the Class itself.

So, your modified concern code should look like this:

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

This tutorial has a simple and nice explanation about concerns in Rails 4!

like image 30
K M Rakibul Islam Avatar answered Sep 18 '22 16:09

K M Rakibul Islam