Sonntag, 30. November 2014

Sort your Ruby objects!

Sorting non persisted data collections in Ruby is done by Enumerable#sort or its more flexible companion Enumerable#sort_by. And even persisted data has to be sorted by them in rare cases. A simple example:
%w(Ruby Python Javascript).sort
=> ["Javascript", "Python", "Ruby"]
or
%w(Ruby Python Javascript).sort_by(&:reverse)
=> ["Python", "Javascript", "Ruby"]
But what about a simple class like:
class Food
  attr_accessor :name, :kcal # acronym for kilocalories
  def initialize name, kcal
    @name = name
    @kcal = kcal
  end
end
Assuming the foods collection:
foods = [
  Food.new('Pear', 57), 
  Food.new('Apple', 52), 
  Food.new('Avocado', 164)
]
could be sorted with Enumerable#sort_by:
foods.sort_by(&:name)
=> [
  #<Food @name="Apple", @kcal=52>, 
  #<Food @name="Avocado", @kcal=164>, 
  #<Food @name="Pear", @kcal=57>
]
but can not with not be sorted with Enumerable#sort:
foods.sort
=> ArgumentError: comparison of Food with Food failed
because Ruby expects a sorting method in the Food class definition. Passing the sorting block works:
foods.sort { |a, b| a.name <=> b.name }
=> [
  #<Food @name="Apple", @kcal=52>, 
  #<Food @name="Avocado", @kcal=164>, 
  #<Food @name="Pear", @kcal=57>
]
in which String#<=> satisfies the sorting expectation.
The sorting logic is worth a move into the class definition itself:
class Food
  attr_accessor :name, :kilocalories
  def initialize name, kilocalories # acronym for kilocalories
    @name = name
    @kilocalories = kilocalories
  end

  def <=> other
    name <=> other.name
  end
end
which expects an object responding to a name method, which obviously should return a String. It works like:
foods.sort
=> [
  #<Food @name="Apple", @kcal=52>, 
  #<Food @name="Avocado", @kcal=164>, 
  #<Food @name="Pear", @kcal=57>
]
A more complicated sorting requirements can be defined like:
class Food
  attr_accessor :name, :kcal
  def initialize name, kcal
    @name = name
    @kcal = kcal
  end

  def <=> other
    return -(kcal <=> other.kcal) if name.first.eql? other.name.first
    name <=> other.name
  end
end
which compares the foods by their name, but if the first letter is equal, then sort compares them by their kilocalories but in descending order. Descending is achieved by inverting comparison value, which can be negative, zero or positive.
The rule (whereas 'a' is self and 'b' is the other):
  1. if a < b then return -1
  2. if a = b then return 0
  3. if a > b then return 1
  4. if a and b are not comparable then return nil
The result:
[
  Food.new('Pear', 57), 
  Food.new('Apple', 52), 
  Food.new('Avocado', 164)
].sort
=> [
  #<Food @name="Avocado", @kcal=164>, 
  #<Food @name="Apple", @kcal=52>, 
  #<Food @name="Pear", @kcal=57>
]
Please note that Avocado is before Apple, just because of its higher kilocalories.

Supported by Ruby 2.1.1

Sonntag, 23. November 2014

Modularize ActiveRecord has_many proxy extensions!

There are several reasons for extending ActiveRecord::Associations::ClassMethods#has_many associations (please read Extend has_many association proxy!).
One step further in modularization terms is to put those extension methods into a separate module, because it is a matter of:
  1. reuseability (logic can be reused)
  2. concern (logic is consolidated in a concerning module)
Please note, that moving the methods into a concerning module also means moving it out of the first view. But when it comes to reuseability, there is no alternative.
Imagine the example:
class Recipe < ActiveRecord::Base
  def to_s
    "#{name} (#{time})"
  end
end

class Cook < ActiveRecord::Base
  has_many :recipes do
    def to_s
      map(&:to_s).join(', ')
    end

    def [] term
      where("#{proxy_association.klass.table_name}.name LIKE ?", "%#{term}%")
    end
  end
end
and a use case like:
Cook.first.recipes['cookie'].to_s
=> "Chocolate Chunk Cookies (30), Amish Cookies (60)"
can be refactored by moving both Proxy extension methods into a module:
module HasManyProxyExtension
  module NameSearchableAndHumanizable
     def [] term
      where("#{proxy_association.klass.table_name}.name LIKE ?", "%#{term}%")
    end

    def to_s
      map(&:to_s).join(', ')
    end
  end
end

class Cook < ActiveRecord::Base
  has_many :recipes, 
    extend: HasManyProxyExtension::NameSearchableAndHumanizable
end
and the use case again:
Cook.first.recipes['cookie'].to_s
=> "Chocolate Chunk Cookies (30), Amish Cookies (60)"
is still working with the advantage being able to reuse the module in another class:
class Recipe < ActiveRecord::Base
  has_many :ingredients, 
    extend: HasManyProxyExtension::NameSearchableAndHumanizable
  def to_s
    "#{name} (#{time})"
  end
end

class Ingredient < ActiveRecord::Base
  def to_s
    return name if legal?
    "#{name} (illegal)"
  end
end
and its use case:
Recipe.first.ingredients['as'].to_s
=> "Basil, Sassafras (illegal)"
Imagine, how the model classes would look like, if the logic could not be moved into a module.
Some times it is good choice to extend an association proxy with multiple modules:
module HasManyProxyExtension
  module NameSearchable
     def [] term
      where("#{proxy_association.klass.table_name}.name LIKE ?", "%#{term}%")
    end
  end

  module Humanizable
    def to_s
      map(&:to_s).join(', ')
    end
  end
end

class Cook < ActiveRecord::Base
  has_many :recipes, 
    extend: [HasManyProxyExtension::NameSearchable, HasManyProxyExtension::Humanizable]
end
Of course it still works the same. But It allows deeper modularization and a more versatile combination of modules.
Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17

Sonntag, 16. November 2014

Extend has_many association proxy!

ActiveRecord::Associations::ClassMethods#has_many and its companion ActiveRecord::Associations::ClassMethods#has_and_belongs_to_many in Ruby on Rails are both extendible associations. It is a convenient approach to extend Proxy objects for various reasons:
  1. encapsulates logic which applies to the association
  2. provides more expressive access to associated model objects
  3. is combinable with collection proxy object methods
Assuming the original classes:
class Cook < ActiveRecord::Base
  has_many :recipes
end

class Recipe < ActiveRecord::Base
  def self.named_like name
    where("#{table_name}.name LIKE ?", "%#{name}%")
  end

  def name_with_time
    "#{name} (#{time})"
  end
end
whereas a cook has many recipes.

Encapsulate logic

Getting all recipes as a comma separated String is coded quickly:
Cook.first.recipes.map(&:name_with_time).join(', ')
=> "Chocolate Chunk Cookies (30), Oatmeal Shortbread (45)"
but can be refactored from the reuseability perspective by moving the logic into the Cook association to Recipe:
class Cook < ActiveRecord::Base
  has_many :recipes do
    def to_s
      map(&:name_with_time).join(', ')
    end
  end
end
Now the logic, how to get recipes String does not need to be reproduced any more:
Cook.first.recipes.to_s
=> "Chocolate Chunk Cookies (30), Oatmeal Shortbread (45)"

Increase Expressiveness

Another scenario is to search all the cooks recipes by a term:
Cook.first.recipes.named_like('cookie')
=> [#<Recipe id: 1, name: "Chocolate Chunk Cookies", time: 30>]
is pretty expressive, but can be improved by extending the association once again:
class Cook < ActiveRecord::Base
  has_many :recipes do
    def [] term
      named_like term
    end
  end
end
and sending the message:
Cook.first.recipes['cookie']
=> [#<Recipe id: 1, name: "Chocolate Chunk Cookies", time: 30>]

Combine extensions

Proxy objects extensions also can be combined easily, which makes logic flexible and reusable:
class Cook < ActiveRecord::Base
  has_many :recipes do
    def to_s
      map(&:name_with_time).join(', ')
    end

    def [] term
      named_like term
    end
  end
end
and combining both:
Cook.first.recipes['cookie'].to_s
=> "Chocolate Chunk Cookies (30)"
whereas the chain order matters.
Of course the association proxy extensions also can be combined with other CollectionAssociation methods like:
Cook.first.recipes['cookie'].count
=> 1
which generates a different SQL statements projection part as expected. Accessing the Proxy objects inside the extension is also easy as:
class Cook < ActiveRecord::Base
  has_many :recipes do
    def [] term
      named_like term
    end

    def shallow_copy
      proxy_association.proxy.map(&:dup)
    end
  end
end
The message receiver Cook#shallow_copy simply clones the associated objects:
Cook.first.recipes['cookie'].shallow_copy
=> [#<Recipe name: "Chocolate Chunk Cookies", time: 30>]
Please note the cloned recipe has no ID, since it is a new record.
The collection proxy object provides some more helpful accessors like ActiveRecord::Associations::HasManyAssociation#owner and ActiveRecord::Associations::HasManyAssociation#reflection
Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17

Sonntag, 9. November 2014

Respond with JSON using Rabl gem!

JSON format is the de facto standard especially compared to XML. Nowadays controllers responding with JSON is no novelty. Often times a controller responding with JSON looks as simple as:
class IngredientsController < ApplicationController
  respond_to :json

  def index
    respond_with Ingredients.all
  end
end
and requesting it:
curl http://localhost:3000/ingredients.json
=> [
  {"id":1,"created_at":"2014-08-09T19:08:44Z","name":"Basil","illegal":false},
  {"id":2,"created_at":"2014-08-09T19:08:56Z","name":"Ginger","illegal":false},
  {"id":3,"created_at":"2014-11-09T19:13:38Z","name":"Sassafras","illegal":true}
]
which JSONifies the found Ingredient objects and responds them in a very concise manner. It works fine for simple use cases.
But oftentimes use cases get more complex and then a primitive JSON mapping does not satisfy the requirements adequate. For example, when:
  1. the collection response is tremendous and responding only the relevant object attributes makes a huge payload difference
  2. the client receiver Javascript API expects a dedicated JSON object signature (e.g autocompleters often expect 'id' and 'name')
  3. the responding JSON objects require access to custom ActiveRecord methods
then hacking around ActiveRecord#to_json or even ActiveRecord#as_json has its limitation and is rarely convenient. Besides it is a view thing (just another representation of the objects) and requires templating.
Then the Rabl gem comes into play. It is a templating module for JSON objects, which provides the required flexibility.
It is easy included into the Ruby on Rails project (Gemfile):
gem 'rabl'
For example a new requirement wants an autocompleter to list the searched ingredients by their name with acronym in brackets. A solution could be refactoring the controller:
class IngredientsController < ApplicationController
  respond_to :json

  def index
    @ingredients = Ingredients.search params[:search]
    respond_with @ingredients
  end
end
and extending the model:
class Ingredient < ActiveRecord::Base
  def self.search term
    where("#{table_name}.name LIKE :term OR #{table_name}.description LIKE :term", { term: "%#{term}%" })
  end
end
Finally the view (ingredients/index.json.rabl):
collection @ingredients, object_root: false
attributes :id
node(:name) { |ingredient|
  text = ingredient.name 
  text << " (illegal)" if ingredient.illegal?
  text
}
needs some explanation. At first the instance variable @ingredients contains the ingredients collection. The default root node can be removed by setting the option object_root: false.
The required attributes are defined by assigning them to attributes and custom nodes can be defined by passing a block to node.
There are more options and the Rabl API is way more flexible when it comes to child nodes, gluing attributes, partials, inheritance, deep nesting, caching etc.
Requesting the ingredients:
curl http://localhost:3000/ingredients.json?search=as
=> [
  {"id":1,"name":"Basil"},
  {"id":3,"name":"Sassafras (illegal)"}
]
Responding to a request for a specific object could look like:
class IngredientsController < ApplicationController
  respond_to :json

  def show
    @ingredient = Ingredients.find params[:id]
    respond_with @ingredient
  end
end
and its view (ingredients/show.json.rabl):
object @ingredient
attributes id: :id, to_s: :title
child :foods, object_root: 'meal' do
  attribute :name
end
Requesting it:
curl http://localhost:3000/ingredients/1.json
=> {"ingredient":{"title":"Basil","foods":[
    {"meal":{"name":"Garlic Basil Shrimp"}},
    {"meal":{"name":"Basil and Lime Sorbet"}}
  ]
}}

Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17

Sonntag, 2. November 2014

Parse XML in Ruby with Nokogiri gem!

The Ruby gem 'Nokogiri' is the quasi standard for parsing XML and HTML in the Ruby community.
The official Nokogiri web page explains how to install it on various platforms.
An XML file like the catalog.xml:



  
  
  
  
  

can be loaded and parsed like:
file_path = '/usr/share/xml/docbook/schema/dtd/catalog.xml'
xml = Nokogiri::XML(File.read file_path)
=> #<Nokogiri::XML::Document:0xfda908 name="document"...>
There are also several XML/ HTML parse options available, for example eliminating all blank nodes:
xml = Nokogiri::XML File.read(file_path), &:noblanks
=> #<Nokogiri::XML::Document:0xfda908 name="document">
The returned Nokogiri object provides a lot ways of accessing the XML nodes.

Basic Ruby API

The object can be grasped with pure Ruby like:
xml.children.children.select { |node| 
  node.name.eql? 'system' 
}.map{ |node| 
  node.attributes["systemId"].value 
}
=> ["http://oasis-open.org/docbook/xml/4.1/docbookx.dtd", 
"http://docbook.org/xml/4.1/docbookx.dtd"]
which often times is cumbersome and verbose. That is why there are other APIs.

Basic XPath API

One of them is XPath:
xml.xpath("//xmlns:catalog/xmlns:system/@systemId")
=> [#<Nokogiri::XML::Attr:0xf4ea20 name="systemId" value="http://oasis-open.org/docbook/xml/4.1/docbookx.dtd">, 
#<Nokogiri::XML::Attr:0xf4e110 name="systemId" value="http://docbook.org/xml/4.1/docbookx.dtd">]
which has to be combined with Ruby to get the values too:
xml.xpath("//xmlns:catalog/xmlns:system/@systemId").
  map(&:value)
=> ["http://oasis-open.org/docbook/xml/4.1/docbookx.dtd", 
"http://docbook.org/xml/4.1/docbookx.dtd"]
Dealing with namespaces in many use cases is annoying and can be removed:
xml.remove_namespaces!.
  xpath("//catalog/system/@systemId").map(&:value)
=> ["http://oasis-open.org/docbook/xml/4.1/docbookx.dtd", 
"http://docbook.org/xml/4.1/docbookx.dtd"]
The entire XPath reference is documented at w3school.

Basic CSS API

The third kind of accessing the XML node tree is CSS. The full CSS selector reference also is documented at w3school, but receiving XML nodes works like:
xml.css("catalog > system")
=> [#<Nokogiri::XML::Element:0xf4ea84 name="system" attributes=[
  #<Nokogiri::XML::Attr:0xf4ea20 name="systemId" value="http://www.oasis-open.org/docbook/xml/4.1/docbookx.dtd">
]>, 
#<Nokogiri::XML::Element:0xf4e174 name="system" attributes=[
  #<Nokogiri::XML::Attr:0xf4e110 name="systemId" value="http://docbook.org/xml/4.1/docbookx.dtd">
]
With the CSS API only the nodes can be grasped. When dealing with their attributes is required Ruby API comes into play:
xml.css("catalog > system").map{ |node| 
  node.attributes['systemId'].value 
}
=> ["http://oasis-open.org/docbook/xml/4.1/docbookx.dtd", 
"http://docbook.org/xml/4.1/docbookx.dtd"]


Nokogiri can handle HTML documents as well.
Grasping the home page document from Nokogiri with curl (read Tell shell scripting apart in Ruby!) and finding the text of the first tutorials link is easy as:
html = Nokogiri::HTML(%x[curl http://www.nokogiri.org/])
html.css('.page ol li a').first.text

Further articles of interest:

Supported by Ruby 2.1.1