module Morph

Constants

VERSION

Public Class Methods

classes() click to toggle source
# File lib/morph.rb, line 130
def classes
  Chas.morph_classes
end
from_csv(csv, class_name, namespace=Morph) click to toggle source
# File lib/morph.rb, line 150
def from_csv csv, class_name, namespace=Morph
  objects = []
  CSV.parse(csv, { :headers => true }) do |row|
    object = object_from_name class_name, namespace
    row.each do |key, value|
      object.morph(key, value)
    end
    objects << object
  end
  objects
end
from_hash(hash, namespace=Morph) click to toggle source
# File lib/morph.rb, line 190
def from_hash hash, namespace=Morph
  if hash.keys.size == 1
    name = hash.keys.first

    case hash[name]
    when Hash
      object_from_hash hash[name], name, namespace
    when Array
      objects_from_array hash[name], name, namespace
    else
      raise 'hash root value must be a Hash or an Array'
    end
  else
    raise 'hash must have single key'
  end
end
from_json(json, root_key=nil, namespace=Morph) click to toggle source
# File lib/morph.rb, line 183
def from_json json, root_key=nil, namespace=Morph
  require 'json' unless defined? JSON
  hash = JSON.parse json
  hash = { root_key => hash } if root_key
  from_hash hash, namespace
end
from_tsv(tsv, class_name, namespace=Morph) click to toggle source
# File lib/morph.rb, line 162
def from_tsv tsv, class_name, namespace=Morph
  lines = tsv.split("\n")
  attributes = lines[0].split("\t")
  lines = lines[1..(lines.length-1)]
  objects = []
  lines.each do |line|
    values = line.split("\t")
    object = object_from_name class_name, namespace
    attributes.each_with_index do |attribute, index|
      object.morph(attribute, values[index])
    end
    objects << object
  end
  objects
end
from_xml(xml, namespace=Morph) click to toggle source
# File lib/morph.rb, line 178
def from_xml xml, namespace=Morph
  hash = Hash.from_xml xml
  from_hash hash, namespace
end
generate_migrations(object, options={}) click to toggle source
# File lib/morph.rb, line 142
def generate_migrations object, options={}
  options[:ignore] ||= []
  options[:belongs_to_id] ||= ''
  migrations = []
  name = object.class.name.demodulize.underscore
  add_migration name, object.morph_attributes, migrations, options
end
included(base) click to toggle source
# File lib/morph.rb, line 216
def included(base)
  base.extend ClassMethods
  base.send(:include, MethodMissing)
end
register_listener(listener) click to toggle source
# File lib/morph.rb, line 134
def register_listener listener
  Chas.register_listener listener
end
script_generate(morphed_class, options={}) { |name| ... } click to toggle source
# File lib/morph.rb, line 207
def script_generate morphed_class, options={}
  name = morphed_class.name.to_s.split('::').last
  name = yield name if block_given?
  generator = options[:generator] || 'model'
  line = ["rails destroy #{generator} #{name}; rails generate #{generator} #{name}"]
  morphed_class.morph_methods.select{|m| not(m =~ /=$/) }.each {|attribute| line << " #{attribute}:string"}
  line.join('')
end
unregister_listener(listener) click to toggle source
# File lib/morph.rb, line 138
def unregister_listener listener
  Chas.unregister_listener listener
end

Private Class Methods

add_migration(name, attributes, migrations, options) click to toggle source
# File lib/morph.rb, line 222
def add_migration name, attributes, migrations, options
  migration = "./script/generate model #{name}#{options[:belongs_to_id]}"
  options[:belongs_to_id] = ''
  migrations << migration
  attributes = [attributes] if attributes.is_a?(String)
  attributes.to_a.sort{|a,b| a[0].to_s <=> b[0].to_s}.each do |attribute, value|
    case value
      when String
        attribute_name = attribute.to_s
        unless options[:ignore].include?(attribute_name)
          type = attribute_name[/date$/] ? 'date' : 'string'
          attribute_def = "#{attribute}:#{type}"
          migration.sub!(migration, "#{migration} #{attribute_def}")
        end            when Array
        options[:belongs_to_id] = " #{name}_id:integer"
        migrations = add_migration(attribute, '', migrations, options)
      when Hash
        options[:belongs_to_id] = " #{name}_id:integer"
        migrations = add_migration(attribute, value, migrations, options)
      when nil
        # ignore
      else
        puts 'not supported ' + value.inspect
    end
  end
  migrations
end
add_to_object(object, attributes, namespace) click to toggle source
# File lib/morph.rb, line 280
def add_to_object object, attributes, namespace
  attributes.each do |name, value|
    name = name.to_s if name.is_a?(Symbol)
    attribute = name.gsub(':',' ').underscore

    value = value.to_time if defined?(XMLRPC::DateTime) && value.is_a?(XMLRPC::DateTime)

    case value
      when String, Date, Time, TrueClass, FalseClass, Integer, Float
        object.morph(attribute, value)
      when Array
        attribute = attribute.pluralize
        object.morph(attribute, objects_from_array(value, name, namespace))
      when Hash
        object.morph(attribute, object_from_hash(value, name, namespace))
      when NilClass
        object.morph(attribute, nil)
      else
        raise "cannot handle adding #{name} value of class: #{value.class.name}"
    end
  end
  object
end
class_constant(namespace, name) click to toggle source
# File lib/morph.rb, line 250
def class_constant namespace, name
  "#{namespace.name}::#{name}".constantize
end
object_from_hash(hash, name, namespace) click to toggle source
# File lib/morph.rb, line 266
def object_from_hash hash, name, namespace
  object = object_from_name(name, namespace)
  add_to_object(object, hash, namespace)
end
object_from_name(name, namespace) click to toggle source
# File lib/morph.rb, line 254
def object_from_name name, namespace
  name = Chas.convert_to_morph_class_name(name).camelize
  begin
    type = class_constant namespace, name
  rescue NameError => e
    namespace.const_set name, Class.new
    type = class_constant namespace, name
    type.send(:include, Morph)
  end
  type.new
end
objects_from_array(array, name, namespace) click to toggle source
# File lib/morph.rb, line 271
def objects_from_array array, name, namespace
  if array.size > 0 && array.collect(&:class).uniq == [Hash]
    name = name.to_s.singularize
    array.map! { |hash| object_from_hash(hash, name, namespace) }
  else
    array
  end
end

Public Instance Methods

morph(attributes_or_label, value=nil) click to toggle source

Set attribute value(s). Adds accessor methods to class if they are not already present.

Can be called with a string and a value, a symbol and a value, or with a hash of attribute to value pairs. For example.

require 'rubygems'; require 'morph'

class Order; include Morph; end

order = Order.new
order.morph :drink => 'tea', :sugars => 2, 'milk' => 'yes please'
order.morph 'Payment type:', 'will wash dishes'
order.morph :lemon, false

p order # -> #<Order:0x33c50c @lemon=false, @milk="yes please",
             @payment_type="will wash dishes", @sugars=2, @drink="tea">
# File lib/morph.rb, line 358
def morph attributes_or_label, value=nil
  if attributes_or_label.is_a? Hash
    attributes_or_label.each { |a, v| morph(a, v) }
  else
    attribute = Chas.convert_to_morph_method_name(attributes_or_label)
    send("#{attribute}=".to_sym, value)
  end
end
morph_attributes() click to toggle source
# File lib/morph.rb, line 367
def morph_attributes
  attributes = self.class.morph_attributes.inject({}) do |hash, attribute|
    unless attribute =~ /=\Z/
      symbol = attribute.to_sym
      value = send(symbol)

      value.each do |key, v|
        value[key] = v.morph_attributes if v.respond_to?(:morph_attributes)
      end if value.is_a? Hash

      value = value.collect {|v| v.respond_to?(:morph_attributes) ? v.morph_attributes : v } if value.is_a? Array
      value = value.morph_attributes if value.respond_to? :morph_attributes

      hash[symbol] = value
    end
    hash
  end
end