gentle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +64 -0
  6. data/Rakefile +9 -0
  7. data/gentle.gemspec +29 -0
  8. data/lib/gentle.rb +8 -0
  9. data/lib/gentle/blackboard.rb +44 -0
  10. data/lib/gentle/client.rb +92 -0
  11. data/lib/gentle/documents/document.rb +15 -0
  12. data/lib/gentle/documents/request/hash_converter.rb +55 -0
  13. data/lib/gentle/documents/request/shipment_order.rb +167 -0
  14. data/lib/gentle/documents/response/shipment_order_result.rb +56 -0
  15. data/lib/gentle/error_message.rb +19 -0
  16. data/lib/gentle/message.rb +43 -0
  17. data/lib/gentle/phase_1_set.rb +53 -0
  18. data/lib/gentle/queue.rb +40 -0
  19. data/lib/gentle/request.rb +11 -0
  20. data/lib/gentle/response.rb +11 -0
  21. data/lib/gentle/version.rb +3 -0
  22. data/spec/fixtures/credentials.yml +12 -0
  23. data/spec/fixtures/documents/responses/shipment_order_result.xml +23 -0
  24. data/spec/fixtures/messages/error_message.xml +10 -0
  25. data/spec/fixtures/messages/purchase_order_message.xml +11 -0
  26. data/spec/remote/blackboard_spec.rb +43 -0
  27. data/spec/remote/queue_spec.rb +64 -0
  28. data/spec/spec_helper.rb +323 -0
  29. data/spec/unit/blackboard_spec.rb +102 -0
  30. data/spec/unit/client_spec.rb +97 -0
  31. data/spec/unit/documents/document_spec.rb +44 -0
  32. data/spec/unit/documents/request/hash_converter_spec.rb +42 -0
  33. data/spec/unit/documents/request/shipment_order_spec.rb +164 -0
  34. data/spec/unit/documents/response/shipment_order_result_spec.rb +26 -0
  35. data/spec/unit/error_message_spec.rb +31 -0
  36. data/spec/unit/message_spec.rb +71 -0
  37. data/spec/unit/phase_1_set_spec.rb +76 -0
  38. data/spec/unit/queue_spec.rb +57 -0
  39. metadata +196 -0
@@ -0,0 +1,56 @@
1
+ require 'time'
2
+ module Gentle
3
+ module Documents
4
+ module Response
5
+ class ShipmentOrderResult
6
+ attr_reader :io
7
+
8
+ def initialize(options = {})
9
+ @io = options[:io]
10
+ @document = Nokogiri::XML::Document.parse(@io)
11
+ end
12
+
13
+ def client_id
14
+ @client_id ||= so_result['ClientID']
15
+ end
16
+
17
+ def business_unit
18
+ @business_unit ||= so_result['BusinessUnit']
19
+ end
20
+
21
+ def date_shipped
22
+ @date_shipped ||= Time.parse(so_result['DateShipped']).utc
23
+ end
24
+
25
+ def order_number
26
+ @order_number ||= so_result['OrderNumber']
27
+ end
28
+
29
+ def carton_count
30
+ @carton_count ||= so_result['CartonCount'].to_i
31
+ end
32
+
33
+ def carrier
34
+ @carrier ||= carton['Carrier']
35
+ end
36
+
37
+ def service_level
38
+ @service_level ||= carton['ServiceLevel']
39
+ end
40
+
41
+ def tracking_number
42
+ @tracking_number ||= carton['TrackingId']
43
+ end
44
+
45
+ def so_result
46
+ @so_result ||= @document.css('SOResult').first
47
+ end
48
+
49
+ def carton
50
+ @carton ||= @document.css('Carton').first
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ module Gentle
2
+ class ErrorMessage
3
+ attr_reader :xml
4
+
5
+ def initialize(options = {})
6
+ @xml = Nokogiri::XML::Document.parse(options[:xml]) if options[:xml]
7
+ end
8
+
9
+ def to_xml
10
+ @xml.serialize(encoding: "UTF-8")
11
+ end
12
+
13
+ def shipment_number
14
+ result_description = @xml.css("ErrorMessage").first["ResultDescription"]
15
+ match_data = result_description.match(/(.+)_(.+)_(.+)_.*.xml/) unless result_description.nil?
16
+ match_data[2] unless match_data.nil?
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module Gentle
2
+ class Message
3
+
4
+ NAMESPACE = "http://schemas.quietlogistics.com/V2/EventMessage.xsd"
5
+
6
+ class MissingDocumentError < StandardError; end
7
+ class MissingClientError < StandardError; end
8
+
9
+ attr_reader :client, :document, :xml
10
+
11
+ def initialize(options = {})
12
+ @xml = Nokogiri::XML::Document.parse(options[:xml]) if options[:xml]
13
+ @client = options[:client]
14
+ @document = options[:document]
15
+ end
16
+
17
+ def to_xml
18
+ builder = Nokogiri::XML::Builder.new do |xml|
19
+ xml.EventMessage(attributes)
20
+ end
21
+ builder.to_xml
22
+ end
23
+
24
+ def document_type
25
+ @document ? @document.type : @xml.css('EventMessage').first['DocumentType']
26
+ end
27
+
28
+ def document_name
29
+ @document ? @document.filename : @xml.css('EventMessage').first['DocumentName']
30
+ end
31
+
32
+ def attributes
33
+ raise(MissingClientError.new("client cannot be missing")) unless @client
34
+ raise(MissingDocumentError.new("document cannot be missing")) unless @document
35
+ {
36
+ ClientId: @client.client_id, BusinessUnit: @client.business_unit,
37
+ DocumentName: @document.filename, DocumentType: @document.type,
38
+ Warehouse: @document.warehouse, MessageDate: @document.date.utc.iso8601,
39
+ MessageId: @document.message_id, xmlns: NAMESPACE
40
+ }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ module Gentle
2
+ class Phase1Set
3
+
4
+ SKUS = {
5
+ "GPS1-5" => "GPST100 - 2",
6
+ "GPS2-5" => "GPST200 - 2",
7
+ "GPS3-5" => "GPST300",
8
+ "GPS4-5" => "GPST400",
9
+ "GPS5-5" => "GPST500"
10
+ }
11
+
12
+ def initialize(item)
13
+ @item = item
14
+ end
15
+
16
+ def self.match(item)
17
+ SKUS.key? item.sku
18
+ end
19
+
20
+ def included_items
21
+ [
22
+ milky_jelly,
23
+ priming_moisturizer,
24
+ balm_dotcom,
25
+ skin_tint
26
+ ]
27
+ end
28
+
29
+ def milky_jelly
30
+ line_item(97 , "GMJC100", 18.0)
31
+ end
32
+
33
+ def priming_moisturizer
34
+ line_item( 3, "GPM100", 25.0)
35
+ end
36
+
37
+ def balm_dotcom
38
+ line_item(96, "GBD100-3", 12.0)
39
+ end
40
+
41
+ def skin_tint
42
+ line_item(6, SKUS[@item.sku], 26.0)
43
+ end
44
+
45
+ def line_item(id, sku, price)
46
+ LineItem.new(sku, id, @item.quantity, price)
47
+ end
48
+
49
+ end
50
+
51
+ class LineItem < Struct.new(:sku, :id, :quantity, :price)
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ module Gentle
2
+ class Queue
3
+ attr_reader :client
4
+ def initialize(client)
5
+ @client = client
6
+ end
7
+
8
+ def send(message)
9
+ queue = client.to_quiet_queue
10
+ queue.send_message(message.to_xml)
11
+ end
12
+
13
+ def receive
14
+ queue = client.from_quiet_queue
15
+ message = nil
16
+ queue.receive_message do |msg|
17
+ message = build_message(msg)
18
+ end
19
+ message || Message.new
20
+ end
21
+
22
+ def approximate_pending_messages
23
+ client.from_quiet_queue.approximate_number_of_messages
24
+ end
25
+
26
+ private
27
+
28
+ def build_message(msg)
29
+ if received_error? msg
30
+ ErrorMessage.new(xml: msg.body)
31
+ else
32
+ Message.new(xml: msg.body)
33
+ end
34
+ end
35
+
36
+ def received_error?(msg)
37
+ msg.body.include? "<ErrorMessage"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ require 'gentle/documents/request/shipment_order'
2
+
3
+ module Gentle
4
+ module Request
5
+ VALID_REQUEST_TYPES = %w(ShipmentOrder)
6
+
7
+ def self.valid_type?(type)
8
+ VALID_REQUEST_TYPES.include?(type)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'gentle/documents/response/shipment_order_result'
2
+
3
+ module Gentle
4
+ module Response
5
+ VALID_RESPONSE_TYPES = %w(ShipmentOrderResult)
6
+
7
+ def self.valid_type?(type)
8
+ VALID_RESPONSE_TYPES.include?(type)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Gentle
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ access_key_id: abracadabra
2
+ secret_access_key: alakazam
3
+ client_id: Gentle
4
+ business_unit: Gentle
5
+ warehouse: SPACE
6
+ buckets:
7
+ to: gentle-to-quiet
8
+ from: gentle-from-quiet
9
+ queues:
10
+ to: http://queue.amazonaws/1234567890/gentle_to_quiet
11
+ from: http://queue.amazonaws/1234567890/gentle_from_quiet
12
+ inventory: http://queue.amazonaws/1234567890/gentle_inventory
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <SOResult
3
+ xmlns="http://schemas.quiettechnology.com/V2/SOResultDocument.xsd"
4
+ ClientID="Gentle"
5
+ BusinessUnit="Gentle"
6
+ CartonCount="1"
7
+ DateShipped="2009-09-01T04:00:00Z"
8
+ FreightCost="10.00"
9
+ OrderNumber="1234567890">
10
+ <Line Line="1" Quantity="1" />
11
+ <Line Line="2" Quantity="1" />
12
+ <Carton
13
+ Carrier="USPS"
14
+ CartonId="S12345678901"
15
+ CartonNumber="1"
16
+ FreightCost="10.00"
17
+ ServiceLevel="FIRST"
18
+ TrackingId="40000000000"
19
+ Weight="0.66">
20
+ <Content Line="1"Quantity="1" />
21
+ <Content Line="2"Quantity="1" />
22
+ </Carton>
23
+ </SOResult>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ErrorMessage
3
+ xmlns="http://schemas.quietlogistics.com/V2/EventMessageErrorResponse.xsd"
4
+ OriginalMessageId="ab6e7e7a-6d45-4c75-9fec-e08dba4a9c31"
5
+ ResponseDate="2009-09-01T12:00:00Z"
6
+ ClientId="Gentle"
7
+ BusinessUnit="Gentle"
8
+ ResultCode="1030"
9
+ ResultDescription="Document: Gentle_ShipmentOrder_H123456789_20160229_135723.xml - Error: An error has occured">
10
+ </ErrorMessage>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <EventMessage
3
+ xmlns="http://schemas.quietlogistics.com/V2/EventMessage.xsd"
4
+ ClientId="Gentle"
5
+ BusinessUnit="Gentle"
6
+ DocumentName="Gentle_PurchaseOrder_1234_20100927_132505124.xml"
7
+ DocumentType="PurchaseOrder"
8
+ MessageId="EF1CE966-38A2-428b-BA67-EFF23AF22F57"
9
+ Warehouse="CORP1"
10
+ MessageDate="2009-09-01T12:00:00Z">
11
+ </EventMessage>
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'gentle'
3
+ require 'gentle/blackboard'
4
+
5
+ module Gentle
6
+ describe "BlackboardRemote" do
7
+ include Configuration
8
+ include Fixtures
9
+
10
+ before do
11
+ AWS.config(stub_requests: false)
12
+ @client = Client.new(load_configuration)
13
+ @blackboard = Blackboard.new(@client)
14
+ @document = DocumentDouble.new(
15
+ message_id: '1234567',
16
+ date: Time.new(2013, 04, 05, 12, 30, 15).utc,
17
+ filename: 'neat_beans.xml',
18
+ client: @client,
19
+ type: 'ShipmentOrderResult'
20
+ )
21
+ end
22
+
23
+ after do
24
+ buckets = [@client.to_quiet_bucket, @client.from_quiet_bucket]
25
+ buckets.each { |bucket| bucket.objects.delete_all }
26
+ end
27
+
28
+ it "should be able to write a document to an S3 bucket" do
29
+ message = @blackboard.post(@document)
30
+ file = message.document_name
31
+ bucket = @client.to_quiet_bucket
32
+ assert bucket.objects[file].exists?, "It appears that #{file} was not written to S3"
33
+ end
34
+
35
+ it "should be able to fetch a document from an S3 bucket when given a message" do
36
+ expected_contents = load_response('shipment_order_result')
37
+ @client.from_quiet_bucket.objects[@document.filename].write(expected_contents)
38
+ message = MessageDouble.new(document_name: @document.filename, document_type: @document.type)
39
+ document = @blackboard.fetch(message)
40
+ assert_equal expected_contents, document.io
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'openssl'
3
+ require 'gentle'
4
+ require 'gentle/message'
5
+
6
+ module Gentle
7
+ describe "QueueRemote" do
8
+ include Configuration
9
+
10
+ before do
11
+ AWS.config(:stub_requests => false)
12
+ @client = Client.new(load_configuration)
13
+ @sqs_queues = [@client.to_quiet_queue, @client.from_quiet_queue]
14
+ @default_wait_time = @sqs_queues.map(&:wait_time_seconds).max
15
+ @sqs_queues.each { |queue| queue.wait_time_seconds = 1 }
16
+
17
+ @document = DocumentDouble.new(
18
+ :message_id => '1234567',
19
+ :date => Time.new(2013, 04, 05, 12, 30, 15).utc,
20
+ :filename => 'neat_beans.xml',
21
+ :client => @client,
22
+ :type => 'Thinger'
23
+ )
24
+
25
+ @message = Message.new(:client => @client, :document => @document)
26
+ @queue = Queue.new(@client)
27
+ end
28
+
29
+ after do
30
+ @sqs_queues.each do |queue|
31
+ flush(queue)
32
+ end
33
+ @sqs_queues.each { |queue| queue.wait_time_seconds = @default_wait_time }
34
+ end
35
+
36
+ it "should be able to push a message onto the queue" do
37
+ expected_md5 = OpenSSL::Digest::MD5.new.hexdigest(@message.to_xml)
38
+ sent_message = @queue.send(@message)
39
+ # NOTE: This is failing, but the message is getting sent
40
+ # assert_equal 1, @client.to_quiet_queue.approximate_number_of_messages
41
+ assert_equal expected_md5, sent_message.md5
42
+ end
43
+
44
+ it "should be able to fetch a message from the queue" do
45
+ @client.from_quiet_queue.send_message(@message.to_xml)
46
+ message = @queue.receive
47
+
48
+ assert_equal @message.document_type, message.document_type
49
+ assert_equal @message.document_name, message.document_name
50
+ end
51
+
52
+ private
53
+ def flush(queue)
54
+ pending_messages = queue.approximate_number_of_messages
55
+ while pending_messages > 0
56
+ queue.receive_message do |message|
57
+ message.delete
58
+ pending_messages -= 1
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,323 @@
1
+ require 'minitest/autorun'
2
+ require 'nokogiri'
3
+ require 'yaml'
4
+ require 'mocha/setup'
5
+ require 'gentle'
6
+ require 'pry'
7
+
8
+ module Fixtures
9
+ def load_fixture(path)
10
+ File.open(path, 'rb').read
11
+ end
12
+
13
+ def load_response(response_name)
14
+ load_fixture("spec/fixtures/documents/responses/#{response_name}.xml")
15
+ end
16
+
17
+ def load_message(message_name)
18
+ load_fixture("spec/fixtures/messages/#{message_name}.xml")
19
+ end
20
+ end
21
+
22
+ module Configuration
23
+ def load_configuration
24
+ test_credentials_file = ENV['HOME'] + '/.gentle/credentials.yml'
25
+ test_credentials_file = "spec/fixtures/credentials.yml" unless File.exists?(test_credentials_file)
26
+ YAML.load(File.open(test_credentials_file, 'rb'))
27
+ end
28
+ end
29
+
30
+ class VariantDouble
31
+ DEFAULT_OPTIONS = {
32
+ id: 112233,
33
+ sku: "ABC-123",
34
+ price: '6.95'
35
+ }
36
+
37
+ attr_reader :id, :sku, :price
38
+ def initialize(options = {})
39
+ @id = options[:id]
40
+ @sku = options[:sku]
41
+ @price = options[:price]
42
+ end
43
+
44
+ def self.example(options = {})
45
+ self.new(DEFAULT_OPTIONS.merge(options))
46
+ end
47
+ end
48
+
49
+ class LineItemDouble
50
+ DEFAULT_OPTIONS = {
51
+ id: 123456,
52
+ quantity: 1,
53
+ price: '12.95',
54
+ sku: "ABC-123",
55
+ part_line_items: []
56
+ }
57
+
58
+ attr_reader :id, :quantity, :price, :sku, :product, :part_line_items
59
+ def initialize(options = {})
60
+ @id = options[:id]
61
+ @quantity = options[:quantity]
62
+ @price = options[:price]
63
+ @sku = options[:sku]
64
+ @product = options[:product]
65
+
66
+ @part_line_items = options[:part_line_items].map do |part|
67
+ PartLineItemDouble.example(
68
+ id: part.id,
69
+ variant: part.variant,
70
+ line_item: LineItemDouble.example(quantity: @quantity)
71
+ )
72
+ end
73
+ end
74
+
75
+ def self.example(options = {})
76
+ self.new(DEFAULT_OPTIONS.merge(options))
77
+ end
78
+ end
79
+
80
+ class PartLineItemDouble
81
+ DEFAULT_OPTIONS = {
82
+ id: 444555,
83
+ variant: VariantDouble.example,
84
+ line_item: LineItemDouble.example
85
+ }
86
+
87
+ attr_reader :id, :variant, :line_item
88
+ def initialize(options = {})
89
+ @id = options[:id]
90
+ @variant = options[:variant]
91
+ @line_item = options[:line_item]
92
+ end
93
+
94
+ def self.example(options = {})
95
+ self.new(DEFAULT_OPTIONS.merge(options))
96
+ end
97
+ end
98
+
99
+ class StateDouble
100
+ DEFAULT_OPTIONS = {
101
+ iso_name: "CANADA",
102
+ name: "Ontario",
103
+ abbr: "ON",
104
+ }
105
+
106
+ attr_reader :name
107
+
108
+ def initialize(options = {})
109
+ @name = options[:name]
110
+ end
111
+
112
+ def self.example(options = {})
113
+ self.new(DEFAULT_OPTIONS.merge(options))
114
+ end
115
+ end
116
+
117
+ class CountryDouble
118
+ DEFAULT_OPTIONS = {
119
+ iso_name: "CANADA",
120
+ name: "Canada",
121
+ iso: "CA"
122
+ }
123
+
124
+ attr_reader :name
125
+
126
+ def initialize(options = {})
127
+ @name = options[:name]
128
+ end
129
+
130
+ def self.example(options = {})
131
+ self.new(DEFAULT_OPTIONS.merge(options))
132
+ end
133
+
134
+ end
135
+
136
+ class AddressDouble
137
+ DEFAULT_OPTIONS = {
138
+ company: 'Shopify',
139
+ name: 'John Smith',
140
+ address1: '123 Fake St',
141
+ address2: 'Unit 128',
142
+ city: 'Ottawa',
143
+ country: CountryDouble.example,
144
+ state: StateDouble.example,
145
+ zipcode: 'K1N 5T5'
146
+ }
147
+
148
+ attr_reader :company, :name, :address1, :address2, :city, :country, :state, :zipcode
149
+
150
+ def initialize(options = {})
151
+ @company = options[:company]
152
+ @name = options[:name]
153
+ @address1 = options[:address1]
154
+ @address2 = options[:address2]
155
+ @city = options[:city]
156
+ @country = options[:country]
157
+ @state = options[:state]
158
+ @zipcode = options[:zipcode]
159
+ end
160
+
161
+ def full_name
162
+ name
163
+ end
164
+
165
+ def self.example(options = {})
166
+ self.new(DEFAULT_OPTIONS.merge(options))
167
+ end
168
+ end
169
+
170
+ class ShippingLineDouble
171
+ def initialize(options = {})
172
+ @options = options
173
+ end
174
+
175
+ def carrier
176
+ @options[:code].split('_').first
177
+ end
178
+
179
+ def service_level
180
+ @options[:code].split('_').last
181
+ end
182
+ end
183
+
184
+ class ShippingMethodDouble
185
+ DEFAULT_OPTIONS = {
186
+ name: "UPS Ground",
187
+ admin_name: "fed001",
188
+ code: "fedex",
189
+ carrier: "FEDEX",
190
+ service_level: "GROUND"
191
+ }
192
+
193
+ attr_reader :name, :admin_name, :code, :carrier, :service_level
194
+
195
+ def initialize(options = {})
196
+ @name = options[:name]
197
+ @admin_name = options[:admin_name]
198
+ @code = options[:code]
199
+ @carrier = options[:carrier]
200
+ @service_level = options[:service_level]
201
+ end
202
+
203
+ def self.example(options = {})
204
+ self.new(DEFAULT_OPTIONS.merge(options))
205
+ end
206
+ end
207
+
208
+ class OrderDouble
209
+ DEFAULT_OPTIONS = {
210
+ number: "GLO#{rand.to_s[2..11]}",
211
+ line_items: [LineItemDouble.example],
212
+ ship_address: AddressDouble.example,
213
+ bill_address: AddressDouble.example,
214
+ note: 'Happy Birthday',
215
+ created_at: Time.new(2013, 04, 05, 12, 30, 00),
216
+ id: 123456,
217
+ email: 'john@smith.com',
218
+ total_price: '123.45'
219
+ }
220
+
221
+ attr_reader :line_items, :ship_address, :bill_address, :note, :email
222
+ attr_reader :total_price, :email, :id, :type, :created_at, :shipping_lines
223
+ attr_reader :number
224
+
225
+ def initialize(options = {})
226
+ @line_items = options[:line_items]
227
+ @ship_address = options[:ship_address]
228
+ @bill_address = options[:bill_address]
229
+ @note = options[:note]
230
+ @created_at = options[:created_at]
231
+ @id = options[:id]
232
+ @shipping_lines = options[:shipping_lines]
233
+ @email = options[:email]
234
+ @total_price = options[:total_price]
235
+ @number = options[:number]
236
+ end
237
+
238
+ def self.example(options = {})
239
+ self.new(DEFAULT_OPTIONS.merge(options))
240
+ end
241
+
242
+ def special_instructions
243
+ @note
244
+ end
245
+
246
+ def shipping_address
247
+ @ship_address
248
+ end
249
+
250
+ def billing_address
251
+ @bill_address
252
+ end
253
+
254
+ def type
255
+ @type || "SO"
256
+ end
257
+ end
258
+
259
+ class ShipmentDouble
260
+ DEFAULT_OPTIONS = {
261
+ shipping_method: ShippingMethodDouble.example,
262
+ number: "H#{rand.to_s[2..11]}",
263
+ order: OrderDouble.example,
264
+ address: AddressDouble.example,
265
+ state: "pending",
266
+ created_at: Time.new(2013, 04, 06, 13, 45, 00)
267
+ }
268
+
269
+ attr_reader :order, :number, :address, :shipping_method, :created_at
270
+
271
+ def initialize(options = {})
272
+ @order = options[:order]
273
+ @number = options[:number]
274
+ @address = options[:address]
275
+ @shipping_method = options[:shipping_method]
276
+ @created_at = options[:created_at]
277
+ end
278
+
279
+ def self.example(options = {})
280
+ self.new(DEFAULT_OPTIONS.merge(options))
281
+ end
282
+ end
283
+
284
+ class DocumentDouble
285
+ include Gentle::Documents::Document
286
+ attr_accessor :type, :message_id, :warehouse, :date, :client
287
+ attr_accessor :business_unit, :document_number, :io
288
+
289
+ def initialize(options = {})
290
+ options.each do |key, value|
291
+ self.public_send("#{key}=".to_sym, value)
292
+ end
293
+ end
294
+
295
+ def to_xml
296
+ builder = Nokogiri::XML::Builder.new do |xml|
297
+ xml.DocumentDouble 'Hello World'
298
+ end
299
+ builder.to_xml
300
+ end
301
+
302
+ def filename=(filename)
303
+ @filename = filename
304
+ end
305
+ end
306
+
307
+ class MessageDouble
308
+ attr_accessor :document_name, :document_type
309
+
310
+ def initialize(options = {})
311
+ @document_name = options[:document_name]
312
+ @document_type = options[:document_type]
313
+ end
314
+ end
315
+
316
+ class ClientDouble
317
+ attr_accessor :client_id, :business_unit
318
+
319
+ def initialize(options = {})
320
+ @client_id = options[:client_id]
321
+ @business_unit = options[:business_unit]
322
+ end
323
+ end