Why include module affeacting all my instance object? - ruby

This has been puzzling me:
class Entry
end
module A
def test
print "A"
end
end
class Entry
include A
end
i = Entry.new
i.test # --> print 'A', expected
p i.class.ancestors # --> [Entry, A, Object, Kernel, BasicObject]
module B
def test
print "B"
end
end
class Entry
include B
end
ii = Entry.new
ii.test # --> print 'B', expected
p ii.class.ancestors # --> [Entry, B, A, Object, Kernel, BasicObject]
i.test # --> print 'B', now this is unexpected!
p i.class.ancestors # --> [Entry, B, A, Object, kernel, BasicObject] --> WHY?
It seems include is changing all instance internal state too. Is there any reason it behave that way?
Thanks!

It is actually a pretty expected behaviour. In ruby, pretty much everything is a reference. object.class is a reference to an instance of Class class, and instances of Class are fully mutable.
Your i object holds a reference to Entry class, so does ii. When you do Entry.include B you modify the existing Entry class which will affect all the object that holds any reference to it. It is conceptually similar to:
class A
def initialize(str)
#str = str # holding reference to string object
end
attr_reader :str
end
string = 'hello'
a = A.new(string)
a.str #=> "hello"
string << " there" # modifying the string object
a.str #=> "hello there"
a.str.object_id == string.object_id #=> true
Now imagine how difficult it would be to work with a code where two instances of the same class would have different behaviour.
If you want some instances to have slightly different behaviour, you need to either use subclasses
class BEntry < Entry
include B
end
Entry.new.test #=> 'A'
BEntry.new.test #=> 'B'
or you can make use of singleton classes of specific objects (warning - this can make the code a bit less maintainable)
a = Entry.new
b = Entry.new
b.extend(B)
a.test #=> 'A'
b.test #=> 'B'

Related

Create dynamically methods in a module for each constant value defined within the module

I am trying to find out a way to generate dynamically methods in module (SomeConstants) based on constants defined within this module.
what I think I would like to achieve is the situation in which I would be able to include (mixin) SomeConstants to other classes/modules so they are aware of methods returning constant values as well as being able to extend object so the object itself has an access to constants through methods
module SomeConstants
A = 'a'
B = 'b'
constants.each do |const|
define_method(const.downcase.to_sym, lambda {SomeConstants.const_get(const)})
end
C = 'c'
end
class SomeClass
include SomeConstants
end
s = SomeClass.new
p s.a
p s.b
p s.class.ancestors
p s.singleton_class.ancestors
p s.singleton_methods
o = Object.new
o.extend(SomeConstants)
p o.a
p o.b
p o.class.ancestors
p o.singleton_class.ancestors
p o.singleton_methods
Output:
"b"
[SomeClass, SomeConstants, Object, Kernel, BasicObject]
[#<Class:#<SomeClass:0x00559069504fe8>>, SomeClass, SomeConstants, Object, Kernel, BasicObject]
[]
"a"
"b"
[Object, Kernel, BasicObject]
[#<Class:#<Object:0x00559069504660>>, SomeConstants, Object, Kernel, BasicObject]
[:a, :b]
The above one seem to work fine, apart from the fact that all constants must be defined before creating methods due to the way how source code is processed.
My second approach was to use included/extended hooks, though I am not sure if this doesn't smell badly.. see below
module SomeConstants
A = 'a'
B = 'b'
def self.included(base)
constants.each do |const|
base.send(:define_method, const.downcase.to_sym, lambda { SomeConstants.const_get(const) })
end
end
def self.extended(base)
constants.each do |const|
base.send(:define_singleton_method, const.downcase.to_sym, lambda { SomeConstants.const_get(const) })
end
end
C = 'c'
end
class SomeClass
include SomeConstants
end
s = SomeClass.new
p s.a
p s.b
p s.c
p s.class.ancestors
p s.singleton_class.ancestors
p s.singleton_methods
o = Object.new
o.extend(SomeConstants)
p o.a
p o.b
p o.c
p o.class.ancestors
p o.singleton_class.ancestors
p o.singleton_methods
class SomeOtherClass
extend SomeConstants
end
s = SomeOtherClass
p s.a
p s.b
p s.c
p s.class.ancestors
p s.singleton_class.ancestors
p s.singleton_methods
Output:
"a"
"b"
"c"
[SomeClass, SomeConstants, Object, Kernel, BasicObject]
[#<Class:#<SomeClass:0x005580a51562a8>>, SomeClass, SomeConstants, Object, Kernel, BasicObject]
[]
"a"
"b"
"c"
[Object, Kernel, BasicObject]
[#<Class:#<Object:0x005580a5155448>>, SomeConstants, Object, Kernel, BasicObject]
[:a, :b, :c]
"a"
"b"
"c"
[Class, Module, Object, Kernel, BasicObject]
[#<Class:SomeOtherClass>, SomeConstants, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
[:a, :b, :c]
Are there some better approaches? Is above one asking for troubles?
Is it a common requirement that one wants to be able to extend an instance of an object
o = Object.new
o.extend SomeModule
and at the same time be able to include (mixin) such module
class A
include SomeModule
end
Can someone put some light on these?
I don’t know if it’s asking for trouble so much as that it might get confusing for users of an API if constants are being used not only as unique references to values in syntax, but also as config to specify mixin methods.
A lot of Ruby libraries and frameworks probably wouldn’t use constants like that, but instead, would treat them as the output of a generator.
A very common thing to see is block configuration DSL patterns used to define identifiers and values used to generate the dynamic methods:
ASCII = LookupTable.define do
uppercase_letter_a 'A'
lowercase_letter_a 'a'
end
CP_437 = LookupTable.define do
light_shade '░'
medium_shade '▒'
dark_shade '▓'
end
p ASCII::UPPERCASE_LETTER_A
p ASCII::LOWERCASE_LETTER_A
obj = Object.new
obj.extend(ASCII::Methods)
cls = Class.new
cls.include(ASCII::Methods)
cls_obj = cls.new
p cls_obj.uppercase_letter_a == obj.uppercase_letter_a
p cls_obj.lowercase_letter_a == obj.lowercase_letter_a
class BoxDrawing
include CP_437::Methods
end
# etc...
In some places, you might also see a module const declared in a namespace as ClassMethods, with singleton defined methods rather than instance defined. But the pattern is very similar across a lot of libraries—how you set up the particular definition of modules vs classes largely depends on the calling conventions/style you want for the generated code.
Whether consumers of a DSL or library embed the dynamically created modules
in object instances or declare classes in whichever way is mostly a style question. If you want your code to be called in a particular way by consumers, be sure to design the API and document the examples so that it’s easy for people to follow your intended setup.
Implementation for the above code looks like:
class LookupTable
class << self
def define(&block)
#table = Class.new
#table.const_set(:Methods, Module.new)
instance_eval(&block)
#table
end
def method_missing(identifier, value)
#table.const_set(identifier.upcase.to_s, value)
#table.const_get(:Methods).define_method(identifier, lambda { value })
end
end
end
There’s no reason why you couldn’t do the opposite of this and use declared consts as the input to a module generator. It’s not as common to see as the DSL/instance_eval patterns.
If you do want to go ahead with what you describe above, I would still recommend designing it in a way that the instance methods are separated into a different scope/attached to a different module than the consts so that you create a clear distinction in the API between what is expected to be embedded/included and what is expected to be referenced directly. Hence, I’d be leaning towards your first approach rather than the second (the second is very good for adaptive plugin APIs—take a look at the Sequel and Roda Gems for more inspiration in this direction).

Struct with and without member

I'm a new Rubyist, currently I'm going to use the Struct in class in order to create a temporary object. However, I encounter a question when I use a member in Struct like this:
class A
attr_accessor :b
B = Struct.new(:name, :print_name) do
def print_name
p "Hello #{name}"
end
end
def initialize(input_name)
#b = B.new(input_name)
end
end
a = A.new('Leo')
a.b.print_name # Hello Leo
But I also receive the same result when my Struct B params don't include :print_name.
B = Struct.new(:name) do
...
end
So what is the different ? And when should I use the member param and when I'm not ?
Thanks
In first case you define a class which's initializer takes two arguments - name and print_name.
In second case you define a class which's initializer takes single argument - name.
These has nothing to do with the fact, that you are defining an instance method called print_name.
So instances of both classes (with and without print_name argument) have print_name method defined, thus both examples work identically.
The difference will be visible when you inspect created objects:
# first case with two arguments
foo = B.new(:a, :b)
foo.inspect
=> "#<struct B name=:a, print_name=:b>"
# second case with single argument
foo = B.new(:a)
foo.inspect
=> "#<struct B name=:a>"
Also, when you'll check instance methods of B class for both cases, you can see the difference:
# first case with two arguments
B.instance_methods false
#=> [:name, :name=, :print_name, :print_name=]
# second case with single argument
B.instance_methods false
#=> [:name, :name=, :print_name]
But I also receive the same result when my Struct B params don't
include :print_name
The difference, is that in first case you are able to do the following:
a.b.print_name = 'new print name'
a.b.inspect
#=> "#<struct B name='Leo', print_name='new print name'>"
Whereas in second case it will fail:
a.b.print_name = 'new print name'
#=> NoMethodError: undefined method 'print_name=' for #<struct B name='Leo'>

Creating a new class with an existing name

We can create a class and then create another class with the same name. That is not surprising.
[1] pry(main)> class A; end
=> nil
[2] pry(main)> a = A.new
=> #<A:0x0000000bd8a008>
[3] pry(main)> A = Class.new
(pry):3: warning: already initialized constant A
(pry):1: warning: previous definition of A was here
=> A
[4] pry(main)> new_a = A.new
=> #<A:0x0000000be001e0>
[5] pry(main)> a.class.name == new_a.class.name
=> true
[6] pry(main)> a.class == new_a.class
=> false
[7] pry(main)> a.class == A
=> false
[8] pry(main)> new_a.class == A
=> true
However, after redefining the constant we get what seems to be a collision: constant A and new_a.classmethod return the new class, while a.class returns the original class. These classes are different, yet they have the same name. How can this be possible and what exactly is going on when this code is executed?
class A; end does two things:
it creates a new class object
it assigns this class object to the constant A
Removing or resassigning the constant only affects (2), it doesn't change the class object (1).
Ruby also sets the class name when assigning a class to a constant:
A.name #=> "A"
The class name is stored in a special instance variable (see below) and you see this name when inspecting an instance of your class:
A.new
#=> #<A:0x007febc1230848>
# ^
# |
# +- this is A.name
The class name is independent of the constant a class is assigned to:
B = A
B.name #=> "A"
B.new #=> #<A:0x007febc1313e68>
And this is why you can create multiple classes with the same name.
How the class name is stored internally
Ruby stores the class name in a special instance variable __classpath__. It can't be accessed from within Ruby, but if you would remove this restriction (I've patched Ruby for this example), you could read it:
A.instance_variable_get('__classpath__') #=> "A"
and even change it:
a = A.new #=> #<A:0x007fe0cd03ad30>
A.instance_variable_set('__classpath__', 'just a name')
A.name #=> "just a name"
a #=> #<just a name:0x007fe0cd03ad30>
These classes are different, yet they have the same name. How can this be possible and what exactly is going on when this code is executed?
It's perfectly possible for two different objects to return the same value when you call a certain method. There's really nothing sophisticated about that:
a = [1, 2]
b = 'AB'
a.size # => 2
b.size # => 2
Note that both a and b return 2 when I call size, but that does not imply any sort of relationship at all between a and b.
It’s the same like having two “John Smith” entries in your phonebook. Consider we have a class:
class A
def whoami
puts 'original'
end
end
Let’s now instantiate it:
a = new A
a_frozen = new A
a.whoami
#⇒ original
a_frozen.whoami
#⇒ original
Let’s modify a’s eighenclass:
class << a
def whoami
'modified'
end
end
a.whoami
#⇒ modified
a_frozen.whoami
#⇒ original
And, of course, it’s still like:
a.class.name == a_frozen.class.name == 'A'
Name is the name only, nothing more. And there is no problem for different class instances share the same name.
Hope it helps.
a and new_a are instances of two different classes.
You can access these classes with a.class and new_a.class.
These classes are two instances of Class. They appear to have the same name they really are different classes, with different methods, etc.
class A
def a
end
end
a= A.new
A = Class.new
new_a = A.new
puts a.respond_to?(:a) # => true
puts new_a.respond_to?(:a) # => false

Constants in singleton classes

I have this code:
class A
def print
puts CONSTANT
end
end
module B
CONSTANT = "Don't Panic!"
end
Suppose a is an instance of class A.
So I need a definition of CONSTANT that should be able to be found to make a.print available.
I considered to include the module B into a's singleton class like:
a.singleton_class.send :include, B
a.print # error: CONSTANT isn't found
I assumed it should be okay now to call the method but actually not.
The constant should be successfully imported as the following code works as expectation:
a.singleton_class.constants # => [.., :CONSTANT, ..]
However, by including the constant into the class instead of singleton class, it works:
a.class.send :include, B
a.print # prints: Don't Panic!
I thought it is strange that I can't refer a constant that is defined in a singleton class. If this is reasonable, I want to know why.
The class returned by singleton_class is an anonymous class which was inherited from class of the object a.
So a.class and a.singleton_class refer to different objects.
puts a.class.object_id # => 79495930
puts a.singleton_class.object_id # => 79495830
And also different classes: a.singleton_class is a child class of A.
a.singleton_class.superclass # => A
a.singleton_class.ancestors # => [B, A, Object, Kernel, BasicObject]
a.class.ancestors # => [A, Object, Kernel, BasicObject]
And because of this, a.singleton_class doesn't share its own constants to parent just like any other child class do.
puts a.class.constants # => []
puts a.singleton_class.constants # => [:CONSTANT]

Ruby and class variables in inherit class

class A
def set(v)
##v = v
end
def put
puts ##v
end
end
class B < A
end
class C < A
end
B.new.set 'b'
B.new.put # => b
C.new.set 'c'
C.new.put # => c
B.new.put # => c
Why? And how should I write this to have 'b' in last B.new.put?
Here is a nice article on the subject - Class and Instance Variables In Ruby.
Basically, what you can do is:
class A
class << self
attr_accessor :class_var
end
def set_class_var(value)
self.class.class_var = value
end
def get_class_var
self.class.class_var
end
end
class B < A; end
A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b
A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb
To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:
a = A.new
def a.foo
puts 'foo'
end
In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:
class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end
In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.
Another option is to pull out class_inheritable_accessor code from Rails and include its behavior in your classes. See here for a good discussion and the guts of the code.
Perhaps you don't really want a class variable, though.
Assigning a value to a class variable (an ## variable) sets it for EVERY instance of the class. It even "sets" it for instances that "aren't created yet." So, consider this...
B.new.set 'b' # OK, that set ##v for that particular instance of B
B.new.put # Hey, you just created *another* new instance of B!
How can ##v have a value in that one? The second object's value of ##v would be unset, except for the fact that ##v is a class variable, so it has the same value for every instance of the class.

Resources