gentle 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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