Frozen objects in Ruby - ruby

In Ruby, what does it mean for a String or Array (etc) object to be 'Frozen'? How/where is this property set or modified?

It means you cannot modify it. You set it by freeze method.
s = "a"
concat modifies the string instance.
s.concat("b")
# => "ab"
When you freeze the string:
s.freeze
then, you cannot apply concat any more.
s.concat("c")
# => RuntimeError: can't modify frozen String
However, you can apply methods that do not modify the receiver:
s + "c"
# => "abc"

Prevents further modifications to obj. A RuntimeError will be raised if modification is attempted. There is no way to unfreeze a frozen object. See also Object#frozen?.
a = [ "a", "b", "c" ]
a.freeze
a << "z"
produces:
prog.rb:3:in `<<': can't modify frozen array (RuntimeError)
from prog.rb:3
Doco

Related

Couldn't understand the difference between `puts{}.class` and `puts({}.class)`

As the anonymous block and hash block looks like approximately same. I was doing kind of playing with it. And doing do I reached to some serious observations as below:
{}.class
#=> Hash
Okay,It's cool. empty block is considered as Hash.
print{}.class
#=> NilClass
puts {}.class
#=> NilClass
Now why the above code showing the same as NilClass,but the below code shows the Hash again ?
puts ({}.class)
#Hash
#=> nil
print({}.class)
#Hash=> nil
Could anyone help me here to understand that what's going one above?
I completely disagree with the point of #Lindydancer
How would you explain the below lines:
print {}.class
#NilClass
print [].class
#Array=> nil
print (1..2).class
#Range=> nil
Why not the same with the below print [].class and print (1..2).class?
EDIT
When ambiguity happens with local variable and method call, Ruby throws an error about the fact as below :
name
#NameError: undefined local variable or method `name' for main:Object
# from (irb):1
# from C:/Ruby193/bin/irb:12:in `<main>'
Now not the same happens with {} (as there is also an ambiguity between empty code block or Hash block). As IRB also here not sure if it's a empty block or Hash. Then why the error didn't throw up when IRB encountered print {}.class or {}.class?
The precedence rules of ruby makes print{}.class interpreted as (print{}).class. As print apparently returns a nil the class method returns #NilClass.
EDIT: As been discussed on other answers and in the updates to the question, print{} it of course interpreted as calling print with a block, not a hash. However, this is still about precedence as {} binds stronger than [] and (1..2) (and stronger than do ... end for that matter).
{} in this case is recognized as block passed to print, while [] unambiguously means empty array.
print {}.class # => NilClass
print do;end.class # => NilClass
You are running into some nuances of Ruby, where characters mean different things depending on context. How the source code is interpreted follows rules, one of which is that {} is a closure block if it follows a method call, and otherwise a Hash constructor.
It's common throughout the language to see characters mean different things depending on context or position within the statement.
Examples:
Parens () used for method call or for precedence
print(1..5).class => NilClass
print (1..5).class => Range <returns nil>
Square brackets [] used to call :[] method or for Array
print[].class => NoMethodError: undefined method `[]' for nil:NilClass
print([].class) => Array <returns nil>
Asterisk * used for multiplication or splatting
1 * 5 => 5
[*1..5] => [1, 2, 3, 4, 5]
Ampersand & used for symbol -> proc or logical and
0 & 1 => 0
[1, 2, 3].map(&:to_s) => ["1", "2", "3"]
Or in your case, braces used for block closures or for a hash
... hope it makes sense now ...

Why datamapper does not update records / detect dirtiness?

I recently wanted to write a simple migration script. I wrote:
#entries = Entries.all(:text => /test/)
#entries.each do |entry|
entry.update(:text => entry.text.gsub!(/test/, "no-test"))
end
It didn't save the records, even though the update statement returned true. What did I miss?
In the 1.x series of datamapper the dirty tracking is done via calling #== on the new and old attribute values to detect dirtyness. If an object is mutated inplace (for example with the String bang methods), the change cannot be detected as the "orignal" state gets mutated also.
Basically the following happens internally:
a = "foo"
b = a.gsub!("foo", "bar")
a == b # => true both a and b refer to the same mutated object
a.equal?(b) # => true
In your example you assign the original mutated attribute back to the object, no identity change => no update detected.
In case you create a new object via String#gsub istead of mutating the original attribute value via String#gsub! you end up with a detectable change.
With assigning a new object with different value the following happens:
a = "foo"
b = a.gsub("foo", "bar")
a == b # => false, loaded state does not equal resource state so change is detected
a.equal?(b) # => false
And for having all cases covered, assigning a new object with same value:
a = "foo"
b = "foo"
a == b # => true, no dirtyness detected.
a.equal?(b) # => false
Hopefully this explains the semantic differences good enough to explain all similar cases.
BTW In datamapper 2.0 we have a differend mechanism that will also catch in place mutations. Disclaimer, I'm the author of this component called dm-session.
Remove the exclamation.
entry.update(:text => entry.text.gsub(/test/, "no-test"))
The record doesn't go dirty when you replace the string content. You should reassign it.

Coercing a scalar or an array to become an array

Sometimes I want a variable to always be an array, whether its a scalar or already an array.
So I normally do:
[variable].flatten
which is compatible with ruby-1.8.5, 1.8.7, 1.9.x.
With this method when variable is a string (variable = "asdf"), it gives me ["asdf"]. If it's already an array (variable = ["asdf","bvcx"]), it gives me: ["asdf","bvcx"].
Does anyone have a better way? "Better" meaning more readable, more performant, succinct or more effective in other ways.
Array(variable)
should do the trick. It uses the little known Kernel#Array method.
The way I do, and think is the standard way, is using [*...]:
variable1 = "string"
variable2 = ["element1", "element2"]
[*variable1] #=> ["string"]
[*variable2] #=> ["element1", "element2"]
You might need something like Array.eat. Most other methods either call #to_a or #to_ary on the object. If you where using [ obj ].flatten that might give surprising results. #flatten will also mangle nested arrays unless called with a level parameter and will make an extra copy of the array.
Active support provides Array.wrap, but that also calls #to_ary, which might or might not be to your liking, depending on your needs.
require 'active_support/core_ext/array/wrap'
class Array
# Coerce an object to be an array. Any object that is not an array will become
# a single element array with object at index 0.
#
# coercing nil returns an empty array.
#
def self.eat( object )
object.nil? and return []
object.kind_of?( Array ) and return object
[object]
end
end # class Array
a = { a: 3 }
p [a].flatten # => [{:a=>3}]
p [*a] # => [[:a, 3]] -> OOPS
p Array a # => [[:a, 3]] -> OOPS
p Array.wrap a # => [{:a=>3}]
p Array.eat a # => [{:a=>3}]

Ruby change value in hash by reference

I'm using eval to work with a hash. This part works:
some_hash = {"a" => {"b" => "c"}}
target_hash = "some_hash"
target_key = "['a']"
my_value = eval(target_hash + target_key)
puts "my_value " + my_value.to_s
and prints:
my_value {"b"=>"c"}
How would I change the value, using eval, so that the hash results in this:
some_hash = {"a" => {"d" => "e"}}
Thanks
Edit:
I don't think I'm explaining correctly. I have to drill down into a hash, but I want the flexibility to do it with a string that is set at run time. That string could be "['key_level_1']['key_level_2']['key_level_3']" which is supposed to refer to some_hash['key_level_1']['key_level_2']['key_level_3'].
And again, i need to set that value to something. Does that make sense?
I would take an array e.g. ['key1', 'key2', 'key3'] (which can be constructed from an appropriately formatted string) and the "root object" and use it to locate a particular "target object" branch (I would recommend recursion). Then manipulate the appropriate object that was located. In this case some_hash should be the "root object" to start the traversal from, and not a string.
Here is a link to a really old answer I wrote (when I still actively used ruby). It doesn't handle the assignment bit, but I believe it shows the start of a valid approach that is eval-free: How do you access nested elements of a hash with a single string key? After the "target object" is located, then it's just a matter of assigning a new value to particular key. (The accepted answer in the linked post is also a gem, if not a little more cryptic and symbol-leaking.)
Happy coding.
You can use Hash#replace
def changeHash(a, b, targetHash, targetKey)
change = ".replace({'#{a}' => '#{b}'})"
my_value = eval(target_hash + target_key + change)
end
some_hash = {"a" => {"b" => "c"}}
target_key = "['a']"
changeHash('d', 'e', 'some_hash', '["a"]')
I suggest setting up a method like this:
def hash_change(target_hash,target_key,*new_value)
if new_value
target_hash["target_key"] = new_value
end
puts "my_value " + target_hash["target_key"]
end
That will give you flexibility to either display the original or a new hash should you pass in a new value.
Edit: sorry forgot the call to the method
hash_change(some_hash,"a",{"d" => "e"})
Thanks all, for the help. I changed it up a bit. Instead of passing in string as array, I just pass in an object like this:
{some_hash => {"a" => {"b" => "nil"}}}
Then I recursively traverse both the reference, and the real hash. When I detect nil in the reference, I know I am there.

Ruby multiple named arguments

I'm very new to ruby and I'm trying to write a web application using the rails framework. Through reading I've seen methods being called like this:
some_method "first argument", :other_arg => "value1", :other_arg2 => "value2"
Where you can pass an unlimited number of arguments.
How do you create a method in ruby that can be used in this way?
Thanks for the help.
That works because Ruby assumes the values are a Hash if you call the method that way.
Here is how you would define one:
def my_method( value, hash = {})
# value is requred
# hash can really contain any number of key/value pairs
end
And you could call it like this:
my_method('nice', {:first => true, :second => false})
Or
my_method('nice', :first => true, :second => false )
This is actually just a method that has a hash as an argument, below is a code example.
def funcUsingHash(input)
input.each { |k,v|
puts "%s=%s" % [k, v]
}
end
funcUsingHash :a => 1, :b => 2, :c => 3
Find out more about hashes here http://www-users.math.umd.edu/~dcarrera/ruby/0.3/chp_03/hashes.html
Maybe that *args can help you?
def meh(a, *args)
puts a
args.each {|x| y x}
end
Result of this method is
irb(main):005:0> meh(1,2,3,4)
1
--- 2
--- 3
--- 4
=> [2, 3, 4]
But i prefer this method in my scripts.
You can make the last argument be an optional hash to achieve that:
def some_method(x, options = {})
# access options[:other_arg], etc.
end
However, in Ruby 2.0.0, it is generally better to use a new feature called keyword arguments:
def some_method(x, other_arg: "value1", other_arg2: "value2")
# access other_arg, etc.
end
The advantages of using the new syntax instead of using a hash are:
It is less typing to access the optional arguments (e.g. other_arg instead of options[:other_arg]).
It is easy to specify a default value for the optional arguments.
Ruby will automatically detect if an invalid argument name was used by the caller and throw an exception.
One disadvantage of the new syntax is that you cannot (as far as I know) easily send all of the keyword arguments to some other method, because you don't have a hash object that represents them.
Thankfully, the syntax for calling these two types of methods is the same, so you can change from one to the other without breaking good code.

Resources