Undefined method belongs_to usign Rails concern, why? - ruby-on-rails

I'm trying to extend a rails model from a gem.
Using concern I've been able to extend class methods but I cannot extend associations. included do returns undefined method belongs_to. I think Rails cannot load the class properly...
The model is in a engine and I'm trying to access it from my gem.
Here is the code:
# mygem/config/initializers/mymodel_extension.rb
require 'active_support/concern'
module MymodelExtension
extend ActiveSupport::Concern
# included do
# belongs_to :another
# end
class_methods do
def swear
return "I'm not doing it again"
end
end
end
class Myengine::Mymodel
include MymodelExtension
end
From command line:
Myengine::Mymodel.swear
# => "I'm not doing it again"
If I uncomment the included do I get this undefined method 'belongs_to' for Myengine::Mymodel:Class (NoMethodError) error.

Myengine::Mymodelclass should inherit from ActiveRecord::Base to have belongs_to method defined.
ActiveRecord::Base includes bunch of modules, one of which is Associations, where belongs_to association is defined.

Related

gem module method cant be included to ActiveRecord::Base

I have a module in my gem
module joinSelect
def self.with
puts 'with called'
end
ActiveRecord::Base.send :include, self
end
but I am unable to access method with in any of model classes
irb(main):015:0> User.with
NoMethodError: undefined method `with' for User (call 'User.connection' to establish a connection):Class
I have tried putting
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include JoinSelect
#or
extend JoinSelect
end
``
doesen't work. How can I get "with" accessible on ApplicationRecord ?
Thanks in advance.
I'll recommend to include the module with your code only in the classes that will need that functionality. Including your code in ActiveRecord::Base is really not recommended, other gems you may use may conflict with it.
If you need your code to be available on all your ActiveRecord models, then define it in your ApplicationRecord. Since all your models will inherit from it, all will gain the functionality.
If you want to add a class method in your AR class, create a module with the function and extend it from your class:
module A
def foo
"Hi"
end
end
class User < ApplicationRecord
extend A
end
User.foo # => "Hi"
If you need to do more things, like declaring scopes, using ActiveRecord hooks, etc. then you'll need to use concerns, see here
define it without self in module
module JoinSelect
def with
puts 'with called'
end
end
and in ApplicationRecord use extend to include it as a class method
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend JoinSelect
end

Wrapping Rails ActiveSupport::Concern into other Concerns

I have created a ActiveSupport::Concern, and inside a ClassMethods method I'm calling the mount_uploader method from Carrierwave
/lib/my_concern.rb
require 'active_support/concern'
module MyConcern
extend ActiveSupport::Concern
included do
end
module ClassMethods
def cover_image
attr_accessible :cover_image
mount_uploader :cover_image, "CoverImageUploader"
end
end
ActiveRecord::Base.send(:include, MyConcern)
/app/models/my_model.rb
class MyModel < ActiveRecord::Base
cover_image
end
It all works well until I change something (I mean anything) in the class that I called the method, then I gives me:
undefined method `cover_image_url' for #<MyModel:0x007fa0b043dbb0>
I believe is something related in how it loads those methods.
Ps: I'm using Thin in my dev enviroment, and every time that gives me the error and I restart the server it come back to work.

Creating a gem with associated Rails models

I would like to package a gem that includes ActiveRecord models that can be associated with models in the existing Rails application. I've been trying to follow the code for acts_as_taggable_on, but I'm running into some problems getting the associations to work.
My gem is called Kitchen. My model defined in the gem is Dish, and I want to add a polymorphic association (as Cook) to a model in the main application such as User.
In lib/kitchen.rb
require "active_record"
require "kitchen/dish.rb"
require "kitchen/cook.rb"
In lib/kitchen/dish.rb
module Kitchen
class Dish < ::ActiveRecord::Base
belongs_to :cook, :polymorphic => true
end
end
In lib/kitchen/cook.rb (lifting code from http://guides.rubyonrails.org/plugins.html#add-an-acts_as-method-to-active-record without much understanding)
module Kitchen
module Cook
extend ActiveSupport::Concern
included do
end
module ClassMethods
def acts_as_cook
class_eval do
has_many :dishes, :as => :cook
end
end
end
end
end
ActiveRecord::Base.send :include, Kitchen::Cook
Finally, I've migrated everything in my dummy app, and include the association in spec/dummy/app/models/user.rb
class User < ActiveRecord::Base
acts_as_cook
end
I'm getting an error any time I try to access user.dishes for a User instance:
NameError:
uninitialized constant User::Dish
Any idea what's missing?
Try this maybe:
has_many :dishes, :as => :cook, :class_name => 'Kitchen::Dish'

Rails/Ruby Mixin brings unwanted other class methods

Just hoping someone can explain this for me..
I have a Site class that imports a module:
Site
class Site < ActiveRecord::Base
include TrackableChanges
...
In trackable_changes.rb
I have this code..
module TrackableChanges
include ActiveSupport::Callbacks
def self.included(base)
# Initialize module.
base.has_many :change_requests, :as => :model, :dependent => :destroy
#Callbacks
base.before_save :before_save_change_request
base.after_save :after_save_change_request
base.before_destroy :before_destroy_change_request
Facility
end
...
The reference to Facility is really confusing me (I put a trivial reference in here..). Basically in Facility.rb I have this..
class Facility < ActiveRecord::Base
acts_as_citier
acts_as_citier looks a bit like this:
module Citier
def self.included(base)
# When a class includes a module the module’s self.included method will be invoked.
base.send :extend, ClassMethods
end
end
ActiveRecord::Base.send :include, Citier
Now.. Just by referencing Facility in my initial module it is going to the acts_as_citier gem and extending the ActiveRecord of my Site class. I want the acts_as_citier gem for my Facility but NOT for my Site.
Can anyone help stop this include trail bringing in this unwanted reference!?
EDIT
Seems like I can't reference the class Facility at all without it bringing in the ActiveRecord class additions that is defined in the Facility via it's reference to the gem act_as_citier
cities does this...
ActiveRecord::Base.send :include, Citier
class Facility < ActiveRecord::Base
#attr_accessible :name, :type, :facility_type_id, :accessibility_id, :tldc_id, :site_id, :tldc_approved
acts_as_citier if self.to_s == 'Facility' #Protects other classes from accidently getting the AR additions
Adding a condition to the include stops the acts_as_citier gem from extending any class that references 'Facility'.
I'm assuming the include on my Site class > runs through the module > which when it hits a reference to Facility class > runs through the Facility.rb > which runs through acts_as_citier and then hits the extend to active record line. It helped me remembering that every part of a .rb file is an executable.

Extend model in plugin with "has_many" using a module

I have a some code in an engine style plugin which includes some models. In my app I want to extend one of these models. I have managed to add both instance and class methods to the model in question by including a module from within an initializer.
However I cannot seem to add associations, callbacks etc. I get a 'method not found' error.
/libs/qwerty/core.rb
module Qwerty
module Core
module Extensions
module User
# Instance Methods Go Here
# Class Methods
module ClassMethods
has_many :hits, :uniq => true # no method found
before_validation_on_create :generate_code # no method found
def something # works!
"something"
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
end
end
end
/initializers/qwerty.rb
require 'qwerty/core/user'
User.send :include, Qwerty::Core::Extensions::User
You should be able to do this. A little more concise IMHO.
module Qwerty::Core::Extensions::User
def self.included(base)
base.class_eval do
has_many :hits, :uniq => true
before_validation_on_create :generate_code
end
end
end
I think this should work
module Qwerty
module Core
module Extensions
module User
# Instance Methods Go Here
# Class Methods
module ClassMethods
def relate
has_many :hits, :uniq => true # no method found
before_validation_on_create :generate_code # no method found
end
def something # works!
"something"
end
end
def self.included(base)
base.extend(ClassMethods).relate
end
end
end
end
end
The old code is wrong cause the validation and the association are called upon module loading, and this module knows nothing about ActiveRecord. That's a general aspect of Ruby, code inside class or module bodies is called directly when loaded. You don't want that. To get around that you can use the above solution.
In Rails 3, this sounds like a good use case for ActiveSupport::Concern:
module Qwerty::Core::Extensions::User
extend ActiveSupport::Concern
included do
has_many :hits, :uniq => true
before_validation_on_create :generate_code
end
end
class User
include Querty::Core::Extensions::User
# ...
end
Here are the ActiveSupport::Concern docs and the most helpful blog article on it I've found.

Resources