module MyModule
def self.my_method
#instance_variable ||= "some string"
end
end
Consider another module OtherModule that includes MyModule. When code in OtherModule calls my_method multiple times will #instance_variable be instantiated multiple times? Or just once?
updated answer:
if the delegate module call the method, you will get an error.
if the original module call the method, the class instance variable only instanced 1 time.
if you want to define "class methods" in an module, you can't define self.xx then simply extend/include it. you should either "extend" it, or "include it to the eigen class", e.g.
module SomeModule
def self.aim_to_be_class_method ; end;
def aim_to_be_instance_method ; end;
end
# class methods: []
# instance methods: ["aim_to_be_instance_method"]
class SomeClassIncludingTheModule
include SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassExtendingTheModule
extend SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassMadeEigenClassIncludingModule
class << self
include SomeModule
end
end
your code is an example of "class instance variables". according to the book << metaprogramming ruby>>, page 127, you can consider "class instance variables" as Java's static fields. so, I think most of the case, it should run once.
for more details, please write an unit test and see what happens? I will update my answer after a short while with my own unit test code written.
UPDATED: my unit test and result:
# all of the code is placed in a file: class_instance_variable_test.rb
# define a class so that we see its initialize process
class Apple
def initialize
puts "Apple initialized~"
end
end
# define an original_module that calls Apple.new
module OriginalModule
def self.original_method
#var ||= Apple.new
end
end
# define another module to call the original_module
module DelegateModule
include OriginalModule
end
# now the test begins
require 'test/unit'
class ClassInstanceTest < Test::Unit::TestCase
# output of this test case:
# NoMethodError: undefined method `original_method' for DelegateModule:Module
def test_if_delegate_module_could_call_original_method_by_including_original_module
DelegateModule.original_method
end
# output of this test case:
# ----- calling from original_method, 3 times called, how many times initialized?
# Apple initialized~
# ------ ends
def test_if_we_could_call_original_method_from_original_module
puts "----- calling from original_method, 3 times called, how many times initialized? "
OriginalModule.original_method
OriginalModule.original_method
OriginalModule.original_method
puts "------ ends "
end
end
Related
I've been reading this article on the difference between include & extend in ruby.
If I have this module, I understand how the first and second methods of the module will be used in the class. What I don't understand is how the class << self will be used by include or extend.
module Direction
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
class << self
def stop
puts "stopping!"
end
end
end
# This will work because `include` brings them in as instance methods
class Car
include Direction
end
Car.new.straight
Car.new.turn
# ---------------------
# Now this will also work because `extend` brings them in as class methods
class Car
extend Direction
end
Car.straight
Car.turn
# ---------------------
Now, the issue is, doing Car.stop or Car.new.stop will always result in an error:
/Users/<name>/Projects/ruby-testing/main.rb:34:in `<main>': undefined method `stop' for Car:Class (NoMethodError)
Why are class methods not carried over via include and extend?
I started thinking about this because of my research into the [forwardable source code at line 119].(https://github.com/ruby/ruby/blob/master/lib/forwardable.rb#L119)
Thank you for any help you may have!
Update from Answer Below
The following was an example given:
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
This I understand now, and I understand how I can implement class methods from a module into a class using def self.included(base). My question is, if we used extend inside of Car instead of include, would we still be able to get at those class methods using def self.included(base)?
When you define a method with class << self you are defining a class method. It's the same as defining the methed like this:
class Foo
def self.foo
puts 'foo'
end
# the above definition is the same as doing:
class << self
def foo
puts 'foo'
end
end
end
The above shows 2 ways of defining class methods which are called directly on the class and not on instances of the class. You might use the 2nd syntax if you want to define only class methods or several of them inside of the class << self block. But either style has the same result.
Since you've defined a class method on the Direction module, include or extend will not inherit the class method of that module. This is the expected behavior.
If you want to use inheritance with class methods from a module, you should do it like this which is explained further down in the article you've linked
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
Now calling class methods on Car will inherit as defined in the Direction class.
Car.stop
stopping!
=>nil # calling a method will return nil unless the method returns a value.
However always be careful using inheritance of any kind as Ruby is a dynamic language. So if you do the above code and then later redefine this method:
module Direction
module ClassMethods
def stop
puts 'go!'
end
end
end
Guess what will happen if you do this:
Car.stop
Since the method was defined inside Direction module, when the method gets called on Car it will be calling the method from the Direction module.
Car.stop
go!
=>nil
Updated based on comments:
If you prefer to use extend vs include you would need to do this instead:
module Direction
def self.extended(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
end
class Car
extend Direction
end
In this example, all the methods which were inherited from the module are "copied" to the class extending them. This avoids the problem of possible result of redefining the module method which I warned about when using include previously in my answer.
But you may want to look at answers to this question for ideas about when and why to use either case.
I'm confused about using "include" vs "extend, after searching for hours all I got is that module methods used with instance of the class including the module, and module methods used with the class itself when the class extending the module of those methods.
but this didn't help me to figure out, why this code give error when commenting the extend module line in "#extend Inventoryable"
while work when uncomment it, here's the code
module Inventoryable
def create(attributes)
object = new(attributes)
instances.push(object)
return object
end
def instances
#instances ||= []
end
def stock_count
#stock_count ||= 0
end
def stock_count=(number)
#stock_count = number
end
def in_stock?
stock_count > 0
end
end
class Shirt
#extend Inventoryable
include Inventoryable
attr_accessor :attributes
def initialize(attributes)
#attributes = attributes
end
end
shirt1 = Shirt.create(name: "MTF", size: "L")
shirt2 = Shirt.create(name: "MTF", size: "M")
puts Shirt.instances.inspect
the output is
store2.rb:52:in `<main>': undefined method `create' for Shirt:Class (NoMethodError)
while when uncomment the "extend Inventoryable" to make the code work:
module Inventoryable
def create(attributes)
object = new(attributes)
instances.push(object)
return object
end
def instances
#instances ||= []
end
def stock_count
#stock_count ||= 0
end
def stock_count=(number)
#stock_count = number
end
def in_stock?
stock_count > 0
end
end
class Shirt
extend Inventoryable
include Inventoryable
attr_accessor :attributes
def initialize(attributes)
#attributes = attributes
end
end
shirt1 = Shirt.create(name: "MTF", size: "L")
shirt2 = Shirt.create(name: "MTF", size: "M")
puts Shirt.instances.inspect
makes the code work and output the following
[#<Shirt:0x0055792cb93890 #attributes={:name=>"MTF", :size=>"L"}>, #<Shirt:0x0055792cb937a0 #attributes={:name=>"MTF", :size=>"M"}>]
it's kinda confusing, but all I need to know, is why I need to extend the module in order to avoid the error ?, and how to edit this code to make it work without the extend method ? , what's left in the code that still depends on the extend ?
When you extend a module, the methods in that module become "class methods"**. So, when you extend Inventoryable, create becomes available as a method on the Shirt class.
When you include a module, the methods in that module become "instance methods"**. So, when you include Inventoryable, create is not available on the Shirt class (but is available on an instance of Shirt).
To make create available on the Shirt class when using include, you can use the included hook. That might look something like:
module Inventoryable
module ClassMethods
def create
puts "create!"
end
end
module InstanceMethods
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.include InstanceMethods
end
end
Then if you do:
class Shirt
include Invetoryable
end
You can do:
> Shirt.create
create!
=> nil
** The ruby purists in the crowd will correctly point out that, in ruby, everything is an instance method and that there are no class methods. That is formally 100% correct, but we'll use the colloquial meaning of class and instance methods here.
When you extend a module in a class, you get the module's methods exposed as class methods but if you include the module then you get the module's method as instance methods, in your example for you to be able to call create method of Inventoryable class you need to invoke it using an instance of Shirt class (if you include the module)
shirt1 = Shirt.new(attributes).create(attributes)
Without more info I can't tell what you are trying to do but you need to redesign the initialize and create methods to decide where or what to do in those methods.
I'll try to explain it using a simple example
module A
def test
puts "ok"
end
end
class B
include A
end
class C
extend A
end
puts C.test # here you invoke the method against the class itself
puts B.new.test #here you create an instance to do it
Hope it helps.
At the end of the day, it's really simple:
C.include(M) makes the current superclass of C the superclass of M and M the superclass of C. In other words, it inserts M into C's ancestry chain.
obj.extend(M) is (roughly) the same as obj.singleton_class.include(M).
I have created a new library file sampler.rb inside the lib folder. Consider this as the content of the file
module Sampler
def sample_tester
"test"
end
end
I have included it in the application_controller and added a require statement in the config\initializers. When I try to access the method sample_tester from my controllers, I get the following error
undefined local variable or method `sample_tester` for #<BlogsController:0xb8fbac8>
Am I missing something?
Since it doesn't look like you are creating an instance of this, my first guess is that you need to define it as a class method so that it can be called like this: Sampler.sample_tester.
In your file you could do it one of two ways:
# first way
module Sampler
def self.sample_tester
"test"
end
end
# second way
module Sampler
class << self
def sample_tester
"test"
end
end
The second way is nicer if you want to define a number of class methods.
if you want to have your module method defined as a class method you need to use extend instead of include:
module Mod
def bla
puts "bla"
end
end
class String
include Mod
end
String.bla rescue puts $! # => undefined method `bla' for String:Class
class String
extend Mod
end
puts String.bla # => bla
Can we use include statement to include a module anywhere within the class or does it has to be at the beginning of the class?
If I include the module at the beginning of my class declaration, method overriding works as expected. Why is it not working if i include at the end as described below?
# mym.rb
module Mym
def hello
puts "am in the module"
end
end
# myc.rb
class Myc
require 'mym'
def hello
puts "am in class"
end
include Mym
end
Myc.new.hello
=> am in class
When you include a module, its methods do NOT replace methods defined in this class, but rather they are injected into inheritance chain. So, when you call super, method from included module will get called.
They will behave almost the same way with other modules. When a module gets included, it is placed right above the class in inheritance chain, with existing modules placed above it. See example:
module Mym
def hello
puts "am in the module"
end
end
module Mym2
def hello
puts "am in the module2"
super
end
end
class Myc
include Mym
include Mym2
def hello
puts "im in a class"
super
end
end
puts Myc.new.hello
# im in a class
# am in the module2
# am in the module
For more info see this post.
Also read this: http://rhg.rubyforge.org/chapter04.html
Quick question regarding the use of "SELF" inside a module or library. Basically what is the scope/context of "SELF" as it pertains to a module or library and how is it to be properly used? For an example of what I'm talking about, check out the "AuthenticatedSystem" module installed with "restful_authentication".
NOTE: I'm aware that 'self' equates to 'this' in other languages and how 'self' operates on a class/object, however in the context of a module/library there is nothing to 'self'. So then what is the context of self inside something like a module where there is no class?
In a module:
When you see self in an instance method, it refers to the instance of the class in which the module is included.
When you see self outside of an instance method, it refers to the module.
module Foo
def a
puts "a: I am a #{self.class.name}"
end
def Foo.b
puts "b: I am a #{self.class.name}"
end
def self.c
puts "c: I am a #{self.class.name}"
end
end
class Bar
include Foo
def try_it
a
Foo.b # Bar.b undefined
Foo.c # Bar.c undefined
end
end
Bar.new.try_it
#>> a: I am a Bar
#>> b: I am a Module
#>> c: I am a Module
For a short summary...
http://paulbarry.com/articles/2008/04/17/the-rules-of-ruby-self
self is also used to add class methods (or static methods for C#/Java people). The following snippet is adding a method called do_something to the current class object (static)...
class MyClass
def self.do_something # class method
# something
end
def do_something_else # instance method
end
end