Class: Qbxml::Request
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(object, type, options_or_regexp = {}) ⇒ Request
constructor
1.
-
#to_xml(as_set = true) ⇒ Object
This is where the magic happens to convert a request object into xml worthy of quickbooks.
Constructor Details
#initialize(object, type, options_or_regexp = {}) ⇒ Request
-
List queries (:query)
Request.new(object, :query) # <= will return the record matching the object supplied (automatically searches by list_id or txn_id)
Request.new(Customer, :query, :limit => 1) # <= will return the first customer
Request.new(Customer, :query, /some match/) # <= will return all customers matching the regexp
-
Deleted queries (:deleted)
Request.new(object, :deleted) # <= INVALID!! (besides, you wouldn't have an object to check up on anyway :P )
Request.new(Customer, :deleted) # <= will return all deleted Customers
Request.new(Customer, :deleted, :deleted_date_range_filter => {'from_deleted_date' => Date.parse('2008/10/05')}) # <= will return all Customers deleted since 5 October 2008
-
Object-specific transaction query (:transaction)
Request.new(Transaction, :query) # <= will return the transaction matching the object supplied
-
Mod requests (:mod)
Request.new(object, :mod) # <= will update the object
-
Delete requests (:delete)
Request.new(object, :delete) # <= will delete the object
We want the attributes of the object when we are updating, but no other time.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/qbxml/request.rb', line 69 def initialize(object, type, ={}) = .is_a?(Regexp) ? {:matches => } : @type = type raise ArgumentError, "Qbxml::Requests can only be of one of the following types: :query, :transaction, :any_transaction, :mod, :add, :delete, :deleted, or :report" unless @type.is_one_of?(:query, :transaction, :any_transaction, :mod, :report, :add, :delete, :deleted) @klass = object.is_a?(Class) ? object : object.class @object = object @options = # Transform the options received for :deleted requests. # This way, Request.new(Customer, :deleted) == Request.new(ListDeleted, :list_del_type => 'Customer') # and Request.new(Transaction, :deleted) == Request.new(TxnDeleted, :txn_del_type => 'Transaction') if @type == :deleted case @klass.ListOrTxn when 'List' @options[:list_del_type] = @klass.lone_name @klass = Quickbooks::ListDeleted when 'Txn' @options[:txn_del_type] = @klass.lone_name @klass = Quickbooks::TxnDeleted end end # Return only specific properties: Request.new(Customer, :query, :only => [:list_id, :full_name]); Quickbooks::Customer.first(:only => :list_id) @ret_elements = @options.delete(:only).to_a.only(@klass.properties).ordered!(@klass.properties).stringify_values.camelize_values!(Quickbooks::CAMELIZE_EXCEPTIONS) if @options.has_key?(:only) # Includes only valid filters + aliases for valid filters, # => {underscore/slashed keys + aliases} # then transforms aliased filters to real filters, # => {underscore/slashed keys} # then camelizes keys to prepare for writing to XML, # => {} # lastly orders the keys to a valid filter order. # Study the following: # @klass.filter_aliases = {'deleted_after' => 'deleted_date_range_filter/from_deleted_date'} # @klass.valid_filters = ['deleted_date_range_filter/from_deleted_date'] # {:deleted_after => "2008-03-20T11:20:26-04:00"}.stringify_keys.flatten_slashes. # only(@klass.valid_filters + @klass.filter_aliases.expand_slashes.keys). # transform_keys!(@klass.filter_aliases.expand_slashes.inject({}) {|h,(k,v)| v = [v].flatten_slashes; h[k] = v.length < 2 ? v[0] : v; h}). # slash_camelize_keys!(Quickbooks::CAMELIZE_EXCEPTIONS). # ordered!(@klass.camelized_valid_filters).expand_slashes # # => {"DeletedDateRangeFilter" => {"FromDeletedDate" => "2008-03-20T11:20:26-04:00"}} @filters = @options.delete(:filters) || @options.stringify_keys.flatten_slashes. only(@klass.valid_filters + @klass.filter_aliases..keys). transform_keys!(@klass.filter_aliases..inject({}) {|h,(k,v)| v = [v].flatten_slashes; h[k] = v.length < 2 ? v[0] : v; h}). slash_camelize_keys!(Quickbooks::CAMELIZE_EXCEPTIONS). ordered!(@klass.camelized_valid_filters). @filters = @filters.to_hash unless @filters.is_a?(Hash) @filters = @filters['FILTERS'] if @filters.keys == ['FILTERS'] # So if you include an xml-formatted string of filters, you can simply wrap them in <FILTERS></FILTERS> as a root key # Complain if: # 1) type is :mod or :delete, and object supplied is not a valid model raise ArgumentError, "A Quickbooks record object must be supplied to perform an add, mod or del action" if @type.is_one_of?(:add, :mod, :delete) && !@object.is_a?(Quickbooks::Base) end |
Class Method Details
.next_request_id ⇒ Object
120 121 122 123 |
# File 'lib/qbxml/request.rb', line 120 def self.next_request_id @request_id ||= 0 @request_id += 1 end |
Instance Method Details
#to_xml(as_set = true) ⇒ Object
This is where the magic happens to convert a request object into xml worthy of quickbooks.
126 127 128 129 130 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 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 |
# File 'lib/qbxml/request.rb', line 126 def to_xml(as_set=true) return (RequestSet.new(self)).to_xml if as_set # Simple call yields xml as a single request in a request set. However, if the xml for the lone request is required, pass false. req = Builder::XmlMarkup.new(:indent => 2) request_root, container = case when @type.is_one_of?(:query) ["#{@klass.class_leaf_name}QueryRq", nil] when @type == :add ["#{@klass.class_leaf_name}AddRq", "#{@klass.class_leaf_name}Add"] when @type == :mod ["#{@klass.class_leaf_name}ModRq", "#{@klass.class_leaf_name}Mod"] when @type == :delete ["#{@klass.ListOrTxn}DelRq", nil] when @type == :deleted ["#{@klass.ListOrTxn}DeletedQueryRq", nil] else raise RuntimeError, "Could not convert this request to qbxml!\n#{self.inspect}" end inner_stuff = lambda { deep_tag = lambda {|k,v| if v.is_a?(Hash) if k == '' v.each { |k,v| deep_tag.call(k,v) } else req.tag!(k.camelize) { v.each { |k,v| deep_tag.call(k,v) } } end else req.tag!(k.camelize,uncast(v)) end } # Add the specific elements for the respective request type if @type.is_one_of?(:add, :mod) if @type == :mod # First the ObjectId: req.tag!(@klass.ListOrTxn + 'ID', @object.send("#{@klass.ListOrTxn}Id".underscore)) # Second the EditSequence req.tag!('EditSequence', @object.send(:edit_sequence)) end # Then, all the dirty_attributes deep_tag.call('',@object.to_dirty_hash) # (this is an hash statically ordered to the model's qbxml attribute order) elsif @type == :query && @object.class == @klass # Sent an instance object for a query - we should include the ListId/TxnId (then other filters?) req.tag!(@klass.ListOrTxn + 'ID', @object.send("#{@klass.ListOrTxn}Id".underscore)) deep_tag.call('', @filters) elsif @type == :delete req.tag!(@klass.ListOrTxn + 'DelType', @klass.class_leaf_name) req.tag!(@klass.ListOrTxn + 'ID', @object.send("#{@klass.ListOrTxn}Id".underscore)) else # just filters deep_tag.call('', @filters) end # Lastly, specify the fields to return, if desired @ret_elements.each { |r| req.tag!('IncludeRetElement', r) } if @ret_elements.is_a?(Array) && @ret_elements.length > 0 } req.tag!(request_root, :requestID => self.class.next_request_id) { if container req.tag!(container) { inner_stuff.call } else inner_stuff.call end } # puts req.target! req.target! end |