Assuming a module is included, not extended, what's difference between module instance variable and class variable?
I do not see any difference between two.
module M
#foo = 1
def self.foo
#foo
end
end
p M.foo
module M
##foo = 1
def self.foo
##foo
end
end
p M.foo
I have been using # as ## within a module, and I recently saw other codes are using ## within a module. Then I thought I might have been using it incorrectly.
Since we cannot instantiate a module, there must be no difference between # and ## for a module. Am I wrong?
----------------------- added the following --------------------
To answer some of questions on comments and posts, I also tested the following.
module M
#foo = 1
def self.bar
:bar
end
def baz
:baz
end
end
class C
include M
end
p [:M_instance_variabies, M.instance_variables] # [#foo]
p [:M_bar, M.bar] # :bar
c = C.new
p c.instance_variables
p [:c_instance_variabies, c.instance_variables] # []
p [:c_baz, c.baz] :baz
p [:c_bar, c.bar] # undefined method
When you include a module within a class, module class variables and class methods are not defined in a class.
Class variables can be shared between modules and classes where these modules are included.
module A
##a = 5
end
class B
include A
puts ##a # => 5
end
Meanwhile, instance variables belong to self. When you include module A into class B, self object of A is not the same as self object of B, therefore you will not be able to share instance variables between them.
module A
#a = 5
end
class B
include A
puts #a # => nil
end
## refers to class variable and # refers to instance variable. While using this with module makes big difference. When you include a module you add class methods to your class while extending add instance methods to your class
Following is a nice article on this
http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
I found this SO post that talks about how to create class variables in modules, "they are supported natively." One commenter even says the name 'class variable' is misleading since classes are just modules with a few extra powers.
All I'm sure of is nearly every article I've read about class variables thinks they're evil and to avoid them at all costs because of the weird inheritance issues you can encounter.
Related
Say we have a class that we cannot change,
class C
def foo
super
puts "Low!"
end
end
We'll need to dynamically define the method foo in something that we'll be able to inject into C's ancestry chain. The behavior of super must be specific to a given object, not class-wide. We'll be able to enclose that logic into an anonymous module (let's name it for now):
module M
def foo
puts "High!"
end
end
Extending an instance of C with the module:
c = C.new
c.extend(M)
c.foo
# High!
will not work since we've put the method from the module before the method we've defined in the class. Looking at our object's ancestors
c.singleton_class.ancestors
# => [#<Class:#<C:0x00005652be630b20>>, M, C, ...]
I came up with an ugly workaround, which is redefining the methods from our class in our singleton_class, i.e.
c.define_singleton_method(:foo, c.class.instance_method(:foo))
c.foo
# High!
# Low!
While this works (does it work? I've tested it for a bit and it seems to, but I'm no longer certain), I wonder whether I'm missing something obvious and there's an easier way to dynamically define a "super" method for an instance of a class.
To be clear, we want to be able to extend another instance of C with another module, i.e.
C.new.extend(Module.new do
def foo
puts "Medium!"
end
end).foo
# Medium!
# Low!
and have its output not tainted by other instances.
Now that I understand you're trying to work around an issue in some third-party code, I can suggest a more reasonable solution. In the code below, I'm thinking that B and C are defined by the gem and you don't want to change their source code, but you want to inject some code into the place where C#foo calls B#foo.
class B
def foo
puts "Highest!"
end
end
class C < B
def foo
super
puts "Low!"
end
end
module CE
def foo
super
foo_injected
end
end
C.include(CE)
module M
def foo_injected
puts "High!"
end
end
c = C.new
c.extend(M)
p c.singleton_class.ancestors
c.foo
The output is:
[#<Class:#<C:0x000055ce443366a8>>, M, C, CE, B, Object, Kernel, BasicObject]
Highest!
High!
Low!
I am trying to define a DSL where some constants are defined within a block and must be copied into a fresh made module. I got this so far:
class Foo
def self.macros(&block)
mod = Module.new do
module_eval &block
end
self.const_set(:Macros, mod)
end
macros do
Point = Struct.new :x, :y
VALUE = 5
def self.bar
"bar"
end
def foo
"foo"
end
end
end
With the code above I managed to get Foo::Macros.bar however the constants are missing.
How can I get the constants defined within the block?
I want to access them through the new module embedded inside the class, like Foo::Macros::Point
Ruby constant lookup rules doesn't change with class_eval or module_eval, so you cannot make a constant defined in a block in Foo be part of Foo::Macros, sadly.
Simply:
Foo::Value
Foo::Point
Foo::Macros is just an alias for the anonymous module you defined, it doesn't change the scope of object defined.
If you define the module first, you can access it using const_get:
module Test
end
Test.module_eval do
ANSWER = 42
end
Test.const_get('ANSWER')
=> 42
I am still trying to clearly understand Module/Class/Instance variables ...
My code currently looks something like this ...
module Foo
##var1 ={}
##var2 =[]
##var3 = nil
def m1(value)
##var2 << value
end
def m2(value)
##var1[##var3]=value
end
end
class Bar
include Foo
p ##var1
end
class Bar2
include Foo
p #var1
end
I am trying to create a module that contains a class-wide configuration for how each class will behave. The configuration is stored in ##var1 and ##var2. Using this code the variables are shared across ALL classes that include the module. This is not the desire result, I want each class to have it's own behavior configuration.
I have also tried creating a single class that includes the module and also creates the variables but then the variables are not accessible by the module.
module Foo
def m1(value)
##var2 << value
end
def m2(value)
##var1[##var3]=value
end
end
class T
##var1 ={}
##var2 =[]
##var3 = nil
include foo
end
class Bar < T
p ##var1
end
class Bar2 < T
p #var1
end
I have also read that having modules with class variables is not good coding style but I cannot think of a way to achieve my functionality with this ...
Thanks in advance for any help
Firstly - class variables are evil and should be avoided (because they are also inherited by all subclasses and usually causes more harm than good.
You want to create a class instance variable (not class variable) on a class or module which is including given module. It is easy to do with included method:
module Foo
#default_settings = {}
module ClassMethods
def foo_settings
#foo_settings
end
end
def self.included(target)
target.instance_variable_set('#foo_settings', #default_settings.dup)
target.extend ClassMethods
end
end
How is it possible that I can have instance variables in a module even though I cannot create an instance of the module? What would be the purpose of #stack in module Stacklike below?
module Stacklike
def stack
#stack ||= []
end
end
Think of the instance variable as something which will exist in any class that includes your module, and things make a bit more sense:
module Stacklike
def stack
#stack ||= []
end
def add_to_stack(obj)
stack.push(obj)
end
def take_from_stack
stack.pop
end
end
class ClownStack
include Stacklike
def size
#stack.length
end
end
cs = ClownStack.new
cs.add_to_stack(1)
puts cs.size
will output "1"
See the below:
p RUBY_VERSION
module Stacklike
def stack
#stack ||= []
end
def add_to_stack(obj)
stack.push(obj)
end
def take_from_stack
stack.pop
end
end
class A
include Stacklike
end
a = A.new
p a.instance_variables #<~~ E
p a.instance_variable_defined?(:#stack) #<~~ A
a.add_to_stack(10) #<~~ B
p a.instance_variable_defined?(:#stack) #<~~ C
p a.instance_variables #<~~ D
Output:
"1.9.3"
[]
false
true
[:#stack]
Explanation: Yes, Module instance variables are present in the class when you would include them inside the class. But you can see that p a.instance_variable_defined?(:#stack) is showing false as #stack is still not defined till A. At point B I defined the instance variable #stack. Thus statement in point C, outputs as true. Means module instance variables are not being created by the module itself,but that can be done by the class instances if the class included that module. Statement in E outputs [] as still that point the instance variable was not defined, but if you see the output for the line D, it is proved the #stack is inside the object a of class A.
Why such design?
This is the design or sometimes come from the requirements. Say you have been asked to write a stack operation code which will be used by two ticket booking companies,Say A and B. Now A are stack policy for their customers to serve but also they have any more formalities with that. B company also using stack policy with their own formalities which is different from A. Thus in case of designing Stack operation inside class A and class B, better idea to write it in a common place,as Both A and B have this functionality common within them. In future if another company C comes to you you can also use that module into their class, without rewriting the same functionality for each A,B and C. There can be more thoughts but hope this will help you to answer your self for your last part of the questions.
That's all about the concept. Hope it helps.
Cheers!!
When you include a module in a class, all of its instance methods are effectively "pasted in" to the host class. So if you have:
class Lifo
include Stacklike
end
l = Lifo.new
l.add_to_stack(:widget)
Then l now has an instance variable #stack, brought in from Stacklike.
When you include the module Stacklike in some other class that instance variable will be available as if it was defined in that class. That give you the option to set and handle instance variables of the base class from the module itself.
Note that a module can store its own instance variables regardless of whether it gets included in the class or not.
Consider:
module Stacklike
def self.stack(args = nil)
#stack ||= (args)
end
end
Stacklike.instance_variables
> []
Stacklike.stack("original stack")
> "original stack"
Stacklike.instance_variables
> [:#stack]
Stacklike.stack("new stack")
> "original stack"
When you defined the module StackLike, this created an object named StackLike having type Class. Since it's an object, it can have its own instance variables, and those can be accessed by its class methods.
Here is what I tried:
module A
def self.method1; "method1"; end
def method2; "method2"; end
end
module B; include A; end
B.method1 # => error
B.method2 # => error
B::method1 # => error
B::method2 # => error
I want to avoid copying and pasting equivalent code between two modules. The reason I'm using modules instead of classes here is because I don't need more than one instance of each module, as they simply hold constants (other modules, at this point).
What is the best way to solve this problem?
Plain include only gives you instance methods (method2 in your particular piece of code). If you want to share module-level methods - extract them to separate module and extend other modules with it:
module A
extend self # to be able to use A.method1
def method1
"method1"
end
end
module B
extend A
end
B.method1 # => "method1"
It is also possible get module-level methods by include, but with a little twist, using hook method:
module A
def self.included(other)
other.extend ModuleMethods # this is where the magic happens
end
def instance_method
'instance method'
end
module ModuleMethods
def module_method
'module method'
end
end
extend ModuleMethods # to be able to use A.module_method
end
module B
include A
end
B.module_method #=> "module method"
B.instance_methods #=> [:instance_method]
First of all, please note that A.method2 won't work either. You can create objects including A (or B) that will have method2:
class C
include B # (or A)
end
c = C.new
c.method2
So, for method2 it just works as you intended.
Regarding method1, it is a singleton method of the object A and there is no way to inherit it.