I have done a module in lib directory in ruby on rails application
its like
module Select
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def select_for(object_name, options={})
#does some operation
self.send(:include, Selector::InstanceMethods)
end
end
I called this in a controller like
include Selector
select_for :organization, :submenu => :general
but I want to call this in a function
i.e
def select
#Call the module here
end
Let's clarify: You have a method defined in a module, and you want that method to be used in an instance method.
class MyController < ApplicationController
include Select
# You used to call this in the class scope, we're going to move it to
# An instance scope.
#
# select_for :organization, :submenu => :general
def show # Or any action
# Now we're using this inside an instance method.
#
select_for :organization, :submenu => :general
end
end
I'm going to change your module slightly. This uses include instead of extend. extend is for adding class methods, and include it for adding instance methods:
module Select
def self.included(base)
base.class_eval do
include InstanceMethods
end
end
module InstanceMethods
def select_for(object_name, options={})
# Does some operation
self.send(:include, Selector::InstanceMethods)
end
end
end
That will give you an instance method. If you want both instance and class methods, you just add the ClassMethods module, and use extend instead of include:
module Select
def self.included(base)
base.class_eval do
include InstanceMethods
extend ClassMethods
end
end
module InstanceMethods
def select_for(object_name, options={})
# Does some operation
self.send(:include, Selector::InstanceMethods)
end
end
module ClassMethods
def a_class_method
end
end
end
Does that clear things up? In your example you defined a module as Select but included Selector in your controller...I just used Select in my code.
Related
I want to have separate logs for my app. I created the following module:
module MyApp
module MyLog
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def logger
##logger ||= Logger.new("#{Rails.root}/log/#{self.name.underscore}.log")
end
end
end
end
Then, in any of my models, I can add:
include MyApp::MyLog
and use it as (log file will appear in .../log/cat.log):
Cat.logger.info 'test'
I tried to use this method included on Cat and Dog models, and I have this result:
Cat.new.logger
# => #<Logger:0x007fe4516cf0b0 #progname=nil, ... #dev=#<File:/.../log/cat.log>, ...
Dog.new.logger
# => #<Logger:0x007fe4516cf0b0 #progname=nil, ... #dev=#<File:/.../log/cat.log>, ... (the same)
If I try to use my logger for Dog model first, I will have a log file with the name dog (/dog.log).
How can I set class variable ##logger from a module for each class with the correct initialized logger?
Do not use class variable, use instance_variable that is attached to the class.
module MyApp
module MyLog
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def logger
#logger ||= Logger.new("#{Rails.root}/log/#{self.name.underscore}.log")
end
end
end
end
Example:
module A
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def logger
puts #logger
#logger ||= name
end
end
end
class B
include A
end
class C
include A
end
B.logger
#
B.logger
# B
C.logger
#
B.logger
# B
C.logger
# C
First time you call the method it is nil, thus the empty line, second time you call method the value equals to class name, B, and if called on new class it is again nil, check also this answer
Ruby class instance variable vs. class variable
New to Ruby\Rails, shame on me :(
I'm developing an engine for personal use (simple admin panel). What I want, is to be able to config my main app's models, like this:
class User < ActiveRecord::Base
include Entropy::Configurable
entropy_config do
form_caption 'Editing user'
end
end
And then in engine's templates do this:
<h1><%= #object.entropy_config :form_caption %></h1>
Engine's module:
module Entropy
module Configurable
def self.included(base)
## to call entropy_config in model class
base.send :extend, ClassMethods
end
def entropy_config(arg)
## ... I'm missing this part
end
module ClassMethods
##config = { ... }
def entropy_config (&block)
class_eval &block
end
def form_caption(arg)
// skipping class identification
##config[:user][:form_caption] = arg
end
end
end
end
The problem is that I can not get access to ##config from Configurable module, actually when I call entropy_config on #object. What I'm doing wrong?
First of all you've doing it wrong. Rails is on of the frameworks that pushed a lot on the MVC architecture. Having your model know about form captions is wrong. For that I would use the rails i18n gem. For the sake of the argument here's some untested code that will probably answer your question:
module Entropy
module Configurable
def self.included(base)
## to call entropy_config in model class
base.send :extend, ClassMethods
end
def entropy_config(key)
self.class.config[:user][key]
end
module ClassMethods
cattr_accessor :config
def entropy_config (&block)
self.config ||= {}
class_eval &block
end
def form_caption(arg)
// skipping class identification
self.config[:user][:form_caption] = arg
end
end
end
end
see http://apidock.com/rails/Class/cattr_accessor for more info
I have some modules to be included in my controller classes. These modules define before_filter:
module BasicFeatures
def filter_method
...
end
def self.included(base)
base.before_filter(:filter_method)
...
end
end
module AdvancedFeatures
include BasicFeatures
...
end
And the classes:
class BasicController < ApplicationController
include BasicFeatures
end
class AdvancedController < ApplicationController
include AdvancedFeatures
end
When BasicFeatures module is included in AdvancedFeatures module, there are no before_filter methods in it.
The AdvancedController didn't get the before_filter call.
I need both my controllers to get the before_filter without any code duplication. I don't know if I am using the best approach so, I'm open to any suggestion.
This is why ActiveSupport::Concern was created.
module BasicFeatures
extend ActiveSupport::Concern
included do
before_filter :require_user
end
def this_is_an_instance_method
'foo'
end
module ClassMethods
def this_is_a_class_method
'bar'
end
end
end
class SomeClass
include BasicFeatures
end
SomeClass.new.this_is_an_instance_method #=> 'foo'
You can also nest them — that is, create concerns that include concerns — and everything will work as expected. And here are the docs.
You can try this. Instead of including the module in AdvancedFeatures, You can include the BasicFeatures module on the class including AdvancedFeatures
module BasicFeatures
def filter_method
#code....
end
#some others basic methods...
def self.included(base)
base.before_filter(:filter_method)
#some other class method calls
end
end
module AdvancedFeatures
def self.included klass
klass.class_eval do
include BasicFeatures
end
end
#some advanced methods
end
I am writing my first Rails gem, which adds a method to ActiveRecord. I can't seem to figure out a simple way to call other methods from within the method I am adding to ActiveRecord. Is there a pattern for this I should be using?
module MyModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
# This doesn't work
some_utility_method
end
end
def some_utility_method
# Do something useful
end
end
ActiveRecord::Base.send(:include, MyModule)
Once you've included MyModule, ActiveRecord::Base will have my_class_method as a class method (equivalently, an instance method of the Class object ActiveRecord::Base), and some_utility_method as an instance method.
So, inside my_class_method, self is the Class ActiveRecord::Base, not an instance of that class; it does not have some_utility_method as an available method
Edit:
If you want a utility method private to the Module, you could do it like this:
module MyModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def my_class_method
# This doesn't work
MyModule::some_utility_method
end
end
def self.some_utility_method
# Do something useful
end
end
ActiveRecord::Base.send(:include, MyModule)
I'm creating a plugin and am having a hard time defining a before_save filter that calls an instance method I've just defined. Here's a quick sample:
module ValidatesAndFormatsPhones
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
def validates_and_formats_phones(field_names = [:phone])
send :include, InstanceMethods
# the following variations on calls to :format_phone_fields fail
before_save send(:format_phone_fields, field_names)
before_save format_phone_fields(field_names)
before_save lambda { send(:format_phone_fields, field_names) }
# EACH OF THE ABOVE RETURNS 'undefined_method :format_phone_fields'
end
end
module InstanceMethods
def format_phone_fields(fields = [:phone], *args)
do stuff...
end
end
end
ActiveRecord::Base.send :include, ValidatesAndFormatsPhones
I guess the question is, how do I change the context to the instance, instead of the class?
I'd prefer to call the instance method because the class shouldn't really have a method called 'format_phone_fields' but the instance should.
Thanks!
Include your method at the right moment: when you're extending the base class:
module ValidatesAndFormatsPhones
def self.included(base)
base.send :extend, ClassMethods
base.send :include, InstanceMethods
end
module ClassMethods
def validates_and_formats_phones(field_names = [:phone])
before_save {|r| r.format_phone_fields(field_names)}
end
end
module InstanceMethods
def format_phone_fields(fields = [:phone], *args)
# do stuff...
end
end
end
ActiveRecord::Base.send :include, ValidatesAndFormatsPhones
I haven't run the code, but it should work. I've done similar things often enough.
as you are using callback macros, you can only pass a symbol for the method you want to run, passing arguments is not possible. the 'workaround' from the rails documentation is to use a 'method string' that gets evaluated in the right context:
before_save 'self.format_phone_fields(....)'
another possibility: store your field names as a class variable and access that one in your instance, then you can use before_save :format_phone_fields