I am learning Ruby & Perl has this very convenient module called Data::Dumper, which allows you to recursively analyze a data structure (like hash) & allow you to print it. This is very useful while debugging. Is there some thing similar for Ruby?
Look into pp
example:
require 'pp'
x = { :a => [1,2,3, {:foo => bar}]}
pp x
there is also the inspect method which also works quite nicely
x = { :a => [1,2,3, {:foo => bar}]}
puts x.inspect
Related
I am using the digest module for some task.
As always, ruby-doc has a very nice documentation.
When I want to get the classes from a module or class:
module A
class B end
C = Class.new(B)
D ||= 1
end
A.const_set(:E, Math::E)
p A.constants # => [:B, :C, :D, :E]
p A.constants.select { |x| A.const_get(x).kind_of?(Class) } # => [:B, :C]
But in Digest module, something ridiculous is happening:
require 'digest' # => true
p Digest.constants # => [:Class, :Base, :REQUIRE_MUTEX, :Instance]
p Digest::SHA1 # => Digest::SHA1
p Digest.constants # => [:Class, :Base, :SHA1, :REQUIRE_MUTEX, :Instance]
p Digest::SHA2 # => Digest::SHA2
p Digest.constants # => [:Class, :SHA2, :Base, :SHA1, :SHA256, :REQUIRE_MUTEX, :SHA512, :SHA384, :Instance]
As you can see the constant method doesn't return SHA1 class (on line 2). Then when I write Digest::SHA1 the program loads up the SHA1 class. Then when you run call the constants method again, it returns the SHA1 in the array.
What's going on here? Is there a way not to load all the classes in my program the same way? Is this an efficient design?
In the case of Digest, this behaviour is intentional. Depending on the hashing algorithm, different libraries are required. This is counterintuitive, but it's also expected behaviour as you can see here: https://github.com/ruby/ruby/blob/master/ext/digest/lib/digest.rb#L8-L25.
To change that, Ruby Core would need according adoption.
I hope you find this helpful.
The magic is in the const_missing singleton method in the Digest module. #knugje has pointed out a very useful link in his answer which makes in clearer.
ri documentation about const_missing says:
Invoked when a reference is made to an undefined constant in mod
So here's the trick:
module X
def self.const_missing(name)
name
end
end
p X.constants # => []
p X::Y # => :Y
p X.constants # => []
So the code doesn't define the constant in the module. So we can use a fairly simple trick:
module X
define_singleton_method(:const_missing) { |n| const_set(n, n.id2name) }
end
p X.constants # => []
p X::Y # => "Y"
p X::Z # => "Z"
p X.constants # => [:Y, :Z]
p X::Z # "Z"
That's how const_missing is used here. But Instead of const_set, what digest does is loading up a file with the name and do some exception handling and checking if the constants exist on the file.
Personally speaking, this may create confusion. For example, when you have no document to offline and online documents, you can't just simply run Digest.constants to list the available constants.
As knugie pointed out, you can read about the const_missing implementation in the Digest module here.
Had this as part of an interview,I guessed it took incoming data, translated it into local language then commits to database? Which was obviously wrong.
def optimize(hsh)
hsh.reduce({}) do |new_hsh, (k,v)|
new_hsh[k.to_sym] = v.kind_of?(Hash) ? optimize(v) : v
new_hsh
end
end
It looks like it just recursively converts keys from nested hashes to symbols.
optimize({'k' => {'l' => 'v'}})
#=> {:k=>{:l=>"v"}}
Optimize is a poor name and each_with_object should be used instead of reduce :
def symbolize_keys(hash)
hash.each_with_object({}) do |(k, v), new_hash|
new_hash[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v
end
end
puts symbolize_keys('k' => { 'l' => 'v' })
#=> {:k=>{:l=>"v"}}
This method could be used to make sure that a nested hash has the correct keys. Some devs like to use string keys, others prefer symbols :
{'a' => 'b'}[:a]
#=> nil
symbolize_keys({'a' => 'b'})[:a]
#=> 'b'
Servers talk to each others with strings but Rails code is often written with symbols as keys. That's one of the reasons why HashWithIndifferentAccess exists.
I'm trying to implement the rails-settings gem (https://github.com/100hz/rails-settings) into my Rails 3 project using Ruby 1.8.7
Setting and retrieving the settings works perfectly, but I get an error if I try getting all settings of a specific user.
So, in the 'rails console' the following works:
user = User.find(123)
user.settings.color = :red
user.settings.color
But if I try to get all settings:
user.settings.all
I get:
NoMethodError: undefined method `merge' for []:Array
from /[...]/.rvm/gems/ruby-1.8.7-p334/bundler/gems/rails-settings-883114dfd933/lib/rails-settings/settings.rb:55:in `all'
from (irb):5
line 55 in the settings.rb:
#retrieve all settings as a hash (optionally starting with a given namespace)
def self.all(starting_with=nil)
options = starting_with ? { :conditions => "var LIKE '#{starting_with}%'"} : {}
vars = thing_scoped.find(:all, {:select => 'var, value'}.merge(options))
result = {}
vars.each do |record|
result[record.var] = record.value
end
# line 55 is below this one...
##defaults.select{ |k| k =~ /^#{starting_with}/ }.merge(result).with_indifferent_access
end
Whats the problem here? Or is this a ruby 1.8.7 vs. 1.9.2 thing?
That's a Ruby 1.8.7 vs. 1.9.2 thing
The Hash select method under ruby 1.8.7 will return an Array of Arrays.
Example:
{:a => 'a', :b => 'b', :c => 'c'}.select {|k, v| v > 'a'} #=> [[:b,'b'],[:c,'c']]
While the same thing running Ruby 1.9.2 will return:
{:a => 'a', :b => 'b', :c => 'c'}.select {|k, v| v > 'a'} #=> {:b => 'b',:c => 'c'}
You will need to post process the result and turn it into a hsah again or use something like inject.
Edit:
Here is a quick/ugly example of the inject
{:a => 'a', :b => 'b', :c => 'c'}.inject({}) {|r, e| e[1] > 'a' ? r.merge({e[0] => e[1]}) : r }
Semantically speaking:
collection.inject(container) { |container, element| select_condition ? container + element : container }
Edit 2: (Based on #CaleyWoods post)
Hash[*##defaults.select{ |k,v| k =~ /^#{starting_with}/ }.flatten].merge(result)
The |k, v| will prevent unnecessary warnings.
This looks like it's trying to do a merge on an Array which is not a method the Array class in Ruby. Merge is supported for Hash, it looks like your returned object is not the correct type. The author was definitely relying on a hash, in the next to last line 'with_indifferent_access' is called which is trying to allow you to select items from the hash with strings or symbols.
I can't examine the gem further right now and I wish I could provide a more helpful answer. If this hasn't been answered later i'll come back and help you out.
Not sure why the author is using double and single quotes in options and vars. He's also trying to populate the hash by hand instead of with inject. It's not awful by any means but I think there's room for improvement in the small bit of code you posted from the file.
I am learning Ruby & Perl has this very convenient module called Data::Dumper, which allows you to recursively analyze a data structure (like hash) & allow you to print it. This is very useful while debugging. Is there some thing similar for Ruby?
Look into pp
example:
require 'pp'
x = { :a => [1,2,3, {:foo => bar}]}
pp x
there is also the inspect method which also works quite nicely
x = { :a => [1,2,3, {:foo => bar}]}
puts x.inspect
I normally use a YAML dump if I need to quickly check something.
In irb the syntax is simply y obj_to_inspect. In a normal Ruby app, you may need to add a require 'YAML' to the file, not sure.
Here is an example in irb:
>> my_hash = {:array => [0,2,5,6], :sub_hash => {:a => 1, :b => 2}, :visible => true}
=> {:sub_hash=>{:b=>2, :a=>1}, :visible=>true, :array=>[0, 2, 5, 6]}
>> y my_hash # <----- THE IMPORTANT LINE
---
:sub_hash:
:b: 2
:a: 1
:visible: true
:array:
- 0
- 2
- 5
- 6
=> nil
>>
The final => nil just means the method didn't return anything. It has nothing to do with your data structure.
you can use Marshal, amarshal, YAML
When I make a new array/hash in irb, it prints out a nice format to show the structure, ex.
["value1", "value2", "value3"]
{"key1" => "value1"}
... but when I try to print out my variables using puts, I get them collapsed:
value1
value2
value3
key1
value1
I gather that puts is not the right command for what I want, but what is? I want to be able to view my variables in irb in the first format, not the second.
You can either use the inspect method:
a=["value1", "value2", "value3"]
puts a.inspect
Or, even better, use the pp (pretty print) lib:
require 'pp'
a=["value1", "value2", "value3"]
pp a
Another thing you can do is use the y method which converts input into Yaml. That produces pretty nice output...
>> data = { 'dog' => 'Flemeale', 'horse' => 'Gregoire', 'cow' => 'Fleante' }
=> {"cow"=>"Fleante", "horse"=>"Gregoire", "dog"=>"Flemeale"}
>> y data
---
cow: Fleante
horse: Gregoire
dog: Flemeale
The pretty print works well, but the Awesome_Print gem is even better! You will have to require awesome_print but it handles nested hashes and arrays beautifully plus colors them in the Terminal using 'ap' instead of 'p' to puts the output.
You can also include it in your ~/.irbrc to have this as the default method for displaying objects:
require "awesome_print"
AwesomePrint.irb!
Try .inspect
>> a = ["value1", "value2", "value3"]
=> ["value1", "value2", "value3"]
>> a.inspect
=> "[\"value1\", \"value2\", \"value3\"]"
>> a = {"key1" => "value1"}
=> {"key1"=>"value1"}
>> a.inspect
=> "{\"key1\"=>\"value1\"}"
You can also use the p() method to print them:
>> p a
{"key1"=>"value1"}
My personal tool of choice for this is 'Pretty Print' and the pp method
require 'pp' # <- 'Pretty Print' Included in ruby standard library
pp({ :hello => :world, :this => ['is', 'an', 'array'] })
=> {:hello=>:world, :this=>["is", "an", "array"]}