Can `merge!` change a CONST in Ruby? - ruby

I have a situation where merge! seems to modify the value of a CONST. Can this occur? How?
I'm doing some API ingestion and mapping, like you do...
module Placement
FEATURE_DEFAULTS = {
"thingone" => "false",
"thingtwo" => "false"
}
def extract_features!(feat)
feat['norm_features'] ||= FEATURE_DEFAULTS
feat['norm_features'].merge!(
Array(feat['attributes']['feature']).reduce({}) do |h,f|
h[f] = "true"
h
end
)
end
def get_placement(_opts)
data_source["things"]["thing"].map do |thing|
product = {}
thing.each do |key, value|
new_key = RENAME_FIELDS[key] || key
new_value = REPLACE_FIELDS[key] || value
product[new_key] = new_value
end
binding.pry # 1
extract_features!(product)
binding.pry # 2
product
end
end
end
Later, I include this in a class for the API client, and then I call the get_placement method.
Dilemma
step 1
For the first run, in pry binding 1 & 2, the value of FEATURE_DEFAULTS is as seen above. For 2 the value of FEATURE_DEFAULTS is the same, and the value of product['norm_features'] is the same (plus the results of the meshing operation of extract_features!
step 2
The output (to the caller of get_placement), for every thing/product, is
FEATURE_DEFAULTS = {
"thingone" => true,
"thingtwo" => true
}
step 3
When I run this the second time (after starting up the service / app), the value of FEATURE_DEFAULTS, and pry binding 1 & 2, is
FEATURE_DEFAULTS = {
"thingone" => true,
"thingtwo" => true
}
What is happening here?
This seems to confirm that, after running the extract_features! method, the FEATURE_DEFAULTS CONST is changed. If I do not use merge! in extract_features!, and use merge instead, then the CONST value does not change.
I can post more code, if needed, or most to a Gist.
Ruby MRI 2.2.2
I am doing this inside a rails app, but I don't see that it matters.

I have a situation where merge! seems to modify the value of a CONST. Can this occur? How?
No, in general, methods cannot change variable bindings, regardless of whether those variables are local variables, instance variables, class variables, global variables, or constants. Variables aren't objects in Ruby, you can't call methods on them, you can't pass them as arguments to methods, ergo, you can't tell them to change themselves.
The exception to this are meta-programming methods like Binding#local_variable_set, Object#instance_variable_set, Module#class_variable_set, or Module#const_set.
What you can do, however, and what you are doing in this case, is tell the object the variable points to to change itself. The documentation for Hash#merge! is unfortunately a bit unclear in that it does not explicitly mention that Hash#merge! mutates its receiver.
This seems to confirm that, after running the extract_features! method, the FEATURE_DEFAULTS CONST is changed. If I do not use merge! in extract_features!, and use merge instead, then the CONST value does not change.
No, it only confirms that the state of the object FEATURE_DEFAULTS points to changed, it doesn't say anything about whether the binding of FEATURE_DEFAULTS, i.e. which object it points to, changed. You can confirm that the constant still points to the same object by looking at its object_id.
Of course, constants can be re-assigned anyway, albeit triggering a warning.

Related

How does a code block in Ruby know what variable belongs to an aspect of an object?

Consider the following:
(1..10).inject{|memo, n| memo + n}
Question:
How does n know that it is supposed to store all the values from 1..10? I'm confused how Ruby is able to understand that n can automatically be associated with (1..10) right away, and memo is just memo.
I know Ruby code blocks aren't the same as the C or Java code blocks--Ruby code blocks work a bit differently. I'm confused as to how variables that are in between the upright pipes '|' will automatically be assigned to parts of an object. For example:
hash1 = {"a" => 111, "b" => 222}
hash2 = {"b" => 333, "c" => 444}
hash1.merge(hash2) {|key, old, new| old}
How do '|key, old, new|' automatically assign themselves in such a way such that when I type 'old' in the code block, it is automatically aware that 'old' refers to the older hash value? I never assigned 'old' to anything, just declared it. Can someone explain how this works?
The parameters for the block are determined by the method definition. The definition for reduce/inject is overloaded (docs) and defined in C, but if you wanted to define it, you could do it like so (note, this doesn't cover all the overloaded cases for the actual reduce definition):
module Enumerable
def my_reduce(memo=nil, &blk)
# if a starting memo is not given, it defaults to the first element
# in the list and that element is skipped for iteration
elements = memo ? self : self[1..-1]
memo ||= self[0]
elements.each { |element| memo = blk.call(memo, element) }
memo
end
end
This method definition determines what values to use for memo and element and calls the blk variable (a block passed to the method) with them in a specific order.
Note, however, that blocks are not like regular methods, because they don't check the number of arguments. For example: (note, this example shows the usage of yield which is another way to pass a block parameter)
def foo
yield 1
end
# The b and c variables here will be nil
foo { |a, b, c| [a,b,c].compact.sum } # => 1
You can also use deconstruction to define variables at the time you run the block, for example if you wanted to reduce over a hash you could do something like this:
# this just copies the hash
{a: 1}.reduce({}) { |memo, (key, val)| memo[key] = val; memo }
How this works is, calling reduce on a hash implicitly calls to_a, which converts it to a list of tuples (e.g. {a: 1}.to_a = [[:a, 1]]). reduce passes each tuple as the second argument to the block. In the place where the block is called, the tuple is deconstructed into separate key and value variables.
A code block is just a function with no name. Like any other function, it can be called multiple times with different arguments. If you have a method
def add(a, b)
a + b
end
How does add know that sometimes a is 5 and sometimes a is 7?
Enumerable#inject simply calls the function once for each element, passing the element as an argument.
It looks a bit like this:
module Enumerable
def inject(memo)
each do |el|
memo = yield memo, el
end
memo
end
end
And memo is just memo
what do you mean, "just memo"? memo and n take whatever values inject passes. And it is implemented to pass accumulator/memo as first argument and current collection element as second argument.
How do '|key, old, new|' automatically assign themselves
They don't "assign themselves". merge assigns them. Or rather, passes those values (key, old value, new value) in that order as block parameters.
If you instead write
hash1.merge(hash2) {|foo, bar, baz| bar}
It'll still work exactly as before. Parameter names mean nothing [here]. It's actual values that matter.
Just to simplify some of the other good answers here:
If you are struggling understanding blocks, an easy way to think of them is as a primitive and temporary method that you are creating and executing in place, and the values between the pipe characters |memo| is simply the argument signature.
There is no special special concept behind the arguments, they are simply there for the method you are invoking to pass a variable to, like calling any other method with an argument. Similar to a method, the arguments are "local" variables within the scope of the block (there are some nuances to this depending on the syntax you use to call the block, but I digress, that is another matter).
The method you pass the block to simply invokes this "temporary method" and passes the arguments to it that it is designed to do. Just like calling a method normally, with some slight differences, such as there are no "required" arguments. If you do not define any arguments to receive, it will happily just not pass them instead of raising an ArgumentError. Likewise, if you define too many arguments for the block to receive, they will simply be nil within the block, no errors for not being defined.

Ruby: tap writes on a read?

So if I understand correctly Object#tap uses yield to produce a temporary object to work with during the execution of a process or method. From what I think I know about yield, it does something like, yield takes (thing) and gives (thing).dup to the block attached to the method it's being used in?
But when I do this:
class Klass
attr_accessor :hash
def initialize
#hash={'key' => 'value'}
end
end
instance=Klass.new
instance.instance_variable_get('#hash')[key] # => 'value', as it should
instance.instance_variable_get('#hash').tap {|pipe| pipe['key']=newvalue}
instance.instance_variable_get('#hash')[key] # => new value... wut?
I was under the impression that yield -> new_obj. I don't know how correct this is though, I tried to look it up on ruby-doc, but Enumerator::yielder is empty, yield(proc) isn't there, and the fiber version... I don't have any fibers, in fact, doesn't Ruby actually explicitly require include 'fiber' to use them?
So what ought have been a read method on the instance variable and a write on the temp is instead a read/write on the instance variable... which is cool, because that's what I was trying to do and accidentally found when I was looking up a way to deal with hashes as instance variables (for some larger-than-I'm-used-to tables for named arrays of variables), but now I'm slightly confused, and I can't find a description of the mechanism that's making this happen.
Object#tap couldn't be simpler:
VALUE
rb_obj_tap(VALUE obj)
{
rb_yield(obj);
return obj;
}
(from the documentation). It just yields and then returns the receiver. A quick check in IRB shows that yield yields the object itself rather than a new object.
def foo
x = {}
yield x
x
end
foo { |y| y['key'] = :new_value }
# => {"key" => :new_value }
So the behavior of tap is consistent with yield, as we would hope.
tap does not duplicate the receiver. The block variable is assigned the very receiver itself. Then, tap returns the receiver. So when you do tap{|pipe| pipe['key']=newvalue}, the receiver of tap is modified. To my understanding,
x.tap{|x| foo(x)}
is equivalent to:
foo(x); x
and
y.tap{|y| y.bar}
is equivalent to:
y.bar; y

ruby and references. Working with fixnums

I know a bit about ruby way to handle objects and references. The replace stuff, ect ...
I know it d'ont work on fixnum, cause the var is the fixnum. But i wish to change the value of a fixnum inside a function, and that the value changed in the ouside var.
How can i do this ?
I guess i can use a string like this "1" but that's quite dirty.
Ruby will always pass-by-reference (because everything is an object) but Fixnum lacks any methods that allow you to mutate the value. See "void foo(int &x) -> Ruby? Passing integers by reference?" for more details.
You can either return a value that you then assign to your variable, like so:
a = 5
def do_something(value)
return 1 #this could be more complicated and depend on the value passed in
end
a = do_something(a)
or you could wrap your value in an object such as a Hash and have it updated that way.
a = {:value => 5}
def do_something(dict)
dict[:value] = 1
end
do_something(a) #now a[:value] is 1 outside the function
Hope this helps.
You could pass an array with a single number, like [1], or a hash like {value: 1}. Less ugly than a string, as your number itself remains a number, but less overhead than a new class...
When I was building a game I had the same problem you have. There was a numeric score that represented how many zombies you've killed and I needed to manually keep it in sync between Player (that incremented the score), ScoreBar and ScoreScreen (that displayed the score). The solution I've found was creating a separate class for the score that will wrap the value and mutate it:
class Score
def initialize(value = 0)
#value = value
end
def increment
#value += 1
end
def to_i
#value
end
def to_s
#value.to_s
end
end

How can I get a reference to a method that contains the arguments used for invocations, in Ruby?

Given this code:
a = {1=>2}
m = a.method(:[])
I know that I can now use :
value = m.call(1)
and it will return 2. The thing is, what do I need to change so that I can call the method directly like :
m.call()
and it will get the 1 sent as a parameter? It would be nice to be able to write something like :
m = a.method(:[],1) # where the symbol is the method, and 1 will be the parameter it will be called with
The thing is, I'd like to delay the execution of certain parts of my script until some objects get created, and I'd like to avoid rewriting EVERYTHING to use lambdas.
Basically, what you want is a way to curry the function.
http://en.wikipedia.org/wiki/Curry_function
This can be done in many different ways, one of which:
def curry(method, *params)
lambda { send method, *params }
end
You can add this to Hash's metaclass, or to a module you want to include in some of your objects, etc. Then, calling it becomes the usecase you wanted:
irb(main):001:0> a = {1 => 2}
=> {1=>2}
... # add curry to Hash's metaclass
irb(main):011:0> m = a.curry :[], 1
=> #<Proc:0xb76e2154#(irb):8>
irb(main):012:0> m.call
=> 2
There's more than one way to do it, I'm sure.
a = {1=>2}
class << a
def fetch_what(key)
Proc.new { self[key] }
end
end
....
m = a.fetch_what(1)
m.call() # -> 2
It sounds like you should attach the method parameters to the object you're calling the method on, and have the method access them as instance variables.
In terms of simple refactoring steps:
Introduce new instance variables, one per method parameter.
Introduce new accessors for the instance variables.
Refactor the method to use the instance variables if the parameters are not supplied.
Refactor the calling code to set the instance variables through the accessors, at some point prior to the method call.
Refactor the calling code to pass no parameters in the method call.
As an example, refactor calling code like this:
widget = Widget.new
assembly_method = widget.method(:assemble)
# Time passes...
assembly_method.call(:electric, :smooth)
to work like this:
widget = Widget.new
widget.frombulator = :electric
widget.jazzifier = :smooth
assembly_method = widget.method(:assemble)
# Time passes...
assembly_method.call
It's not sexy or clever, but it will result in code that expresses its intent, and odds are good that it will address the real problem, namely that something is missing from your model.

How can I use C# style enumerations in Ruby?

I just want to know the best way to emulate a C# style enumeration in Ruby.
Specifically, I would like to be able to perform logical tests against the set of values given some variable. Example would be the state of a window: "minimized, maximized, closed, open"
If you need the enumerations to map to values (eg, you need minimized to equal 0, maximised to equal 100, etc) I'd use a hash of symbols to values, like this:
WINDOW_STATES = { :minimized => 0, :maximized => 100 }.freeze
The freeze (like nate says) stops you from breaking things in future by accident.
You can check if something is valid by doing this
WINDOW_STATES.keys.include?(window_state)
Alternatively, if you don't need any values, and just need to check 'membership' then an array is fine
WINDOW_STATES = [:minimized, :maximized].freeze
Use it like this
WINDOW_STATES.include?(window_state)
If your keys are going to be strings (like for example a 'state' field in a RoR app), then you can use an array of strings. I do this ALL THE TIME in many of our rails apps.
WINDOW_STATES = %w(minimized maximized open closed).freeze
This is pretty much what rails validates_inclusion_of validator is purpose built for :-)
Personal Note:
I don't like typing include? all the time, so I have this (it's only complicated because of the .in?(1, 2, 3) case:
class Object
# Lets us write array.include?(x) the other way round
# Also accepts multiple args, so we can do 2.in?( 1,2,3 ) without bothering with arrays
def in?( *args )
# if we have 1 arg, and it is a collection, act as if it were passed as a single value, UNLESS we are an array ourselves.
# The mismatch between checking for respond_to on the args vs checking for self.kind_of?Array is deliberate, otherwise
# arrays of strings break and ranges don't work right
args.length == 1 && args.first.respond_to?(:include?) && !self.kind_of?(Array) ?
args.first.include?( self ) :
args.include?( self )
end
end
end
This lets you type
window_state.in? WINDOW_STATES
It's not quite the same, but I'll often build a hash for this kind of thing:
STATES = {:open => 1, :closed => 2, :max => 3, :min => 4}.freeze()
Freezing the hash keeps me from accidentally modifying its contents.
Moreover, if you want to raise an error when accessing something that doesn't exist, you can use a defualt Proc to do this:
STATES = Hash.new { |hash, key| raise NameError, "#{key} is not allowed" }
STATES.merge!({:open => 1, :closed => 2, :max => 3, :min => 4}).freeze()
STATES[:other] # raises NameError
I don't think Ruby supports true enums -- though, there are still solutions available.
Enumerations and Ruby
The easiest way to define an Enum in ruby to use a class with constant variables.
class WindowState
Open = 1
Closed = 2
Max = 3
Min = 4
end
Making a class or hash as others have said will work. However, the Ruby thing to do is to use symbols. Symbols in Ruby start with a colon and look like this:
greetingtype = :hello
They are kind of like objects that consist only of a name.

Resources