Mixins with state - ruby

I'm working through the "Pickaxe Book" and the author gives the following example as a technique for giving a module/mixin state without using an instance variable:
...the module could use a module-level hash, indexed by the current
object ID, to store instance-specific data...
module Test
State = {}
def state=(value)
State[object_id] = value
end
def state
State[object_id]
end
end
class Client
include Test
end
c1 = Client.new
c2 = Client.new
c1.state = 'cat'
c2.state = 'dog'
c1.state # => "cat"
c2.state # => "dog"
I am unclear on how this works exactly. In particular, object_id. How is the object_id method able to access the Client instance in this manner? I tried using length to see if it would index according to that, but I got:
NameError: undefined local variable or method `length' for #<Client:0x00000000ecc570>
I'd like to make sure I understand the principles of what's going on here.

How is the object_id method able to access the Client instance in this
manner?
The state=() method is inherited from the Test module when it is included. Included modules create an anonymous class that is inserted right above the including class in the inheritance chain.
This line:
c1.state = 'cat'
is equivalent to:
c1.state=('cat')
And when c1 calls state=(), inside the state=() method self will be equal to c1. Inside a def, self is equal to the object that called the method.
When you call a method with no receiver, then self is the implicit receiver. Inside state=():
def state=(value)
State[object_id] = value
end
the object_id() method is called with no receiver, so self becomes the receiver. As a result, the line:
State[object_id] = value
is equivalent to:
State[self.object_id] = value
which is equivalent to:
State[c1.object_id] = value

If Client includes Test, and c1 is a Client, then object_id is c1.object_id, inherited from Object. Each Ruby object is guaranteed a unique object_id. Not all objects are guaranteed to have length (and furthermore, many objects will have non-unique length, e.g. "f" and [8] share the length 1).

Related

In Ruby, how do I assign a value to a class instance

I want to create a class instance that has a value such that I can do something like puts a = Example.new(1) where a's value is specified in initialize.
I expect that this is a simple problem since all predefined Ruby classes allow this, but I'm unable to figure out how to do it for my classes.
Class#new and Return Values
Your example doesn't quite work because Ruby treats Class#new as a special case, and is expected to invoke the #initialize method and return an object. If it didn't, calling #new on a class would surprise a lot of people by returning the last evaluation of the initializer from your class, or from Object#new if it's otherwise undefined for your class. In either case, this would violate the principle of least surprise.
However, you can do what you want pretty easily by simply creating an accessor method and then chaining off of Example#new. For example, in Ruby 3.1.0:
class Example
attr_reader :int
def initialize(int) = (#int = int)
end
# prints `1` to STDOUT and assigns the value to *a*,
# but returns nil because you're using Kernel#puts
# which always returns nil
puts a = Example.new(1).int
# shows that the local variable *a* is set to the value
# returned by the Example#int accessor for the class'
# #int instance variable
a
#=> 1
To avoid the confusion of having a nil return value (even though this is expected with Kernel#puts, just change your puts statement to use Kernel#p instead:
p a = Example.new(2).int
#=> 2
Refactoring the Example Class for Older Rubies
If you're using an older Ruby than 3.0, you can't use an endless method or the improved handling for them in Ruby 3.1. The only difference is that rather than an inline method, you need to specify it with the standard def...end syntax, e.g.:
class Example
attr_reader :int
def initialize(int)
#int = int
end
end
Otherwise, the points above are valid as far back as any currently-supported Ruby version.

Copy object without any pointers in Ruby

I am trying to write a Greedy Algorithm for a certain problem. Simplified it looks like this:
There's an object called Foo with an randomized attribute called value and a method that changes this value change_value in a way that depends on an integer input
class Foo
def initialize
value = rand(1,10)
end
def change_value(input)
#changes the value in a certain way
end
end
Now the Greedy Algorithmus just gets the new value of Foo for all possible inputs and return the best input.
foo = Foo.new
best_value = 0
best_input = 0
(1..inputs).each do |k|
temp_foo = foo.clone
temp_foo.change_value(k)
if temp_foo.value>best_value
best_value = temp_foo.value
best_input = k
end
end
Foo.change_value(best_input)
The code works nearly as intended. The big problem is that the change_value-method within the each-funtion alters the temp_foo and the foo. What do I need to change to makes those objects completly dependent of each other? I also tried .dub by the way.
I think #clone or #dup won't work because they will share a reference to #value inside Foo.
In any case, you can do it more readably by changing Foo#change_value so it doesn't actually mutate the object but returns a copy:
class Foo
def initialize(value = nil)
#value = value || rand(10)
end
def change_value(input)
# returns a new Foo instance
Foo.new(#value + 1)
end
def value
#value
end
end
Because you're copying data in any case, using an immutable object (Value Object) is more general than some kind of deep clone.
I assume you assign value to the instance variable #value in Foo#initialize not the local variable value.
I also assume you don't have a simple primitive like in your code above but rather another object that contains a pointer, otherwise you most probably would not have such problem. In other words, I assume your change_value method makes an operation that relies on the #value pointer, such as #value[key] = some_new_value and not pure assignment, such as #value = some_new_object. When your object gets copied with clone or dup, that particular pointer is being copied, instead of the underlying structure, and therefore any calls to temp_foo.change_value will result in changes to foo's underlying #value.
To avoid this, you need to duplicate the object #value refers to. There is a trick you can use with Marshal, as discussed in this post, but I recommend against it since it causes a great deal of overhead. Instead, I would define a deep_dup method, such as below:
class Foo
def deep_dup
# Either this
#value = #value.dup
# OR this, and define the method #deep_dup in the class of #value
# to dup its internal structure too:
#value = #value.deep_dup
end
end
Then instead of doing temp_foo = foo.clone do temp_foo = foo.deep_dup.

Return containing object instance in ruby

I'm trying to implement a funky version of method chaining. Returning the instance of the class after each function call is easy, you just do
def chainable_method
some_code()
self
end
My idea is that the methods you can call depend on the previous method call. I'm trying to achieve this by returning an object belonging to the containing object. The contained object will have a few special methods, and then implement method_missing to return the containing object's instance.
Edit: The child object has some state associated with it that should be in itself, and not the parent. It might not have been clear previously as to why I need a whole instance for just method calls.
super is irrelevant in this case because the contained object doesn't inherit from the containing object, and I wouldn't want to call the containing object's methods on the contained object anyway - I want to call the containing object's methods on the containing object itself. I want the containing object, not the containing object class.
Not sure if this is possible.
Edit: reworded everything to use "containing/contained object" instead of the completely incorrect parent/child object.
Also, I'm using 1.9.3, if that matters. Version isn't important, I can change if needed.
My explanation was probably unclear. Here's the code:
class AliasableString
def initialize(string)
#string = string
end
def as(aka)
#aka = aka
end
def has_aka?
!#aka.nil?
end
# alias is a reserved word
def aka
#aka
end
def to_s
#string + (self.has_aka? ? (" as " + #aka) : "")
end
end
class Query
def initialize
#select_statements = Array.new
end
def select(statement)
select_statement = AliasableString.new(statement)
#select_statements.push(select_statement)
select_statement
end
def print
if #select_statements.size != 0
puts "select"
#select_statements.each_with_index {| select, i|
puts select
}
end
end
end
# Example usage
q0 = Query.new
q0.select("This is a select statement")
.select("Here's another one")
.as("But this one has an alias")
.select("This should be passed on to the parent!")
q0.print
I haven't yet fully implemented print. AliasableString needs to have #string and #aka separate so I can pull them apart later.
First of all, it doesn't matter what class of object is contained within a Query instance. All of the syntax shown on your 'example usage' section is appropriately defined in Query. The only requirement of the objects contained within a query instance is that they respond to as (or some similar method). What you have here is something like a state machine, but the only state that really matters is that some object occupies the last position in the select_statements array. Here's how I would build this (again, based mostly on your example at the end, I'm afraid I can't quite follow your initial explanation):
class Query
# ... initialize, etc.
def select(statement, statement_class = AliasableString)
select_statements << statement_class.new(statement)
self
end
def as(aka)
# this will only ever be used on the most recent statement added
statement_to_alias = select_statements.last
# throw an error if select_statements is empty (i.e., :last returns nil)
raise 'You must add a statement first' unless statement_to_alias
# forward the message on to the statement
statement_to_alias.as(aka)
# return the query object again to permit further chaining
self
end
end
AliasableString doesn't need to know a thing about Query; all it needs to do is respond appropriately to as.

Inconsistent behaviour with instance_eval

When passing a block to instance_eval, it is meant to be executed within the context of that instance. self, when referenced either explicitly or implicitly within that block, should refer to the instance that instance_eval has been called on. This seems to work fine in all cases, except when passing a method object that has been converted to a proc. In this case, self refers to the instance that the method is defined on, rather than the instance where the block is evaluated. Here's a code example to demonstrate what I mean:
class A
def test(&b)
instance_eval(&b)
end
end
class B
def test_a(a)
a.test { puts self }
end
def test_b_helper(*args)
puts self
end
def test_b(a)
m = method(:test_b_helper).to_proc
a.test(&m)
end
end
a = A.new
b = B.new
b.test_a(a) #<A:0x007ff66b086c68>
b.test_b(a) #<B:0x007fa3e1886bc0>
The expected behaviour is for both tests to return the same output. In this case, self should refer to an instance of A, not B.
I have looked in the docs and done some searches, but I have not been able to find information on this peculiarity. I am hoping that there are some experienced Rubyists who can help clear up this difference in behaviour.
Just to clarify, I am using Ruby 1.9.2.
The difference is, that Blocks and Procs are closures, Method objects are not.
Excerpt from D. Flanagan, Y. Matsumoto. The Ruby Programming Language, O'Reilly 2008, p. 204:
One important difference between Method objects and Proc objects is
that Method objects are not closures. Ruby’s methods are intended to
be completely self-contained, and they never have access to local
variables outside of their own scope. The only binding retained by a
Method object, therefore, is the value of self — the object on which the
method is to be invoked.
When you now cast the Method object to_proc, you bind the value of self to the calling instance of B, hence you get the result you described above. Actually, self is already fixed when you create the Method object.
This gets particularly clear when you consider the following code:
class A
def foo
puts 'bar'
end
end
class B; end
class C < A; end
foo = A.instance_method(:foo)
# => #<UnboundMethod: A#foo>
a = A.new
foo.bind(a).call
# bar
b = B.new
foo.bind(b).call
# TypeError: bind argument must be an instance of A
c = C.new
foo.bind(c).call
# bar
Simply put: self is always fixed on Method and UnboundMethod objects.

Why do functions called in a ruby rescue block fail to modify the variables?

I have a situation which is comparable with this code:
i=0
def add_one(i)
i+=1
puts "FUNCTION:#{i}"
end
begin
puts "BEGIN:#{i}"
raise unless i>5
rescue
add_one(i)
puts "RESCUE:#{i}"
retry
end
When I run this, I am seeing this output repeatedly:
BEGIN:0
FUNCTION:1
RESCUE:0
This version increments i and completes the program perfectly:
i=0
begin
puts "BEGIN:#{i}"
raise unless i>5
rescue
i+=1
puts "RESCUE:#{i}"
retry
end
Why is there a difference? How can I get a function in the rescue block to actually modify a variable?
This is because in your add_one function you are not manipulating the same i variable as outside of the function.
Let me try to explain that a bit. In Ruby, you deal with generally mutable objects (notable exceptions are numbers, true, false, and nil). A variable is a pointer to such an object. Multiple variables can point to the same object.
a = 123
b = a
Now a and b refer to the very same object. If you assign a new object to one of a or b, they they will refer to different object again, while still retaining the name.
What you have above are local variables. These are only valid inside a scope, mostly the duration of a method. If you create a new local variable (by assigning a value to it), it will only be valid during the duration of the method and will be garbage collected sometime after leaving the method.
Now as I said above, most objects in Ruby are mutable, meaning that you can change them while retaining the variable pointers. An example is adding an element to an array:
array = []
array << :foo
Now the array variable will still refer to the same Array object it got initialized with. But you have change the object. If you would assign the array variable a new array like
array = [:foo]
it would look like the same object, but effectively, they are different (you can verify that be checking the object_id method on every object. If it is the same, you are referring to the very same object)
Now when you do i += 1 in your add_one method, you are effectively running i = i + 1 which will set the i variable to a new value in the local method scope. You are not actually changing the i variable but you assign it a new value in the local method scope. This means that the variable named i on your outer scope (i.e. the begin/end block) will refer to a different object than the i variable in your add_one method. This is because while they have the same name, they have a different scope. The inner scope always masks the outer scope, so while you have variables with the same names in different scopes, they do not interfer in any way (this changes when dealing with instance or class variables)
Unfortunately, as I said above, numbers are immutable. You can't change them. If you assign a new number to a variable, it is a new object. Thus, you can't change the value of a variable pointing to a number in another scope as the code that changes the value.
To circumvent this, you could either return a value from your function and explicitly assign it to your i variable in the outer scope like this
i = 0
def add_one(i)
i+=1
puts "FUNCTION:#{i}"
return i
end
i = add_one(i)
or you could use instance variable of an object like this
class Foo
def initialize
#i = 0
end
def add_one
#i += 1
end
def do_something
begin
puts "BEGIN:#{#i}"
raise unless #i>5
rescue
add_one
puts "RESCUE:#{#i}"
retry
end
end
end
# create a new object and run the instance method
Foo.new.do_something
The "i" in add_one is a local reference, to the parameter--nutshell is that it's a different "i".
You'd need to use a variable in the correct scope.

Resources