Rspec Candies and Cookies

it "include_hash tests nested hashes" do
 { foo: 'a', bar: {nested_hash: {fi: 'c', baz:'b'} }}.should include_nested_hash(nested_hash: {fi: 'c', baz:'b'})
 end

Combining Mandraka’s  Rspec Candy with this little find from CookiesHQ, led me to a nested hash matcher

Very useful for testing API responses.


def find_first_value_for(hsh, key)
 return hsh[key] if hsh[key]
 hsh.values.each do |hash_value|
   values = [hash_value] unless hash_value.is_a? Array
   values.each{ |value|   return find_first_value_for(value, key) if value.is_a? Hash }
 end
end

RSpec::Matchers.define :include_nested_hash do |expected|
 match do |actual|
   if !actual.nil? and expected.is_a? Hash
     if expected.keys.count > 1
       keys.map do |key|
         value = find_first_value_for actual(actual, key)
         !value.nil? ? {key.to_sym => value} : {}
       end
     elsif expected.keys.count == 1
      {expected.keys.first => find_first_value_for(actual, expected.keys.first)}
     else
      {}
     end
     !actual.nil? && result == expected
   else
     false
   end
 end
end

Just include it in your spec_helper.rb or custom matcher module then test with something like this:

context "when testing with rspec candy" do
 it "include_hash is available" do
 { foo: 'a', bar: 'b' }.should include_hash(foo: 'a')
 end
 it "include_hash tests nested hashes" do
 { foo: 'a', bar: {nested_hash: {fi: 'c', baz:'b'} }}.should include_nested_hash(nested_hash: {fi: 'c', baz:'b'})
 end
end

Ruby CSV write to a file – Gotcha!

Unlike the beautifully concise and familiar 1 liner of code to dump a document to a text file:

File.open(local_filename, 'w') {|f| f.write(doc) }

The ruby csv library requires quite a bit more typing, and the documentation for it is easy to misunderstand.

One of my primary needs, is often data wrangling.  Changing the contents of a csv file for use in another framework, whether it’s reverse coordinates, stripping unwanted columns, or adding needed columns to the data, and I always trip up on how to dump the changed CSV after manipulating it.

As a reminder to myself, and maybe a hint to others, I’ve include the proper way to dump out your arr_of_arrs, once you’ve manipulated it as you will,

CSV.open(newfilename, "w+",
             :write_headers => true,
             :headers => arr_of_arrs.headers) do |csv|
    arr_of_arrs.each{|row| csv << row}
 end

The important bit is remembering to do an each loop of your arr_of_arrays object. This for instance will not produce the desired results:

CSV.open(newfilename, "w+",
                :write_headers => true,
                :headers => arr_of_arrs.headers) do |csv|
   csv << arr_of_arrs
end

XML, Violence, Nokogiri and Xpath

I love Xpath. It makes XML easy to use and easy to query. Gone are the days of parsing things with a SaxParser unless you’re really hard up for control of you text. Also, I love the Ruby Nokogiri Gem. XML is like violence – if it doesn’t solve you…

I love Xpath.

 It makes XML easy to use and easy to query.  Gone are the days of parsing things with a SaxParser unless you’re really hard up for control of you text.

 

Also, I love the Ruby Nokogiri Gem.  

XML is like violence – if it doesn’t solve your problems, you are not using enough of it.

– Nokogiri docs

But I do have to say that there is a lack of good examples and documentation for anything particularly advanced.  I found a working solution to my issue, but thought I’d paste here what I wanted to do versus what I ended up doing.

Given the following XML, 

 

https://gist.github.com/1577136

 

I’d like to grab two elements that include “Cover” in the tag, and then operate on each of them.

Nokogiri’s use of Xpath easily allows the first query expression like so: price_xml = doc_xml.xpath('Container/Set/*[contains(name(), "Cover")]')

I’ve selected all the elements (using *) in Set, and then used an Xpath Expression function:

contains, in order to specify that Adult must be in the name.  This returns two Nokogiri XML Nodes in Nodeset.

 

What I wanted to do was then select one of these elements based on a pattern in the tagname use my favorite tool, Xpath.

But I just couldn’t get Nokogiri to give it to me, and several solutions ending up selecting way more than the 1 element I wanted. (Because the nodes in the Nodeset still contain relationships with their parents)

https://gist.github.com/1579343

 

I’m cross posting this on StackOverflow as a question, just in case any Nokogiri Xpath enthusiasts want to recommend a solution that doesn’t resort to find()

 

 

Rails Rake tips

This is the Hay Magnum-Hay CCM-8 Max Wheel Rake. Now you might think it should have a name like: Hay Magnumator 8000 but it doesn’t. It doesn’t take long playing with Rake before you starting thinking of it like the Hay Magnumator 8000. By Rake, I…

longviewcart
rake tasks

Here’s a tip:
If some of these tasks are actually “private” tasks that only get called by other Rake tasks,
leave off the “desc” line in the task definition.  And Voila, your rake -T list will get a lot cleaner.

 

Thanks to Erik Debill for his nice post on Rake and some Advanced Tips for using it.