How does Ruby Mocha stub a nested hash? - ruby-on-rails

I have a method here that needs to be mocked using Mocha, but currently i have no clue how to mock the nested hash here.
Products.new(:A => "aa", :B => "bb").containers['container_A'].elements['element_b']
So far, i know how to stub Products.new(:A => "aa", :B => "bb"), but have no idea with the hash part after it.
Thanks in advance.

What about a hash/OpenStruct?
require 'ostruct'
product.expects(:containers).
returns('container_A' => OpenStruct.new(:elements => {'element_b' => 'expected_value'}))
puts product.containers['container_A'].elements['element_b']
# => expected_value

Related

rspec assigns converts hash keys to string

I have a controller action where i am assigning a hash to an instance variable. In my rspec test file, i am using assigns to test it the instance variable is assigned to the value i expect. For some reason, assigns gives me a hash with string keys. If i print the instance variable in the controller, i has symbol keys
Please find the code below. It is simplified.
class TestController < ApplicationController
def test
#test_object = {:id => 1, :value => 2, :name => "name"}
end
end
My test file:
describe TestController do
it "should assign test_object" do
get :test
assigns(:test_object).should == {:id => 1, :value => 2, :name => "name"}
end
end
The above test fails with the error message
expected: {:id=>1, :value=>2, :name=>"name"}
got: {"id"=>1, "value"=>2, "name"=>"name"}
Please help me understand why it is doing that.
RSpec borrows assigns from the regular Rails test/unit helpers and it's using with_indifferent_access to return the requested instance variable as in assigns(:my_var).
Hash#with_indifferent_access returns a key-stringified version of the hash (a deep copy), which has the side effect of stringfiying the keys of instance variables that are hashes.
If you try to match the entire hash, it will fail, but it works if you are checking the values of specific keys, whether they're a symbol or a string.
Maybe an example will help clarify:
{:a => {:b => "bravo"}}.with_indifferent_access => {"a"=>{"b"=>"bravo"}}
{:a => {:b => "bravo"}}.with_indifferent_access[:a][:b] => "bravo"

Ruby on Rails: pretty print for variable.hash_set.inspect ... is there a way to pretty print .inpsect in the console?

I findy myself doing a lot of puts .inpsect s in my functional testing to make sure I know how the data is formatted... but hashes are hard to read when there is no new lines after each entry in a hash object.
Is there anyway, maybe a gem?, to pretty print hashes?
So that it looks something like this:
{
entry1 => {
entrey1.1 => 1,
entry1.2 => 3
},
entry2 => 3
}
instead of: { entry1 => { entrey1.1 => 1, entry1.2 => 3}, entry2 => 3 }
?
Thanks!
you could use the awesome_print gem for that.
https://github.com/michaeldv/awesome_print
require 'awesome_print' # if you like to have it in irb by default, add it to your irbrc
>> ap({:a => 1, :b => [1,2,3], :c => :d})
{
:b => [
[0] 1,
[1] 2,
[2] 3
],
:a => 1,
:c => :d
}
btw, instead of puts object.inspect you can also just use p object which calls inspect in the object before printing it.
another way to print objects a little nicer than the default puts is to use pp from the ruby stdlib ( http://ruby-doc.org/stdlib/libdoc/pp/rdoc/index.html )
You could always redefine Hash#inspect in your .irbrc file if you'd like, format them any way you want. That will only affect your interactive environment. An alternative is to express them as YAML which is often more readable. For instance:
def y(object)
puts object.to_yaml
return
end
This way you can run y on objects as you might p today.

Rails 3, Active Record query returns ActiveRecord::Relation object, instead of objects

I feel like this is a simple problem I'm having due to my misunderstanding of the new ActiveRecord query interface, but take this example:
>> Category.first.recipes
=> [ ... ] # array of recipes
However:
>> Category.where(:id => 1).recipes
=> NoMethodError: undefined method `recipes' for #<ActiveRecord::Relation:0x000001033dc9e0>
What's going on here? why does my where method return an ActiveRecord::Relation object? how can I retrieve the objects from the query here?
This is actually intentional.
Category.where(:id => 1)
# Is Equivalent to Category.all(:conditions => {:id => 1}})
Category.where(:id => 1).first
# Is equivalent of Category.first(:conditions => {:id => 1}})
The objects are only retrieved when special methods like first, each etc are called. This is called lazy loading which is a great when you want to cache your views. Read more about why here.
Category.where(:id => 1).recipes
Returns an array. If you simply do Category.where(:id => 1).first.recipes it should work.
But if you are just doing a where against the id, use the find method
Category.find(1) will return a Category object.
So:
Category.find(1).recipes

Is there find_or_create_by_ that takes a hash in Rails?

Here's some of my production code (I had to force line breaks):
task = Task.find_or_create_by_username_and_timestamp_and_des \
cription_and_driver_spec_and_driver_spec_origin(username,tim \
estamp,description,driver_spec,driver_spec_origin)
Yes, I'm trying to find or create a unique ActiveRecord::Base object. But in current form it's very ugly. Instead, I'd like to use something like this:
task = Task.SOME_METHOD :username => username, :timestamp => timestamp ...
I know about find_by_something key=>value, but it's not an option here. I need all values to be unique. Is there a method that'll do the same as find_or_create_by, but take a hash as an input? Or something else with similat semantics?
Rails 3.2 first introduced first_or_create to ActiveRecord. Not only does it have the requested functionality, but it also fits in the rest of the ActiveRecord relations:
Task.where(attributes).first_or_create
In Rails 3.0 and 3.1:
Task.where(attributes).first || Task.create(attributes)
In Rails 2.1 - 2.3:
Task.first(:conditions => attributes) || Task.create(attributes)
In the older versions, you could always write a method called find_or_create to encapsulate this if you'd like. Definitely done it myself in the past:
class Task
def self.find_or_create(attributes)
# add one of the implementations above
end
end
I also extend the #wuputah's method to take in an array of hashes, which is very useful when used inside db/seeds.rb
class ActiveRecord::Base
def self.find_or_create(attributes)
if attributes.is_a?(Array)
attributes.each do |attr|
self.find_or_create(attr)
end
else
self.first(:conditions => attributes) || self.create(attributes)
end
end
end
# Example
Country.find_or_create({:name => 'Aland Islands', :iso_code => 'AX'})
# take array of hashes
Country.find_or_create([
{:name => 'Aland Islands', :iso_code => 'AX'},
{:name => 'Albania', :iso_code => 'AL'},
{:name => 'Algeria', :iso_code => 'DZ'}
])

How to convert a ruby hash object to JSON?

How to convert a ruby hash object to JSON? So I am trying this example below & it doesn't work?
I was looking at the RubyDoc and obviously Hash object doesn't have a to_json method. But I am reading on blogs that Rails supports active_record.to_json and also supports hash#to_json. I can understand ActiveRecord is a Rails object, but Hash is not native to Rails, it's a pure Ruby object. So in Rails you can do a hash.to_json, but not in pure Ruby??
car = {:make => "bmw", :year => "2003"}
car.to_json
One of the numerous niceties of Ruby is the possibility to extend existing classes with your own methods. That's called "class reopening" or monkey-patching (the meaning of the latter can vary, though).
So, take a look here:
car = {:make => "bmw", :year => "2003"}
# => {:make=>"bmw", :year=>"2003"}
car.to_json
# NoMethodError: undefined method `to_json' for {:make=>"bmw", :year=>"2003"}:Hash
# from (irb):11
# from /usr/bin/irb:12:in `<main>'
require 'json'
# => true
car.to_json
# => "{"make":"bmw","year":"2003"}"
As you can see, requiring json has magically brought method to_json to our Hash.
require 'json/ext' # to use the C based extension instead of json/pure
puts {hash: 123}.to_json
You can also use JSON.generate:
require 'json'
JSON.generate({ foo: "bar" })
=> "{\"foo\":\"bar\"}"
Or its alias, JSON.unparse:
require 'json'
JSON.unparse({ foo: "bar" })
=> "{\"foo\":\"bar\"}"
Add the following line on the top of your file
require 'json'
Then you can use:
car = {:make => "bmw", :year => "2003"}
car.to_json
Alternatively, you can use:
JSON.generate({:make => "bmw", :year => "2003"})

Resources