Can I have a summarize of what Ruby module do? - ruby-on-rails

I do know it is a namespace thing ... can anyone give me some sample code ... i understand things fast with sample code ... thanks!

A module in ruby can be used for 3 possible things
1. Namespacing
This is pretty straight forward.
module Foo
class Bar
end
end
f = Foo::Bar.new
2. Collection of functions
Sometimes, you will have some functions that don't really fit in any class. In something like java, you would just put them as a bunch of static methods on a class. In ruby, you would put them on a module, since having them on a class implies the class is intended to be instanciated
module FooHelper
def self.bar
puts 'hi'
end
end
FooHelper.bar # => hi
3. Mixins
This is the hardest to understand of all 3. Basically, it is rubys answer to multiple inheritance in C, or interfaces in java.
Sometimes you have logic which belongs in several classes, but at the same time doesn't fit as a parent class. A mixin describes "mixing" a modules methods into a class. There is actually a lot more to this, but at an extremely high level, it would look like this
module CanFoo
def foo
puts 'bar'
end
end
class Baz
include CanFoo
end
class Bar
include CanFoo
end
baz.new.foo # => bar
bar.new.foo # => bar
mixins are a fairly advanced topic, and it takes a bit of time to understand when you would use one over a super class. IMO they are one of the coolest features of ruby though, and handles the multiple inheritance problem with a great deal more elegance then any other OO language I have looked at.

A module is a collection of constants, class and functions inside a namespace. Here is an example:
module Payments
CARD_TYPES = ["visa", "mastercard"]
class CreditCard
attr_accessor :number
attr_accessor :type
end
extend self
def process_payment
...
end
end
I now have a Payments module. I can call Payments::CARD_TYPES to get an array; Payments::CreditCard.new to create an object and Payments::process_payment to call a function.

Related

How to inherit class variables from parents included module in Ruby

I am trying to develop a reusable module for Active Record models to be used shared in models. It works well except child classes can't find the variables.
This is easier demonstrated with code
This is the module:
module Scanner
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def scan(*fields)
#scan = fields if fields.present?
#scan
end
end
end
Usage:
class User < ActiveRecord::Base
include Scanner
scan :user_name
end
Calling User.scan returns :user_name. Perfect!
However. This is an issue:
class Admin < User
end
Calling Admin.scan returns nil
How come :user_name is not being set in the parent? I am following the source for AR for methods like primary_key and table_name, they seem to be inherting values from the parent
class_attribute takes care of the required inheritance semantics for you. As you've seen, it's not quite as simple as putting an instance variable on the class, because that leaves it nil on subclasses: they're instance variables, and that's a separate instance.
The examples you mention do other, more specialised and more complicated, things... but you'll find plenty of straightforward examples in the Rails source that do use class_attribute.

Can I disallow a Rails model from being access outside of a module?

Is there a way to have a model such that only code within the same module can access it?
Something like:
module SomeModule
class SomeActiveRecordModel
# has attribute `some_attribute`
...
end
end
module SomeModule
class SomeOtherClass
def self.sum_of_attribute
SomeActiveRecordModel.sum(:some_attribute)
end
end
end
class OutsideOfModule
def self.sum_of_attribute
SomeModule::SomeActiveRecordModel.sum(:some_attribute)
end
end
SomeModule::SomeOtherClass.sum_of_attribute # works
OutsideOfModule.sum_of_attribute # raises error
Short answer is no. Here's why
Ideally, you want to implement this in your SomeModule. But when you call SomeModule::SomeOtherClass.sum_of_attribute in other classes, you are in a scope of SomeModule::SomeOtherClass.
SomeModule::SomeActiveRecordModel.sum(:some_attribute)
||
\/
module SomeModule
class SomeActiveRecordModel
def sum(*args)
# Here, self => SomeModule::SomeActiveRecordModel
# That's why you won't be able to do any meta trick to the module
# or classes in the module to identify if it's being invoked outside
end
end
end
So you wouldn't know who the original caller is.
You might be able to dig through the call stack to do that. Here's another SO thread you might find helpful if you want to go down that path.
In short, no. But this is more a question of Ruby's approach and philosophy. There are other ways of thinking about the code that allow you achieve something similar to what you're looking for, in a more Rubyesque way.
This answer covers the different ways of making things private.

Wrapping an object with methods from another class

Let's say I have a model called Article:
class Article < ActiveRecord::Base
end
And then I have a class that is intended to add behavior to an article object (a decorator):
class ArticleDecorator
def format_title
end
end
If I wanted to extend behavior of an article object, I could make ArticleDecorator a module and then call article.extend(ArticleDecorator), but I'd prefer something like this:
article = ArticleDecorator.decorate(Article.top_articles.first) # for single object
or
articles = ArticleDecorator.decorate(Article.all) # for collection of objects
How would I go about implementing this decorate method?
What exactly do you want from decorate method? Should it simply add some new methods to passed objects or it should automatically wrap methods of these objects with corresponding format methods? And why do you want ArticleDecorator to be a class and not just a module?
Updated:
Seems like solution from nathanvda is what you need, but I'd suggest a bit cleaner version:
module ArticleDecorator
def format_title
"#{title} [decorated]"
end
def self.decorate(object_or_objects_to_decorate)
object_or_objects_to_decorate.tap do |objects|
Array(objects).each { |obj| obj.extend ArticleDecorator }
end
end
end
It does the same thing, but:
Avoids checking type of the arguments relying on Kernel#Array method.
Calls Object#extend directly (it's a public method so there's no need in invoking it through send).
Object#extend includes only instance methods so we can put them right in ArticleDecorator without wrapping them with another module.
May I propose a solution which is not using Module mixins and thereby granting you more flexibility. For example, using a solution a bit more like the traditional GoF decorator, you can unwrap your Article (you can't remove a mixin if it is applied once) and it even allows you to exchange the wrapped Article for another one in runtime.
Here is my code:
class ArticleDecorator < BasicObject
def self.[](instance_or_array)
if instance_or_array.respond_to?(:to_a)
instance_or_array.map {|instance| new(instance) }
else
new(instance_or_array)
end
end
attr_accessor :wrapped_article
def initialize(wrapped_article)
#wrapped_article = wrapped_article
end
def format_title
#wrapped_article.title.upcase
end
protected
def method_missing(method, *arguments)
#wrapped_article.method(method).call(*arguments)
end
end
You can now extend a single Article by calling
extended_article = ArticleDecorator[article]
or multiple articles by calling
articles = [article_a, article_b]
extended_articles = ArticleDecorator[articles]
You can regain the original Article by calling
extended_article.wrapped_article
Or you can exchange the wrapped Article inside like this
extended_article = ArticleDecorator[article_a]
extended_article.format_title
# => "FIRST"
extended_article.wrapped_article = article_b
extended_article.format_title
# => "SECOND"
Because the ArticleDecorator extends the BasicObject class, which has almost no methods already defined, even things like #class and #object_id stay the same for the wrapped item:
article.object_id
# => 123
extended_article = ArticleDecorator[article]
extended_article.object_id
# => 123
Notice though that BasicObject exists only in Ruby 1.9 and above.
You'd extend the article class instance, call alias_method, and point it at whatever method you want (although it sounds like a module, not a class, at least right now). The new version gets the return value and processes it like normal.
In your case, sounds like you want to match up things like "format_.*" to their respective property getters.
Which part is tripping you up?
module ArticleDecorator
def format_title
"Title: #{title}"
end
end
article = Article.top_articles.first.extend(ArticleDecorator) # for single object
Should work fine.
articles = Article.all.extend(ArticleDecorator)
May also work depending on ActiveRecord support for extending a set of objects.
You may also consider using ActiveSupport::Concern.

How does Rails method call like "has_one" work?

I am PHP dev and at the moment I am learning Rails (3) and of course - Ruby. I don't want to believe in magic and so I try to understand as much as I can about things that happen "behind" Rails. What I found interesting are the method calls like has_one or belongs_to in ActiveRecord models.
I tried to reproduce that, and came with naive example:
# has_one_test_1.rb
module Foo
class Base
def self.has_one
puts 'Will it work?'
end
end
end
class Model2 < Foo::Base
has_one
end
Just running this file will output "Will it work?", as I expected.
While searching through rails source I found responsible function: def has_one(association_id, options = {}).
How could this be, because it is obviously an instance (?) and not a class method, it should not work.
After some researching I found an example that could be an answer:
# has_one_test_2.rb
module Foo
module Bar
module Baz
def has_one stuff
puts "I CAN HAS #{stuff}?"
end
end
def self.included mod
mod.extend(Baz)
end
end
class Base
include Bar
end
end
class Model < Foo::Base
has_one 'CHEEZBURGER'
end
Now running has_one_test_2.rb file will output I CAN HAS CHEEZBURGER. If I understood this well - first thing that happens is that Base class tries to include Bar module. On the time of this inclusion the self.included method is invoked, which extends Bar module with Baz module (and its instance has_one method). So in the essence has_one method is included (mixed?) into Base class. But still, I don't fully get it. Object#extend adds the method from module but still, I am not sure how to reproduce this behaviour using extend. So my questions are:
What exactly happened here. I mean, still don't know how has_one method become class method? Which part exactly caused it?
This possibility to make this method calls (which looks like configuration) is really cool. Is there an alternative or simpler way to achieve this?
You can extend and include a module.
extend adds the methods from the module as class methods
A simpler implementation of your example:
module Bar
def has_one stuff
puts "I CAN HAS #{stuff}?"
end
end
class Model
extend Bar
has_one 'CHEEZBURGER'
end
include adds the methods from the module as instance methods
class Model
include Bar
end
Model.new.has_one 'CHEEZBURGER'
Rails uses this to dynamically add methods to your class.
For example you could use define_method:
module Bar
def has_one stuff
define_method(stuff) do
puts "I CAN HAS #{stuff}?"
end
end
end
class Model
extend Bar
has_one 'CHEEZBURGER'
end
Model.new.CHEEZBURGER # => I CAN HAS CHEEZBURGER?
I commend you for refusing to believe in the magic. I highly recommend you get the Metaprogramming Ruby book. I just recently got it and it was triggering epiphanies left and right in mah brainz. It goes over many of these things that people commonly refer to as 'magic'. Once it covers them all, it goes over Active Record as an example to show you that you now understand the topics. Best of all, the book reads very easily: it's very digestible and short.
Yehuda went through some alternatives on way to Rails3: http://yehudakatz.com/2009/11/12/better-ruby-idioms/
Moreover, you can use a (usually heavily abused, but sometimes quite useful) method_missing concept:
class Foo
def method_missing(method, *arg)
# Here you are if was some method that wasn't defined before called to an object
puts "method = #{method.inspect}"
puts "args = #{arg.inspect}"
return nil
end
end
Foo.new.abracadabra(1, 2, 'a')
yields
method = :abracadabra
args = [1, 2, "a"]
Generally, this mechanism is quite often used as
def method_missing(method, *arg)
case method
when :has_one
# implement has_one method
when :has_many
# ...
else
raise NoMethodError.new
end
end

Is it possible to reverse the included module in a class?

You include the modules in classes to extend the class functionality in terms of both adding class methods and instance methods to that particular class.
module M
def self.class_method_from_module
'from class_method_from_module'
end
def instance_method_from_module
'from instance_method_from_module'
end
end
class C
include M
def self.class_method
'from class_method'
end
def instance_method
'from instance_method'
end
end
puts C.class_method => 'from class_method'
puts C.class_method_from_module => 'from class_method_from_module'
puts C.new.instance_method => 'from instance_method'
puts C.new.instance_method_from_module => 'instance_method_from_module'
Now even after removing the module M from the memory with the following:
Object.send(:remove_const, :M) #remove the module M
puts C.class_method_from_module => 'from class_method_from_module'
puts C.new.instance_method_from_module => 'instance_method_from_module'
instead of method missing. Why is that? What is the best way to remove the functionality added by a module to a class?
The details vary by implementation, but I know that at least in JRuby and MRI 1.8 there is a construct called an Include Class that is inserted in to the inheritance chain of a class when a Module is extended or included. The Module therefore won't be garbage collected, since the Include Class still refers to it, and the methods will still be on your class. There are some great articles by Patrick Farley on this and related topics in his blog.
So, to "remove" a module, you could individually undefine each method that came from the module, but that's a pretty unwieldy mechanism for that purpose. If using a gem is acceptable to you, it would probably be better would be to use Mixology, which was designed specifically for the purpose of adding and removing modules dynamically.
When you include a mixin in a class, you are effectively adding the methods defined in the module to the class, or replacing methods in the class with those in the module that have the same name. The class does not have any tie to the module, which is why 'undefining' the M module won't affect class C. All that does is prevent you from mixing in M beyond that point.
You could use undef_method to remove methods from class C, but that will have side effects, potentially -- if a method was overriden by including a module, you will not get the original back, for instance. Undefining a class method is kind of ugly.
C.send(:undef_method, :instance_method_from_module)
class << C
self
end.send(:undef_method, :class_method_from_module)

Resources