Class: Exchanger::Element

Inherits:
Object
  • Object
show all
Includes:
Attributes, Dirty, Persistence
Defined in:
lib/exchanger/element.rb

Overview

General purpose element.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Persistence

#destroy, #new_record?, #persisted?, #reload, #save

Methods included from Dirty

#attribute_change, #attribute_changed?, #attribute_was, #changed, #changed?, #changes, included, #move_changes, #previous_changes, #reset_attribute!, #reset_modifications, #setup_modifications

Methods included from Attributes

#attributes, #attributes=, #change_key, #id, #identifier, #method_missing, #read_attribute, #respond_to?, #write_attribute

Constructor Details

#initialize(attributes = {}) ⇒ Element


47
48
49
50
51
# File 'lib/exchanger/element.rb', line 47

def initialize(attributes = {})
  @attributes = {}
  self.attributes = attributes
  setup_modifications
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Exchanger::Attributes

Instance Attribute Details

#tag_nameObject


43
44
45
# File 'lib/exchanger/element.rb', line 43

def tag_name
  @tag_name || self.class.name.demodulize
end

Class Method Details

.create_element_accessors(name) ⇒ Object

Create child element accessors.


35
36
37
38
39
# File 'lib/exchanger/element.rb', line 35

def self.create_element_accessors(name)
  define_method(name) { read_attribute(name) }
  define_method("#{name}=") { |value| write_attribute(name, value) }
  define_method("#{name}?") { read_attribute(name).present? }
end

.element(name, options = {}) ⇒ Object

Define a new child element.


18
19
20
21
22
23
24
# File 'lib/exchanger/element.rb', line 18

def self.element(name, options = {})
  options[:field_uri_namespace] ||= self.field_uri_namespace
  self.elements = self.elements.dup
  self.elements[name] = Field.new(name, options)
  create_element_accessors(name)
  add_dirty_methods(name)
end

.key(name, options = {}) ⇒ Object

Defina a new element attribute.


27
28
29
30
31
32
# File 'lib/exchanger/element.rb', line 27

def self.key(name, options = {})
  self.keys = self.keys.dup
  self.keys << name
  create_element_accessors(name)
  add_dirty_methods(name)
end

.new_from_xml(xml) ⇒ Object


74
75
76
# File 'lib/exchanger/element.rb', line 74

def self.new_from_xml(xml)
  new.assign_attributes_from_xml(xml)
end

Instance Method Details

#==(other) ⇒ Object

Performs equality checking on attributes


54
55
56
57
# File 'lib/exchanger/element.rb', line 54

def ==(other)
  self.class == other.class &&
  self.attributes == other.attributes
end

#assign_attributes_from_xml(xml) ⇒ Object


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/exchanger/element.rb', line 78

def assign_attributes_from_xml(xml)
  # Keys
  xml.attributes.values.each do |attr|
    name = attr.name.underscore.to_sym
    value = attr.value
    write_attribute(name, value)
  end
  # Fields
  xml.children.each do |node|
    name = node.name.underscore.to_sym
    field = elements[name] || Field.new(name)
    value = field.value_from_xml(node)
    write_attribute(name, value)
  end
  send(:reset_modifications)
  self
end

#errorsObject


59
60
61
# File 'lib/exchanger/element.rb', line 59

def errors
  @errors ||= []
end

#inspectObject


63
64
65
66
67
68
69
70
71
72
# File 'lib/exchanger/element.rb', line 63

def inspect
  keys = self.class.elements.keys | attributes.keys.map(&:to_sym)
  keys -= [:id, :change_key, self.class.identifier_name]
  attrs = keys.map { |name, field| "#{name}: #{attributes[name.to_s].inspect}" }
  str = "#<#{self.class.name}"
  str << " id: #{id.inspect}, change_key: #{change_key.inspect}" if self.class.identifier_name || self.class.keys.include?(:id)
  str << " #{attrs * ', '}" unless attrs.empty?
  str << ">"
  str
end

#to_xml(options = {}) ⇒ Object

Builds XML from elements and attributes


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/exchanger/element.rb', line 97

def to_xml(options = {})
  doc = Nokogiri::XML::Document.new
  root = doc.create_element(tag_name)
  self.class.keys.each do |name|
    value = read_attribute(name)
    next if value.blank?
    root[name.to_s.camelize] = value
  end
  self.class.elements.each do |name, field|
    next if options[:only] && !options[:only].include?(name)
    next if field.options[:readonly]
    value = read_attribute(name)
    next if field.type.is_a?(Array) && value.blank?
    next if new_record? && field.type == Identifier
    next if new_record? && value.blank?
    if name == :text
      root << value.to_s
    else
      root << field.to_xml(value)
    end
  end
  root
end

#to_xml_changeObject

Builds XML Item/Folder change for update operations.


122
123
124
125
126
127
128
# File 'lib/exchanger/element.rb', line 122

def to_xml_change
  doc = Nokogiri::XML::Document.new
  root = doc.create_element("#{identifier.tag_name.gsub(/Id$/, '')}Change")
  root << identifier.to_xml
  root << to_xml_updates
  root
end

#to_xml_updatesObject

Builds XML Set/Delete fields for update operations.


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/exchanger/element.rb', line 131

def to_xml_updates
  doc = Nokogiri::XML::Document.new
  root = doc.create_element("Updates")
  self.class.elements.each do |name, field|
    value = read_attribute(name)
    # Create or update existing fields
    if changes.include?(name.to_s)
      field.to_xml_updates(value) do |field_uri_xml, element_xml|
        # Exchange does not like updating to nil, so delete those.
        if element_xml.text.present?
          set_item_field = doc.create_element("SetItemField")
          set_item_field << field_uri_xml
          element_wrapper = doc.create_element(tag_name)
          element_wrapper << element_xml
          set_item_field << element_wrapper
          root << set_item_field
        else
          delete_item_field = doc.create_element("DeleteItemField")
          delete_item_field << field_uri_xml
          root << delete_item_field
        end
      end
    end
    # Delete removed phone numbers, etc
    if changes.include?(name.to_s) && value.is_a?(Array)
      old_values, new_values = changes[name.to_s]
      deleted_values = old_values - new_values
      field.to_xml_updates(deleted_values) do |field_uri_xml, _|
        delete_item_field = doc.create_element("DeleteItemField")
        delete_item_field << field_uri_xml
        root << delete_item_field
      end
    end
  end
  root
end