Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Morph mixin allows you to emerge Ruby class definitions from data via calling assignment methods.
Ruby
branch: master

README.rdoc

Morph mixin allows you to emerge Ruby class definitions from data via calling assignment methods.

Travis CI
build status

Installing Morph

gem install morph

To use Morph:

require 'morph'

Morph creating classes from_csv

Here's example code showing Morph playing with CSV (comma-separated values):

csv = %Q[name,party\nTed Roe,red\nAli Davidson,blue\nSue Smith,green]

people = Morph.from_csv(csv, 'person')

# => [#<Morph::Person @name="Ted Roe", @party="red">,
      #<Morph::Person @name="Ali Davidson", @party="blue">,
      #<Morph::Person @name="Sue Smith", @party="green">]

people.last.party

# => "green"

Morph creating classes from_tsv

Here's example code showing Morph playing with TSV (tab-separated values):

tsv = %Q[name\tparty\nTed Roe\tred\nAli Davidson\tblue\nSue Smith\tgreen]

people = Morph.from_tsv(tsv, 'person')

# => [#<Morph::Person @name="Ted Roe", @party="red">,
      #<Morph::Person @name="Ali Davidson", @party="blue">,
      #<Morph::Person @name="Sue Smith", @party="green">]

people.last.party

# => "green"

Morph creating classes from_xml

Here's example code showing Morph playing with XML:

xml = %Q[<?xml version="1.0" encoding="UTF-8"?>
<councils type="array">
  <council code='1'>
    <name>Aberdeen City Council</name>
  </council>
  <council code='2'>
    <name>Allerdale Borough Council</name>
  </council>
</councils>]

councils = Morph.from_xml(xml)

# => [#<Morph::Council @code="1", @name="Aberdeen City Council">,
      #<Morph::Council @code="2", @name="Allerdale Borough Council">]

councils.first.name

# => "Aberdeen City Council"

Morph playing with Nokogiri

Here's example code showing Morph playing with Nokogiri in Ruby 1.9.2:

require 'morph'; require 'nokogiri'; require 'open-uri'

class Hubbit
  include Morph  # allows class to morph

  def initialize name
    doc = Nokogiri::HTML open("https://github.com/#{name}")

    profile_fields = doc.search('.vcard dt')

    profile_fields.each do |node|
      label = node.inner_text
      value = node.next_element.inner_text.strip

      morph(label, value)  # morph magic adds accessor methods!
    end
  end

  def member_since_date
    Date.parse member_since
  end
end

def Hubbit name; Hubbit.new name; end

The model emerges from the data. Let's start by looking up 'why':

why = Hubbit 'why'

What new methods do we have?

Hubbit.morph_methods.map {|m| m.to_s}

#=> ["location", "location=", "member_since", "member_since=", "name", "name="]

Ah-ha, so we have a name attribute now:

why.name # => "Squatting until _why gets home."

We wrote a member_since_date method in our class, let's call that now:

why.member_since_date # => Wed, 19 Aug 2009

Let's add some of why's projects:

why.projects = %w[shoes hacketyhack camping hoodwinkd hpricot
                 markaby mousehole parkplace poignant sandbox]

That why's a productive fellow! Note new accessor methods have been added:

Hubbit.morph_methods.map {|m| m.to_s}

#=> ["location", "location=", "member_since", "member_since=", "name", "name=",
#    "projects", "projects="]

Let's do some more morphing:

dhh = Hubbit 'dhh'

Do we have more methods now?

Hubbit.morph_methods.map {|m| m.to_s}

#=> ["company", "company=", "email", "email=", "location", "location=",
#    "member_since", "member_since=", "name", "name=", "projects", "projects=",
#    "website_blog", "website_blog="]

So, a new company method has appeared:

dhh.company #=> "37signals"

Morph making sample Active Record line via script_generate

Time to generate an Active Record model? Get a sample script line like this:

Morph.script_generate(Hubbit)

#=> "rails destroy model Hubbit;
#    rails generate model Hubbit company:string email:string location:string
#          member_since:string name:string projects:string website_blog:string"

or specify the generator:

Morph.script_generate(Hubbit, :generator => 'rspec_model')

#=> "rails destroy rspec_model Hubbit;
#    rails generate rspec_model Hubbit company:string email:string
#          location:string member_since:string name:string projects:string
#          website_blog:string"

You'll have to edit this as it currently sets all data types to be string, and doesn't understand associations.

Morph setting hash of attributes via morph

class Order; include Morph; end
order = Order.new

How about adding a hash of attribute values?

order.morph :drink => 'tea', :spoons_of_sugar => 2, :milk => 'prefer soya thanks'

Looks like we got 'em:

order.drink  # => "tea"
order.spoons_of_sugar  # => 2
order.milk  # => "prefer soya thanks"

Morph obtaining hash of attributes via morph_attributes

Create an item:

class Item; include Morph; end
item = Item.new
item.morph :name => 'spinach', :cost => 0.50

Now an order:

class Order; include Morph; end
order = Order.new
order.no = 123
order.items = [item]

Want to retrieve all that as a nested hash of values? No problem:

order.morph_attributes

# => {:items=>[{:name=>"spinach", :cost=>0.5}], :no=>123}

Last bits

See examples/ directory for some example code. See LICENSE for the terms of this software.

.                                                     ,
.                                                 ?7+~::+II~
.                                                ?7:     ,:+7
.                             777IIII777?        7:         :?7
.                          =I=           I:      7?          ,+7
.                         I?         ,,   77      7:           :I
.                        =  ?7777   77  7   7      7+,          :7
.                       7   777777 ~77+=77  I+      I?          ,7
.                      :7  77  ~77  I   I7   7       ?:          ?
.                      I   77   7,  7    7   :I       I          ?
.                      7   ?77=7~    77777    7      ~+          ,+
.                      7~                     7  :I7?~            7
.                      =?                     7 ?I    ~I77=       I=
.                       7    ?          :,   7  I7777,     7       7
.                        ?    777?~~7777+    7              7~      7
.                        ?7    ,777777=,   ,7                7      ,7
.                          7=      ,      =7                 7:      7
.                            +7         :7                    7      ,I
.                             :7        ?~                   7?       7
.                              7         7              ~II7~,        7
.                              7         7  ,  =7777777?+,,,         I=
.                            :7,          ~==,                       7
.                       II~,,                                     77~
.                    ,I?                                      +777
.                   7+,                                 ~7777:
.                 ==                               :77
.               :7:                              ,7I
.             7I                                 7
.            I          ,7,                      7
.          =7          77=7                      7
.        ,7          7I   7                      7
.        I,        I7     7                      7
.       ?,       ,7       7,                     7
.       7       7~        7,                     7
.       7      ,7I        7                      7
.       =+       =7       7                      ~=
.        =7        7,     7                       7
.         ,7,       ~7IIII7+,                     7
.           +:              II                    I
.            ?7              I?                   +~
.              II,           +I                    7
.                ~7          ,I                    7
.                  7=        ~7                    7
.                   ?7,     ~7+                    ?~
.                     ~7777I=                      ,7
.                         7:                        7
.                         I                         7
.                         I          ,:77I          7
.                         I          :7             I
.                         I                         =~
.                         7               ,         ,7
.                         +,         7    :         ,7
.                          +         7    +          7
.                          +         7    +         ,7
.                          7         I    ?         ,7
.                          7         +:   7         ,7
.                          7         =+   7         ,7
.                          7         :I   I         ,7
.                          7         :I   7          7
.                          7         :I   I          7
.                          I,        ,7   I:         7
.                          =+        ,7    ?         7
.                          :?,       ,7    7,        7
.                          I:        ,7    7,        ?
.                         :7         ,7    7,        ,
.                        +I,         :     ?         ,=
.                       +=           ~     =~         7
.                    :II,,           =      I         ?
.                =I=                 ?      7,        :7
.              II~                   I      7,         ,II
.            7~                      ~7     7            ,=7
.            =                       =7     I,             ::
.            77II?==?II777777777777777      7~              7
.                                            77+,,          7:
.                                               777777+:,~777
.
Something went wrong with that request. Please try again.