Ruby assign one value to two variables in one line - ruby

I'm in the process of building my own webserver and want to make a logger. After server get a message, I want both to log it and send the message to the parsers. I need to make some moditications to the message for logging (eg remove the password), but when I change second variable, first is changed too!
msg = log_msg = JSON.parse(something)
log_msg[:password] = '[FILTERED]'
raise msg.inspect # => {..., :password => '[FILTERED]'}
How can I avoid this behavior of my code?
UPDATED It seems more strange, because of irb:
2.2.1 :001 > a = b = 1
=> 1
2.2.1 :002 > b = 2
=> 2
2.2.1 :003 > b
=> 2
2.2.1 :004 > a
=> 1

After the assignment, msg and log_msg reference to the same object. If this is not what you expected, try this:
log_msg = JSON.parse(something)
msg = log_msg.dup
Note that the other example behave differently because Fixnum is special. From the manual:
Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object.

This question is tightly linked to the following duscussions:
Object assignment in ruby
Does ruby pass by reference or by value
Ruby parameters by reference or by value
Please read them carefully to understand what's going on (this addressed your code snippet with assigning integer values aswell).
To assign by value you could clone or dup methods. Check the value of object_id to realize if you're working on the same object or not.
a = {} # => {}
b = a # => {}
b.object_id # => 114493940
a.object_id # => 114493940
b = a.clone # => {}
b.object_id # => 115158164
a.object_id # => 114493940

Related

Ruby: what's the difference between initializing a string using quotes vs colon?

Whats' the difference between initializing a string using quotes vs preceding it with the colon? I.e "bobo" vs :bobo. When you inspect them they appear the same but when you compare them the result evaluates to false.
irb(main):006:0> r = "bobo"
=> "bobo"
irb(main):007:0> puts r
bobo
=> nil
irb(main):008:0> t = :bobo
=> :bobo
irb(main):009:0> puts t
bobo
=> nil
irb(main):010:0> puts r == t
false
=> nil
irb(main):011:0> s = "bobo"
=> "bobo"
irb(main):012:0> puts r == s
true
=> nil
"bobo" is a string whereas :bobo is a symbol.
:bobo.class # => Symbol
"bobo".class # => String
String#==
If obj is not a String, returns false. Otherwise, returns true if str <=> obj returns zero.
So according to the documentation "bobo" == :bobo # => false and "bobo" == "bobo" # => true. - This is expected.
puts :bobo
# >> bobo
:bobo.to_s # => "bobo"
This is because puts applying to_s on the Symbol object :bobo. See Symbol#to_s
Notice the following:
"foo".object_id
=> 70353007334300
"foo".object_id
=> 70353007354360
:foo.object_id
=> 413928
:foo.object_id
=> 413928
Symbols are cached (same object_id) whereas strings are instantiated every time. Keep this in mind for performance knowledge but also keep in mind garbage collector ;)
The String-like thing beginning with a colon is a Symbol:
:bobo.class
# => Symbol
"bobo".class
# => String
When you inspect them, they look different:
:bobo.inspect
# => ":bobo"
"bobo".inspect
# => "\"bobo\""
When you print them with puts they look the same, because puts calls to_s on its arguments, and :bobo.to_s returns "bobo":
:bobo.to_s
# => "bobo"
:bobo.to_s == "bobo"
# => true
If you'd like to understand better the differences and how Symbols are used, perusing the documentation linked to above is a good place to start, as is any Ruby tutorial. This article is also worth reading: The Difference Between Ruby Symbols and Strings.
The "string with a colon" as you say, is a symbol. While they are similar to strings, symbols are immutable. Mutable objects can be changed after assignment. Immutable objects(:symbols) can not be changed after assignment. They can only be overwritten. Ruby is unique and is such a dynamic language, things can and often do change at runtime. Symbols are more rigid and won't be unexpectedly changed at runtime.
the main difference between symbol and string is memory allocation and mutability. strings can be altered anytime.
'prasad' << 'surase' => "prasadsurase"
:prasad << 'surase'
NoMethodError: undefined method `<<' for :prasad:Symbol
also two strings with same value(eg 'prasad' and 'prasad') have two different memory locations and have different object ids.
'prasad'.object_id => 102809450
'prasad'.object_id => 102894570
'prasad'.object_id => 103057580
whereas two same symbols are the same and have the same memory location since there is only a single object.
:prasad.object_id => 2358408
:prasad.object_id => 2358408
:prasad.object_id => 2358408
also, when string is created, it needs to be allocated memory n when not referred, it needs to be garbage collected but symbols stay in memory throughout the programs operation and are not garbage collected when not referred. this affects the performance of the program. u can collect all the declared symbols as
Symbol.all_symbols.include?(:prasad) => true

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.

Why assigned to variable constant is updated

Why when I assign constant to variable and update it, constant is being updated to? Is it expected behavior or bug?
ruby-1.9.3-p0 :001 > A = { :test => '123' }
=> {:test=>"123"}
ruby-1.9.3-p0 :002 > b = A
=> {:test=>"123"}
ruby-1.9.3-p0 :003 > b[:test] = '456'
=> "456"
ruby-1.9.3-p0 :004 > A
=> {:test=>"456"}
This is expected behavior, but why isn't always obvious. This is a very important distinction in languages like Ruby. There are three things in play here:
The constant A
The variable b
The hash { :test => '123' }
The first two are both kinds of variables. The third is an object. The difference between variables and objects is crucial. Variables just refer to objects. When you assign the same object to two variables, they both refer to the same object. There was only ever one object created, so when you change it, both variables refer to the changed object.
This is because of the shallow copy mechanism. In your example A and b are actually references to the same object. To avoid that use:
b = A.dup
This will initialize b with a copy of A instead of pointing it to the same hash(i.e. this uses deep copy).
For more info see here what shallow and deep copy is.

Ruby testing for definition of a variable

I know there are other questions similar such as:
Ruby: how to check if variable exists within a hash definition
Checking if a variable is defined?
But the answers aren't fully satisfactory.
I have:
ruby-1.9.2-p290 :001 > a=Hash.new
=> {}
ruby-1.9.2-p290 :002 > a['one']="hello"
=> "hello"
ruby-1.9.2-p290 :006 > defined?(a['one']['some']).nil?
=> false
ruby-1.9.2-p290 :007 > a['one']['some'].nil?
=> true
It seems like:
if a['one']['some'].nil?
a['one']['some']=Array.new
end
would be sufficient. Is this correct? Would this be correct for any data type? Is defined? needed in this case?
thx
You seem to be confusing two concepts. One is if a variable is defined, and another is if a Hash key is defined. Since a hash is, at some point, a variable, then it must be defined.
defined?(a)
# => nil
a = { }
# => {}
defined?(a)
# => "local-variable"
a.key?('one')
# => false
a['one'] = 'hello'
# => 'hello'
a.key?('one')
# => true
Something can be a key and nil at the same time, this is valid. There is no concept of defined or undefined for a Hash. It is all about if the key exists or not.
The only reason to test with .nil? is to distinguish between the two possible non-true values: nil and false. If you will never be using false in that context, then calling .nil? is unnecessarily verbose. In other words, if (x.nil?) is equivalent to if (x) provided x will never be literally false.
What you probably want to employ is the ||= pattern that will assign something if the existing value is nil or false:
# Assign an array to this Hash key if nothing is stored there
a['one']['hello'] ||= [ ]
Update: Edited according to remarks by Bruce.
I had to dig a number of pages deep into Google, but I eventually found this useful bit from the Ruby 1.9 spec:
"In all cases the test [defined?] is conducted without evaluating the operand."
So what's happening is that it looks at:
a['one']['some']
and says "that is sending the "operator []" message to the 'a' object - that is a method call!" and the result of defined? on that is "method".
Then when you check against nil?, the string "method" clearly isn't nil.
In addition to #tadmans answer, what you actually did in your example was to check, if the string "some" is included in the string "hello" which is stored in your hash at the position "one".
a = {}
a['one'] = 'hello'
a['one']['some'] # searches the string "some" in the hash at key "one"
A more simple example:
b = 'hello'
b['he'] # => 'he'
b['ha'] # => nil
That's why the defined? method did not return nil, as you expected, but "method".

When does variables in Ruby determine whether to hold a new reference?

I learned that in Ruby, variables hold references to objects, not the objects themselves.
For example:
a = "Tim"
b = a
a[0] = 'J'
Then a and b both have value "Jim".
However if I change the 3rd line to
a = "Jim"
Then a == Jim and b == Tim
I assume that means the code I changed created a new reference for a.
So why does changing a letter or changing the entire string make so much difference?
Follow-up question: Does Java work the same way?
Thank you.
The single thing to learn here is the difference between assignment and method call.
a = 'Jim'
is an assignment. You create a new string object (literal 'Jim') and assign it to variable a.
On the other side,
a[0] = 'J'
is a method call on an object already referenced by the variable a. A method call can't replace the object referenced by the variable with another one, it can just change the internal state of the object, and/or return another object.
I find that things like this are easiest to figure out using IRB:
>> a = 'Tim'
=> "Tim"
>> a.object_id
=> 2156046480
>> b = a
=> "Tim"
>> b.object_id
=> 2156046480
>> a.object_id == b.object_id
=> true
As you can see a and b have the same object_id, meaning they reference the same object. So when you change one, you change the other. Now assign something new to a:
>> a = 'Jim'
=> "Jim"
>> a.object_id
=> 2156019520
>> b.object_id
=> 2156046480
>> a.object_id == b.object_id
=> false
You made a point to a new object, while b still kept the old reference. Changing either of them now will not change the other one.
When you do a[0] = 'J', you're asking
Change the first character of the object referenced by a (which happens to be the same as b) to 'J'
While when you do a = "Jim", you're assigning an entirely new object reference (the string "Jim") to a. b is unaffected because you're not changing anything in the original reference.

Resources