Morph allows you to emerge Ruby class definitions from data or by calling assignment methods. [![Build Status](https://travis-ci.org/robmckinnon/morph.svg?branch=master)](https://travis-ci.org/robmckinnon/morph) ## Installing Morph gem install morph To use Morph: require 'morph' Tested to work with Ruby 1.8 - 2.3, JRuby 9, and Rubinius 3. ## Morph creating classes `from_json` Here's an example showing Morph creating classes and objects from JSON: json = '{ "id": "3599110793", "type": "PushEvent", "actor": { "id": 3447, "login": "robmckinnon", "url": "https://api.github.com/users/robmckinnon" }, "repo": { "id": 5092, "name": "robmckinnon/morph", "url": "https://api.github.com/repos/robmckinnon/morph" } }' module Github; end type = :push_event namespace = Github event = Morph.from_json json, type, namespace # => , @repo=# > event.class # => Github::PushEvent event.class.morph_attributes # => [:id, :type, :actor, :repo] event.actor.class # => Github::Actor event.repo.class # => Github::Repo If namespace module not provided, new classes are created in Morph module. event = Morph.from_json json, type, namespace event.class # => Morph::PushEvent ## Morph creating classes `from_csv` Here's an example 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') # => [#, #, #] 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') # => [#, #, #] people.last.party # => "green" ## Morph creating classes `from_xml` Here's example code showing Morph playing with XML: xml = %Q[ Aberdeen City Council Allerdale Borough Council ] councils = Morph.from_xml(xml) # => [#, #] councils.first.name # => "Aberdeen City Council" ## Registering a listener to new class / methods via `register_listener` You can use `register_listener` to get callbacks when new methods on a class are created. For example given Morph used as a mixin: class Project; include Morph; end project = Project.new Register listener: listener = -> (klass, method) do puts "class: #{klass.to_s} --- method: #{method}" end Morph.register_listener listener Callback prints string as new methods are created via assignment calls: project.deadline = "11 11 2075" # class: Project --- method: deadline project.completed = true # class: Project --- method: completed To unregister a listener use `unregister_listener`: Morph.unregister_listener listener For an example of Morph's `register_listener` being used to [create a Github API](https://github.com/robmckinnon/hubbit/blob/master/lib/hubbit.rb) see the [Hubbit module](https://github.com/robmckinnon/hubbit/blob/master/lib/hubbit.rb). ## 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(Project) #=> "rails destroy model Project; # rails generate model Project completed:string deadline:string or specify the generator: Morph.script_generate(Hubbit, :generator => 'rspec_model') #=> "rails destroy rspec_model Project; # rails generate rspec_model Project completed:string deadline: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 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 .