Wrapping Rails ActiveSupport::Concern into other Concerns - ruby-on-rails

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.

Related

Undefined method belongs_to usign Rails concern, why?

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.

Using ActiveSupport::Concern and create accessors and validations dynamically

I want to use ActiveSupport::Concern in order to extend the ActiveRecord::Base funcionality, dynamically.
So I have a class (in app/models/foo.rb)
class Foo < ActiveRecord::Base
end
and in lib/activ_record_extention.rb I have
module ActiveRecordExtension
extend ActiveSupport::Concern
module ClassMethods
c= atrr_name
attr_accessible c.to_sym
end
end
ActiveRecord::Base.send(:include, ActiveRecordExtension)
But when I run the server I get :
undefined method `attr_accessible' for ActiveRecord Extension:Module (NoMethodError)
I guess you need something like this:
module ActiveRecordExtension
extend ActiveSupport::Concern
included do
attr_accessible # Still need to call the API method
def mass_assignment_authorizer
# Here we can access object's attributes
super << some_attr
end
end
end
ActiveRecord::Base.send(:include, ActiveRecordExtension)
I updated my answer.

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.

Error from moving acts_as_taggable into an included module

If I write this, everything works fine:
class A < ActiveRecord::Base
acts_as_taggable
end
But if I take acts_as_taggable and put it into a module that class A includes, I get an error:
module B
def self.included(base)
base.class_eval do
extend ClassMethods
include InstanceMethods
end
end
module ClassMethods
acts_as_taggable
end
module InstanceMethods
end
end
class A < ActiveRecord::Base
include B
The error from the code above is:
undefined local variable or method `acts_as_taggable' for C::ClassMethods:Module
Is it not correct to call acts_as_taggable from an included module?
Does it need to be in the class definition itself?
When Ruby loads the file containing your module B and reaches the acts_as_taggable line, it will try to execute the acts_as_taggable class method of ClassMethods (which doesn't exist because it is actually a class method of ActiveRecord::Base).
You can use the included method to call acts_as_taggable when your module is included though. included is passed the class the module is being included in, so the following will work:
module B
def self.included(base)
base.acts_as_taggable
# ...
end
# ...
end

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