require 'active_support/core_ext/hash/indifferent_access' module Traitorous # Use the HashWithIndifferentAccess from the activesupport gem. HASH = HashWithIndifferentAccess # Read the VERSION file in gem root to set version number. VERSION = File.read('VERSION') # The converter namespace is used to store Converter classes. # Each Converter may take initializer arguments, to allow specific classes or # values to be defined, and 2 methods: :do_import and :do_export. # do_import is meant to take the data, and use the initial data to combine to # build objects or other data structures. module Converter # The purpose of the converters are to facilitate the importation of simple # JSON or YAML data and import that data into an arbitrarily nested tree # of objects. And then to take those object and be able to export that data # in a simple form ready to save. # opts = {some: 'deep', data: ['structures']} # r = Mission.new(opts) # Mission.new(r.export) == r require 'traitorous/converter/identity' require 'traitorous/converter/default_value_static' require 'traitorous/converter/model' require 'traitorous/converter/uniform_array' require 'traitorous/converter/method_keyed_uniform_hash' # if no other converter is declared, use the identity converter which # merely pass through the data passed to it. DEFAULT_CONVERTER = Converter::Identity.new end require 'traitorous/equality' module ClassMethods attr_accessor :traits def trait(attr_name, converter = Traitorous::Converter::DEFAULT_CONVERTER) self.traits ||= HASH.new # sub hash used here, {} chosen for expansion possibility, but only # :converter is in use self.traits[attr_name] = {converter: converter} attr_accessor attr_name end end def self.included(base) base.extend(ClassMethods) end def initialize(opts = {}) raise "No traits have been defined for #{self.class}" unless self.class.traits @opts = HASH.new(opts) traits.each_pair do |new_trait, trait_opts| converter = trait_opts[:converter] val = converter.do_import(@opts.fetch(new_trait, nil)) self.send("#{new_trait}=".intern, val) end end def export exported = HASH.new traits.each_pair do |trait_name, trait_opts| attr_value = self.public_send(trait_name.intern) exported[trait_name] = trait_opts[:converter].do_export(attr_value) end exported end def traits self.class.traits end def inspect string = "#<#{self.class.name}:#{self.object_id} " fields = self.class.traits.keys.map{|field| "#{field}: #{self.send(field).inspect}"} string << fields.join(", ") << ">" end end