Does ActiveSupport::Concern support a way to run a method before or after the object's constructed.
e.g. to achieve something like
Module Benchmarker
extend ActiveSupport::Concern
before_initialize
#constructed_at = DateTime.now
end
end
(Note: Not wanting this for ActiveRecord or ActiveController. Just a generic Ruby class within a Rails project.)
NOTE: This would work for models or only those classes which inherit from ActiveRecord.
Rails does not support a before_initialize callback. You could use the after_initialize callback instead. However, when using it in a concern, you need to mention it in an included do..end block. For example, the following code should work for your use case:
Module Benchmarker
extend ActiveSupport::Concern
included do
after_initialize do |subject|
p "I'm in after_initialize"
end
end
end
You could refer to this answer for more details on how the included hook works.
Related
What is the difference between adding instance methods whith active concern and through the normal ruby def keyword on the module?
module MonsterConcern
extend ActiveSupport::Concern
included do
def engage_rage
end
def chew_bones
end
end
end
and
module MonsterConcern
def engage_rage
end
def chew_bones
end
end
As far as I know there is no difference if the only thing you are interested in is instance methods.
The ActiveSupport::Concern advantage is the ability to define class methods, and to handle some nasty module interdependencies better (related to calling class methods in the included block).
You can read more here: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
What's the difference between
module A
def self.included(klass)
puts klass
end
end
and
module A
include ActiveSupport::Concern
included do
puts self
end
end
Which one is a better and which one to use when?
Both snippets produce the same result. However, there is a small but important difference.
The first code is pure Ruby. It means it will work without any dependency.
The second piece of code depends on ActiveSupport that is an external dependency. If you want to use it you need to include the gem in your project. In a Rails application, there is almost no overhead because the application already depends on ActiveSupport. But in a non-Rails application, it may not be convenient.
Moreover, ActiveSupport::Concern does a lot more than simply adding some syntactic sugar for the Ruby included hook. In fact, the primary scope of such module was to manage multiple dependencies between modules.
In the Rails codebase it's very common to define small piece of features into separate modules. A good example is ActiveSupport itself. However, you may have that the feature A may require some feature defined in the module B, so if you include B in your code, then the module should make sure to require and mix also A, otherwise your code will crash.
It also implements a very common pattern based on the included hook: class-level extensions. The following Ruby code
module A
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def bar
"bar"
end
end
end
class Foo
include A
end
Foo.bar
# => "bar"
becomes
module A
extend ActiveSupport::Concern
module ClassMethods
def bar
"bar"
end
end
end
class Foo
include A
end
Foo.bar
# => "bar"
My personal advice is to avoid using the ActiveSupport::Concern if
You don't need to use its advanced features
The code you are writing, can be easily written with a few code with no dependency on Concern
The app does not already include ActiveSupport as dependency
In your second piece of code, included is called once during when A is defined. It does not do anything unless you had defined included to do something. The block passed to included would have no effect unless you had overwritten included to take a block and do something with it. In short, your second piece of code does not make sense.
I'm a total newbie at ruby (java background) so I'm sorry if this is a really dumb question.
I'm working through some tutorials on modules and they seem somewhat similar to static classes. The bit I'm having trouble wrapping my head around is why you would do something like the following:
module ExampleModule
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def myMethod
end
end
end
Why wouldn't you just put the methods in ClassMethods into ExampleModule and save adding the method hook. I'm sure I'm missing something really basic but I've been at this for a while now so I feel the need to ask.
It's a ruby idiom. It's useful when you want a module that:
adds some instance methods to a class
adds class methods in the same time /like Java static methods/
in the same time
Example:
module ExampleModule
def self.included(base)
puts 'included'
base.extend(ClassMethods)
end
def foo
puts 'foo!'
end
module ClassMethods
def bar
puts 'bar!'
end
end
end
class ExampleClass
include ExampleModule
end
ExampleClass.bar
ExampleClass.new.foo
If you only want to add class methods, you don't need this idiom, you can just add a method to your module and 'extend' it instead of including it.
When on Rails, this idiom is obsolete and you should use ActiveSupport::Concern instead.
The patter you use here is common when both class methods and instance methods are included via a module in ruby. It gives you the advantage of just having to write
include ExampleModule
for including instance methods and extending class methods instead of
# include instance methods
include ExampleModule
# extend class methods
extend ExampleModule::ClassMethods
So, if used just to extend the class with some methods, my personal preference is to use extend directly.
module ExtensionAtClassLevel
def bla
puts 'foo'
end
end
class A
extend ExtensionAtClassLevel
end
A.bla #=> 'foo'
If both instance and class methods are added, I use the include hook you described.
Some rubyists tend to prefer using extend via the include hook to pure extend, which has no reason if you are just adding class methods like in your example.
Does anybody know why the included method doesn't work inside a class method?
class MyClass
include ActionView::Helpers::NumberHelper
def test
puts "Uploading #{number_to_human_size 123}"
end
def self.test
puts "Uploading #{number_to_human_size 123}"
end
end
ree-1.8.7-2011.03 :004 > MyClass.new.test
Uploading 123 Bytes
=> nil
ree-1.8.7-2011.03 :005 > MyClass.test
NoMethodError: undefined method `number_to_human_size' for MyClass:Class
from /path/to/my/code.rb:9:in `test'
from (irb):5
ree-1.8.7-2011.03 :006 >
For anyone wanting to use some custom helpers in class level lib or model, sometimes it is not worth it to include all helpers. Instead, call it directly:
class MyClass
def test
::ApplicationController.helpers.number_to_human_size(42)
end
end
(Taken from http://makandracards.com/makandra/1307-how-to-use-helper-methods-inside-a-model)
It's hard to tell without seeing your helper code, but include will insert all of the methods in that module into instances of the class you include into. extend is used to bring methods into a class. Therefore, if you just have methods defined in NumberHelper, these are being put onto all instances, but not the class, of MyClass.
The way that lots of Rails extensions work is using techniques that have been consolidated into ActiveSupport::Concern. Here is a good overview.
Essentially, extending ActiveSupport::Concern in your modules will allow you to specify, in sub-modules called ClassMethods and InstanceMethods, what functions you want to be added to classes and instances into which you include your module. For example:
module Foo
extend ActiveSupport::Concern
module ClassMethods
def bar
puts "I'm a Bar!"
end
end
module InstanceMethods
def baz
puts "I'm a Baz!"
end
end
end
class Quox
include Foo
end
Quox.bar
=> "I'm a Bar"
Quox.new.baz
=> "I'm a Baz"
I've used this before to do things like define the bar function in ClassMethods, then also make it available to instances by defining a bar method of the same name that just calls this.class.bar, making it callable from both. There are lots of other helpful things that ActiveSupport::Concern does, like allowing you to define blocks that are called back when the module is included.
Now, this is happening here specifically because you're includeing your helper, which might indicate that this functionality is more general-purpose than a helper - helpers are only automatically included in views, since they are only intended to help with views. If you want to use your helper in a view, you could use the helper_method macro in your class to make that method visible to your views, or, even better, make a module as above and not think about it as a helper, but use include to mix it in to the classes you want to use it in. I think I would go that route - there's nothing that says you can't make a HumanReadableNumber module and include it in NumberHelper to make it easily available across your views.
I faced the same issue. Here's how I solved it,
helper = Object.new.extend(ActionView::Helpers::NumberHelper)
helper.number_to_human_size(1000000)
Thanks to RailsForum.
I was facing the same problem. in my case, replacing the include with extend made it work.
class MyClass
extend ActionView::Helpers::NumberHelper
...
end
Ok so I am using some module/lib/plugin (not sure of the exact name), let's say its a authentication/authorization plugin like: http://github.com/technoweenie/restful-authentication
class HomeController < ApplicationController
some_module_name_here
end
Now just from adding the code above 'some_module_name_here', I can access to methods from that module.
What is that line of code doing that gives me access to methods/objects from the module?
Is that declaring a variable like in say java/c#:
public SomeModule _someModule;
I know that plugins/modules basically extend the class under the covers, but how does it do this with a single line of code?
Is it called in the constructor somehow?
When you create a ruby plugin, and load it into the rails app via environment.rb, bundler, or a require call, the methods are loaded as "modules" that can be called. The ones that act like you're talking about will have an extra method called acts_as_list or something similar. All that method does is include the methods of the module into the class where that line was called.
Here's an example, which you could include in your app's lib folder and play with:
module Bellmyer
module Pointless
def self.included(base)
base.extend PointlessMethods
end
module PointlessMethods
def acts_as_pointless
unless included_modules.include? InstanceMethods
extend ClassMethods
include InstanceMethods
end
end
end
module ClassMethods
def pointless_class?
true
end
end
module InstanceMethods
def pointless_instance?
true
end
end
end
end
The module is available to any ruby class in your app, but the methods don't actually get loaded until you call acts_as_pointless, which then includes and extends your class with the methods listed. Only the acts_as_pointless method is immediately available to the model. This is the standard pattern for an ActiveRecord plugin.
That's not how it works.
When the plugin or gem is loaded it adds a class method to, in this case, ApplicationController named some_module_name. When you call that methods, a bunch of other class and instance methods are included.
Check out your favourite gem or plugin to see how they do it exactly.