module Xeroizer module Record module XmlHelper def self.included(base) base.extend(ClassMethods) base.send :include, InstanceMethods end module ClassMethods # Build a record instance from the XML node. def build_from_node(node, parent) record = new(parent) node.elements.each do | element | field = self.fields[element.name.to_s.underscore.to_sym] if field value = case field[:type] when :guid then element.text when :string then element.text when :boolean then (element.text == 'true') when :integer then element.text.to_i when :decimal then BigDecimal.new(element.text) when :date then Date.parse(element.text) when :datetime then Time.parse(element.text) when :belongs_to model_name = field[:model_name] ? field[:model_name].to_sym : element.name.to_sym Xeroizer::Record.const_get(model_name).build_from_node(element, parent) when :has_many if element.element_children.size > 0 sub_field_name = field[:model_name] ? field[:model_name].to_sym : element.children.first.name.to_sym sub_parent = record.new_model_class(sub_field_name) element.children.inject([]) do | list, element | list << Xeroizer::Record.const_get(sub_field_name).build_from_node(element, sub_parent) end end end if field[:calculated] record.attributes[field[:internal_name]] = value else record.send("#{field[:internal_name]}=", value) end end end record end end module InstanceMethods public # Turn a record into its XML representation. def to_xml(b = Builder::XmlMarkup.new(:indent => 2)) optional_root_tag(parent.class.xml_root_name, b) do |b| b.tag!(parent.class.xml_node_name || parent.model_name) { attributes.each do | key, value | field = self.class.fields[key] value = self.send(key) if field[:calculated] xml_value_from_field(b, field, value) unless value.nil? end } end end protected # Add top-level root name if required. # E.g. Payments need specifying in the form: # # # ... # # def optional_root_tag(root_name, b, &block) if root_name b.tag!(root_name) { |b| yield(b) } else yield(b) end end # Format a attribute for use in the XML passed to Xero. def xml_value_from_field(b, field, value) case field[:type] when :guid then b.tag!(field[:api_name], value) when :string then b.tag!(field[:api_name], value) when :boolean then b.tag!(field[:api_name], value ? 'true' : 'false') when :integer then b.tag!(field[:api_name], value.to_i) when :decimal real_value = case value when BigDecimal then value.to_s when String then BigDecimal.new(value).to_s else value end b.tag!(field[:api_name], real_value) when :date real_value = case value when Date then value.strftime("%Y-%m-%d") when Time then value.utc.strftime("%Y-%m-%d") end b.tag!(field[:api_name], real_value) when :datetime then b.tag!(field[:api_name], value.utc.strftime("%Y-%m-%dT%H:%M:%S")) when :belongs_to value.to_xml(b) nil when :has_many if value.size > 0 sub_parent = value.first.parent b.tag!(sub_parent.class.xml_root_name || sub_parent.model_name.pluralize) { value.each { | record | record.to_xml(b) } } nil end end end end end end end