Class: Quickbooks::Element
Overview
Element is the parent of all QBXML elements (tags) that have children. This includes the outer wrapping QBXML tag, request and response tags such as CustomerAddRq and AccountModRs, data return tags such as AccountRet, and any data fields inside them that also have children. If you don’t know what those tags are, don’t worry, Model takes care of knowing how to handle them for you.
Element acts a lot like a Model but without the record-handling parts, and also acts a little like a Property.
Class Attribute Summary collapse
-
.associations ⇒ Object
readonly
Returns the value of attribute associations.
-
.options ⇒ Object
readonly
Returns the value of attribute options.
-
.properties ⇒ Object
readonly
Returns the value of attribute properties.
-
.xsd ⇒ Object
Returns the value of attribute xsd.
Class Method Summary collapse
-
.instantiate(obj_or_attrs = {}) ⇒ Object
This is usually used internally, but is safe to use if you need it for edge cases.
-
.order(obj, a, b) ⇒ Object
:nodoc:.
- .pluralize ⇒ Object
-
.unref ⇒ Object
Provides the class equivalent to the current class but without the Ref suffix.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Access an attribute value by its type – use a string, a symbol, or its Class constant.
-
#[]=(key, value) ⇒ Object
Set the value of an attribute.
-
#add_error(msg) ⇒ Object
:nodoc:.
-
#attributes ⇒ Object
An array of attribute values.
-
#attributes=(attrs) ⇒ Object
Sets whatever attributes you give it using []=.
-
#clean_attributes ⇒ Object
The attributes that have not been changed.
-
#delete(*keys) ⇒ Object
Remove an attribute from the object.
-
#dirty? ⇒ Boolean
Has anything in this object (or its descendents) changed?.
-
#dirty_attributes(include_required = false) ⇒ Object
The attributes that have been changed.
-
#errors ⇒ Object
Holds any errors from the last time valid? is run.
-
#initialize(attrs = {}) ⇒ Element
constructor
Create a new Element object, such as Account.new or ItemInventory.new.
-
#inspect ⇒ Object
:nodoc:.
-
#new_record? ⇒ Boolean
I don’t remember why this nil? had to be modified.
-
#save_associations ⇒ Object
:nodoc:.
-
#to_dirty_xml(include_required = false) ⇒ Object
Dumps only the changed data into QBXML.
-
#to_element ⇒ Object
Returns self.
-
#to_model ⇒ Object
Converts the element to a model if it is an appropriate class type.
- #to_update_element ⇒ Object
-
#to_xml ⇒ Object
Dumps all data into QBXML.
-
#unref ⇒ Object
Attemps to get the Model object being referenced, if this is a Ref class.
-
#update_xml ⇒ Object
Simply calls to_dirty_xml(true).
-
#valid? ⇒ Boolean
Validate all the necessary elements and attributes are included, using the object class’s spec.
-
#validate ⇒ Object
Use this instead of valid? to get the Valean result instead of just a true or false value.
Constructor Details
#initialize(attrs = {}) ⇒ Element
Create a new Element object, such as Account.new or ItemInventory.new. Marks item as a new record, so .save will send a create command to Quickbooks.
84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/quickbooks/element.rb', line 84 def initialize(attrs={}) attrs = attrs.attributes if attrs.is_a?(Element) attrs = attrs.attributes.values if attrs.is_a?(Model) if attrs.is_a?(Array) # Allows just passing an array of attributes in. set_attrs = attrs.select {|a| self.class.xsd.index(a.class.short_name)} reject_attrs = attrs - set_attrs raise AttributeAssignmentError, "Attribute#{'s' if reject_attrs.length > 1} not available: #{reject_attrs.join(', ')}!\nAvailable attributes: #{self.class.xsd.children.collect {|i| i.name}.join(', ')}" unless reject_attrs.empty? attributes.concat(set_attrs.sort! {|a,b| Quickbooks::Element.order(self,a,b) }) else self.attributes = attrs end end |
Class Attribute Details
.associations ⇒ Object (readonly)
Returns the value of attribute associations.
17 18 19 |
# File 'lib/quickbooks/element.rb', line 17 def associations @associations end |
.options ⇒ Object (readonly)
Returns the value of attribute options.
17 18 19 |
# File 'lib/quickbooks/element.rb', line 17 def @options end |
.properties ⇒ Object (readonly)
Returns the value of attribute properties.
17 18 19 |
# File 'lib/quickbooks/element.rb', line 17 def properties @properties end |
.xsd ⇒ Object
Returns the value of attribute xsd.
18 19 20 |
# File 'lib/quickbooks/element.rb', line 18 def xsd @xsd end |
Class Method Details
.instantiate(obj_or_attrs = {}) ⇒ Object
This is usually used internally, but is safe to use if you need it for edge cases. It is meant to create a new Element object as if you just read it from Quickbooks – marks it as not a new record. Call on an Element class, such as QB::Customer.instantiate(attributes).
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/quickbooks/element.rb', line 43 def instantiate(obj_or_attrs={}) obj = allocate if obj_or_attrs.is_a?(Hash) obj_or_attrs.each do |key,value| key = key.to_s if .include?(key) obj[key] = value next end next unless xsd.include?(key) attr_klass = QB[key] # Add the new value if value.is_a?(ElementCollection) value.each do |v| obj.attributes << v end else value = value.is_a?(attr_klass) ? value : attr_klass.new(value) obj.attributes << value if xsd.index(value.class.short_name) end end elsif obj_or_attrs.respond_to?(:to_element) && elem = obj_or_attrs.to_element && elem.is_a?(self) return elem else raise ArgumentError, "must supply a hash of arguments or a model object" end # Sort the values to the proper order suffix = short_name =~ /(Add|Mod|Ret)$/ ? $1 : '' obj.attributes.sort! do |a,b| begin Quickbooks::Element.order(obj,a,b) rescue => e raise e, "-> Comparing #{a.class.short_name.inspect} <=> #{b.class.short_name.inspect} in #{obj.class.short_name} XSD" end end obj.send(:clean!) obj end |
.order(obj, a, b) ⇒ Object
:nodoc:
28 29 30 31 32 33 34 35 |
# File 'lib/quickbooks/element.rb', line 28 def order(obj, a, b) #:nodoc: c = ( obj.class.xsd.index(a.class.short_name) || obj.class.xsd.index(a.class.short_name + obj.send(:suffix)) ) <=> ( obj.class.xsd.index(b.class.short_name) || obj.class.xsd.index(b.class.short_name + obj.send(:suffix)) ) c == 0 ? a.instance_variable_get(:@collection_index).to_i <=> b.instance_variable_get(:@collection_index).to_i : c end |
.pluralize ⇒ Object
13 14 15 |
# File 'lib/quickbooks/element.rb', line 13 def self.pluralize Quickbooks::Elements end |
Instance Method Details
#[](key) ⇒ Object
Access an attribute value by its type – use a string, a symbol, or its Class constant.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/quickbooks/element.rb', line 139 def [](key) return self.attributes = key if key.is_a?(Hash) key = case key when String key when Symbol key.to_s when Module key.short_name else raise RuntimeError, "boy, that is a weird key (type:#{key.class.name})" end return get_associated(key) if self.class.associations.include?(key) return .has_key?(key) ? [key] : self.class.[key].default if self.class..include?(key) vals = attributes.select {|a| a.class.short_name == key} property_xsd = self.class.xsd.find(key.to_s) property_xsd ? (self.class.xsd.repeatable?(key.to_s) ? vals : vals[0]) : nil end |
#[]=(key, value) ⇒ Object
Set the value of an attribute. If key refers to an association, it associates the given value. If it is an option, it sets the option. Otherwise if it’s a valid attribute, it sets the attribute value.
Note: If multiple of this attribute are allowed, you must send an array of values as the value.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/quickbooks/element.rb', line 162 def []=(key,value) key = case key when String key when Symbol key.to_s when Module key.short_name else raise RuntimeError, "man is that a weird kind of key to use (type:#{key.class.name})!" end return associate(key, value) if self.class.associations.include?(key) unless self.class.xsd.include?(key) if self.class.short_name =~ /(Add|Mod|Ret)$/ && self.class.xsd.include?(key + $1) key = key + $1 else return [key] = value if self.class..include?(key) raise Quickbooks::InvalidAttributeError, "'#{key}' is not a valid property or option name for #{self.class.name}!" end end # Instantiate the value into the attribute class, if necessary attr_klass = Quickbooks.get_constant(key.to_s) if self.class.xsd.repeatable?(key.to_s) if value.is_a?(ElementCollection) || value.is_a?(Array) attributes.reject! {|a| a.is_a?(attr_klass)} value.each do |val| val = attr_klass.new(val) unless val.is_a?(attr_klass) val.send(:dirty!) attributes << val end else # If it *should* be an array element, it shouldn't be set here as a single value. This is just for safeguard, so that syntax # always shows what is going on. For an array element, set it with: object.some_attr = [value] raise RuntimeError, "You can't set a single value into a multi-value element to using equals(=). Use \"element[:#{key}] << value\" to append, or wrap the value in an array -- \"element[:#{key}] = [ value ]\" -- if you want to completely replace the current value set." end else value = attr_klass.new(value) unless value.is_a?(attr_klass) value.send(:dirty!) # Remove the previous value attributes.reject! {|a| a.is_a?(attr_klass)} # Add the new value attributes << value end # Sort the values to the proper order attributes.sort! {|a,b| Quickbooks::Element.order(self, a, b) } end |
#add_error(msg) ⇒ Object
:nodoc:
114 115 116 |
# File 'lib/quickbooks/element.rb', line 114 def add_error(msg) #:nodoc: errors << [nil, msg] end |
#attributes ⇒ Object
An array of attribute values. Each value is of course instantiated as its own type (some subclass of Element or Property). These attributes are automatically kept in order, and can be accessed somewhat like a hash using the #[] method.
123 124 125 |
# File 'lib/quickbooks/element.rb', line 123 def attributes @attributes ||= [] end |
#attributes=(attrs) ⇒ Object
Sets whatever attributes you give it using []=. Does not remove attributes not given.
128 129 130 131 132 133 134 135 136 |
# File 'lib/quickbooks/element.rb', line 128 def attributes=(attrs) attrs.each do |k,v| if self.class.xsd.include?(k.to_s) self[k.to_s] = v else raise AttributeAssignmentError, "Attribute not available: #{k.to_s}!\nAvailable attributes: #{self.class.xsd.children.collect {|i| i.name}.join(', ')}" end end end |
#clean_attributes ⇒ Object
The attributes that have not been changed.
243 244 245 |
# File 'lib/quickbooks/element.rb', line 243 def clean_attributes attributes.reject {|a| a.dirty?} end |
#delete(*keys) ⇒ Object
Remove an attribute from the object. Setting an attribute to nil will be an attribute set to nil, but if you remove the attribute completely, it won’t be considered in create/update operations.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/quickbooks/element.rb', line 211 def delete(*keys) keys.each do |key| key = case key when String key when Symbol key.to_s when Module key.short_name else raise RuntimeError, "boy, that is a weird key (type:#{key.class.name})" end unless self.class.xsd.include?(key) || (self.class.short_name =~ /(Add|Mod|Ret)$/ && self.class.xsd.include?(key + $1)) return .delete(key) if self.class..include?(key) raise RuntimeError, "'#{key}' is not a valid property or option name for #{self.class.name}!" end # Instantiate the value into the attribute class, if necessary attr_klass = Quickbooks.get_constant(key.to_s) attributes.reject! {|a| a.is_a?(attr_klass)} end end |
#dirty? ⇒ Boolean
Has anything in this object (or its descendents) changed?
248 249 250 251 |
# File 'lib/quickbooks/element.rb', line 248 def dirty? # Test for any dirty elements @dirty || attributes.any? {|e| e.dirty?} end |
#dirty_attributes(include_required = false) ⇒ Object
The attributes that have been changed.
235 236 237 238 239 240 |
# File 'lib/quickbooks/element.rb', line 235 def dirty_attributes(include_required=false) attr_hash = attributes_hash # (Speedier when we only do it once) include_required ? attributes.select {|a| a.dirty? || self.class.xsd.required?(a.class.short_name) || !self.class.xsd.validate(self.class.short_name => attr_hash.except(a.class.short_name))} : attributes.select {|a| a.dirty?} end |
#errors ⇒ Object
Holds any errors from the last time valid? is run.
111 112 113 |
# File 'lib/quickbooks/element.rb', line 111 def errors @errors ||= [] end |
#inspect ⇒ Object
:nodoc:
118 119 120 |
# File 'lib/quickbooks/element.rb', line 118 def inspect #:nodoc: "<#{self.class.short_name}:##{object_id}#{' ' + .collect {|k,v| "#{k}=#{v.inspect}"}.join(' ')}#{" [#{@collection_index}]" if instance_variables.include?('@collection_index')}\n #{attributes.collect {|a| a.inspect}.join("\n").gsub(/\n/, "\n ")}>" end |
#new_record? ⇒ Boolean
I don’t remember why this nil? had to be modified. If the need pops back up, CREATE A TEST to show the need!! def nil? #:nodoc:
if self.class.xsd.include?('FullName') && self.class.xsd.include?('ListID') || (self.class.xsd.include?('TxnID') && !self.class.xsd.name =~ /DataExt/)
self[:FullName].nil? && self[:ListID].nil? && self[:TxnID].nil?
else
super
end
end
330 331 332 |
# File 'lib/quickbooks/element.rb', line 330 def new_record? end |
#save_associations ⇒ Object
:nodoc:
309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/quickbooks/element.rb', line 309 def save_associations #:nodoc: # first save any of my associated items that aren't already existing self.class.associations.each_key do |association_name| if instance_variable_get("@#{association_name}") && self[association_name].new_record? self[association_name].save self[association_name] = self[association_name] # re-assigns the associated Ref end end # then save any of my children's associated items that aren't already existing attributes.each { |attv| attv.save_associations if attv.respond_to?(:save_associations) } end |
#to_dirty_xml(include_required = false) ⇒ Object
Dumps only the changed data into QBXML. Useful for updating only specific attributes without touching the rest. Set include_required to true if you need valid qbxml to send.
300 301 302 |
# File 'lib/quickbooks/element.rb', line 300 def to_dirty_xml(include_required=false) '<' + self.class.short_name + '>' + ("\n" + dirty_attributes(include_required).collect {|a| a.to_dirty_xml(include_required)}.join("\n")).gsub(/\n( *)</, "\n \\1<") + "\n</" + self.class.short_name + '>' end |
#to_element ⇒ Object
Returns self. Just a convenience method for interoperability with Model.
254 255 256 |
# File 'lib/quickbooks/element.rb', line 254 def to_element self end |
#to_model ⇒ Object
Converts the element to a model if it is an appropriate class type.
275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/quickbooks/element.rb', line 275 def to_model # List clean attributes clean_attrs = attributes_to_hash(clean_attributes).inject({}) {|h,(k,v)| h[k.gsub(/(Add|Mod|Ret)$/,'')] = v.respond_to?(:to_model) ? v.to_model : v; h} # List dirty attributes dirty_attrs = attributes_to_hash(dirty_attributes).inject({}) {|h,(k,v)| h[k.gsub(/(Add|Mod|Ret)$/,'')] = v.respond_to?(:to_model) ? v.to_model : v; h} # Set up the corresponding element with corresponding properties model = model_klass.instantiate(clean_attrs) model.attributes = dirty_attrs # Transfer the order index (for attribute sorting) from elements that were in a collection. model.instance_variable_set(:@collection_index, @collection_index) if instance_variables.include?('@collection_index') model end |
#to_update_element ⇒ Object
288 289 290 291 292 |
# File 'lib/quickbooks/element.rb', line 288 def to_update_element update_element = self.class.new update_element.attributes.replace(dirty_attributes(true)) update_element end |
#to_xml ⇒ Object
Dumps all data into QBXML.
295 296 297 |
# File 'lib/quickbooks/element.rb', line 295 def to_xml '<' + self.class.short_name + [[nil] + .collect {|k,v| "#{k}=#{v.inspect}"}].join(' ') + '>' + ("\n" + attributes.collect {|a| a.to_xml}.join("\n")).gsub(/\n( *)</, "\n \\1<") + "\n</" + self.class.short_name + '>' end |
#unref ⇒ Object
Attemps to get the Model object being referenced, if this is a Ref class.
259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/quickbooks/element.rb', line 259 def unref @retrieve_full ||= begin filter = {} case when self[:ListID] filter[:ListID] = [self[:ListID]] when self[:TxnID] filter[:TxnID] = [self[:TxnID]] when self[:FullName] filter[:FullName] = [self[:FullName]] end self.class.unref.first(filter) end end |
#update_xml ⇒ Object
Simply calls to_dirty_xml(true). Returns the minimal xml necessary to update the record in QuickBooks.
305 306 307 |
# File 'lib/quickbooks/element.rb', line 305 def update_xml to_dirty_xml(true) end |
#valid? ⇒ Boolean
Validate all the necessary elements and attributes are included, using the object class’s spec
99 100 101 |
# File 'lib/quickbooks/element.rb', line 99 def valid? validate.perfect? end |
#validate ⇒ Object
Use this instead of valid? to get the Valean result instead of just a true or false value.
104 105 106 107 108 |
# File 'lib/quickbooks/element.rb', line 104 def validate r = self.class.xsd.validate(self) errors.replace(r.errors) r end |