cxml-ruby 0.3.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -0
  3. data/.rubocop.yml +4 -0
  4. data/CHANGELOG.md +18 -0
  5. data/cxml-ruby.gemspec +1 -0
  6. data/lib/cxml-ruby.rb +1 -1
  7. data/lib/cxml/address.rb +4 -1
  8. data/lib/cxml/comment.rb +9 -0
  9. data/lib/cxml/contact.rb +4 -1
  10. data/lib/cxml/credential.rb +1 -1
  11. data/lib/cxml/deposit_amount.rb +9 -0
  12. data/lib/cxml/document.rb +11 -6
  13. data/lib/cxml/document_node.rb +14 -6
  14. data/lib/cxml/invoice_detail_item.rb +11 -11
  15. data/lib/cxml/invoice_detail_item_reference.rb +3 -1
  16. data/lib/cxml/invoice_detail_request_header.rb +5 -5
  17. data/lib/cxml/invoice_detail_summary.rb +6 -4
  18. data/lib/cxml/item_detail.rb +5 -1
  19. data/lib/cxml/order_request_header.rb +11 -10
  20. data/lib/cxml/parser.rb +30 -2
  21. data/lib/cxml/postal_address.rb +1 -1
  22. data/lib/cxml/version.rb +1 -1
  23. data/spec/document_spec.rb +33 -1
  24. data/spec/fixtures/1.2.014-cXML.dtd +4184 -0
  25. data/spec/fixtures/1.2.020-InvoiceDetail.dtd +4590 -0
  26. data/spec/fixtures/1.2.020-cXML.dtd +4917 -0
  27. data/spec/fixtures/1.2.037-InvoiceDetail.dtd +7287 -0
  28. data/spec/fixtures/invoice_taxes_at_line_multiple_taxes.xml +1 -3
  29. data/spec/fixtures/item_in.xml +2 -1
  30. data/spec/fixtures/{order_request.cxml → order_request.xml} +0 -0
  31. data/spec/fixtures/punch_out_order_message_doc.xml +1 -1
  32. data/spec/fixtures/response_status_200.xml +1 -1
  33. data/spec/fixtures/response_status_400.xml +2 -2
  34. data/spec/invoice_detail_request_spec.rb +21 -1
  35. data/spec/item_in_spec.rb +5 -0
  36. data/spec/money_spec.rb +8 -0
  37. data/spec/output/.gitkeep +0 -0
  38. data/spec/punch_out_order_message_spec.rb +5 -0
  39. data/spec/punch_out_setup_request_spec.rb +1 -0
  40. data/spec/purchase_order_request_spec.rb +6 -1
  41. data/spec/sender_spec.rb +6 -0
  42. data/spec/spec_helper.rb +27 -0
  43. metadata +21 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce8099da5b031d246dc2a1b8d30573135a3c35c233a003f337093d427cefbf1c
4
- data.tar.gz: f5c612fcfbd98677ee6c1997a9d99f8b696434cef5a8b9df10cc02019b609451
3
+ metadata.gz: 87772dd26f3194f54c29efa9a993202f79e8102f9bda767bbab87fb43579b9be
4
+ data.tar.gz: 227e6acd1a26406c8eaeabdec57326e5ac85ff5a965ff11c0d24650d786c3f51
5
5
  SHA512:
6
- metadata.gz: 83392422960e9a04d9a1d1a0becf00de7d424e7f79167a204fbfd3a582be98280c9f063658537a4c7a07d4d0931f0167b6265cb5c662cc5b1b0f63f96836a057
7
- data.tar.gz: b55b5c92d354643dcd877016a6ef0ca8f0599d21cd5a3ab419eeddafa9864ef888a8c7c396caf634eb93944c61919f5900eb0ed2097a2b7b6903353046d513f9
6
+ metadata.gz: 812724e750bb2f67a33fa0cd2acef0d03996d394574ea265f40b90c842f1b5244de9ab7ca74a7acedc38a6dbd0d96030933be52a6ef0cf7d2713328117d660bb
7
+ data.tar.gz: 93d10408aaaafaa739ffa6841a83b0ecabe253247cff418684887ac8bfb3fed328e29a649e09d9e1ed043f8aa3d7adfcfbff4104156fb5e80993463d9ed147b6
@@ -15,6 +15,7 @@ jobs:
15
15
  ruby-version: 2.6.x
16
16
  - name: Test with Rake
17
17
  run: |
18
+ sudo apt-get install libxml2-utils
18
19
  gem install bundler
19
20
  bundle install --jobs 4 --retry 3
20
21
  bundle exec rake
@@ -2,6 +2,7 @@ AllCops:
2
2
  Exclude:
3
3
  - Gemfile
4
4
  - vendor/**/*
5
+ NewCops: enable
5
6
 
6
7
  # # Disable checking for block length
7
8
  Metrics/BlockLength:
@@ -54,6 +55,9 @@ Style/Documentation:
54
55
  Style/ClassAndModuleChildren:
55
56
  Enabled: false
56
57
 
58
+ Style/ArrayCoercion:
59
+ Enabled: false
60
+
57
61
  Style/HashEachMethods:
58
62
  Enabled: true
59
63
 
@@ -14,6 +14,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
14
 
15
15
  ---
16
16
 
17
+ ## [0.6.1] - 2020-09-04
18
+ ### Fixed
19
+ - Handle parsing CDATA tags in cXML content (Thanks @CRiva!).
20
+
21
+ ## [0.6.0] - 2020-05-01
22
+ ### Fixed
23
+ - Conform to cXML DTD spec for ordered output of child nodes.
24
+ ### Added
25
+ - Support parsing and validating output against DTDs in specs.
26
+
27
+ ## [0.5.0] - 2020-04-30
28
+ ### Added
29
+ - Support parsing and specifying different DTDs.
30
+
31
+ ## [0.4.0] - 2020-04-20
32
+ ### Changed
33
+ - Return parsed nodes as plain string if they have no attributes and only string content.
34
+
17
35
  ## [0.3.0] - 2020-04-19
18
36
  ### Changed
19
37
  - Drop Nokogiri and XMLSimple in favor of Ox.
@@ -12,6 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.authors = ['Josh Beckman', 'Eleni Chappen']
13
13
  s.email = ['josh@officeluv.com', 'eleni@officeluv.com']
14
14
 
15
+ s.required_ruby_version = '>= 2.4'
15
16
  s.add_dependency('ox', '~> 2.13')
16
17
 
17
18
  s.add_development_dependency('pry', '~> 0.12')
@@ -26,7 +26,7 @@ module CXML
26
26
  def self.logger
27
27
  return @logger if @logger
28
28
 
29
- @logger ||= Logger.new(STDOUT, level: :warn)
29
+ @logger ||= Logger.new($stdout, level: :warn)
30
30
  end
31
31
 
32
32
  def self.logger=(new_logger)
@@ -8,9 +8,12 @@ module CXML
8
8
  iso_country_code
9
9
  ]
10
10
  accessible_nodes %i[
11
- email
12
11
  name
13
12
  postal_address
13
+ email
14
+ phone
15
+ fax
16
+ url
14
17
  ]
15
18
  end
16
19
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CXML
4
+ class Comments < DocumentNode
5
+ accessible_attributes %i[
6
+ xml_lang
7
+ ]
8
+ end
9
+ end
@@ -8,9 +8,12 @@ module CXML
8
8
  address_id
9
9
  ]
10
10
  accessible_nodes %i[
11
- email
12
11
  name
13
12
  postal_address
13
+ email
14
+ phone
15
+ fax
16
+ url
14
17
  ]
15
18
  end
16
19
  end
@@ -41,8 +41,8 @@ module CXML
41
41
  type
42
42
  ]
43
43
  accessible_nodes %i[
44
- shared_secret
45
44
  identity
45
+ shared_secret
46
46
  credential_mac
47
47
  ]
48
48
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CXML
4
+ class DepositAmount < DocumentNode
5
+ accessible_nodes %i[
6
+ money
7
+ ]
8
+ end
9
+ end
@@ -2,11 +2,13 @@
2
2
 
3
3
  module CXML
4
4
  class Document < DocumentNode
5
+ attr_writer :dtd
6
+
5
7
  accessible_attributes %i[
6
- version
7
8
  payload_id
8
- xml_lang
9
9
  timestamp
10
+ version
11
+ xml_lang
10
12
  ]
11
13
  accessible_nodes %i[
12
14
  header
@@ -27,6 +29,10 @@ module CXML
27
29
  @version ||= '1.2.037'
28
30
  end
29
31
 
32
+ def dtd
33
+ @dtd ||= 'cXML'
34
+ end
35
+
30
36
  # Check if document is request
31
37
  # @return [Boolean]
32
38
  def request?
@@ -59,18 +65,17 @@ module CXML
59
65
  'cXML'
60
66
  end
61
67
 
62
- private
63
-
64
68
  def dtd_url
65
- "http://xml.cxml.org/schemas/cXML/#{version}/cXML.dtd"
69
+ "http://xml.cxml.org/schemas/cXML/#{version}/#{dtd}.dtd"
66
70
  end
67
71
 
72
+ private
73
+
68
74
  def ox_doc
69
75
  doc = Ox::Document.new
70
76
  instruct = Ox::Instruct.new(:xml)
71
77
  instruct[:version] = '1.0'
72
78
  instruct[:encoding] = 'UTF-8'
73
- instruct[:standalone] = 'yes'
74
79
  doc << instruct
75
80
  doc << Ox::DocType.new("cXML SYSTEM \"#{dtd_url}\"")
76
81
  doc
@@ -54,7 +54,7 @@ module CXML
54
54
 
55
55
  def to_element
56
56
  element = ox_element
57
- element << content if content
57
+ element << content.to_s if content
58
58
  self.class.nodes.each do |child_node_name|
59
59
  child_value = send(child_node_name)
60
60
  if child_value.is_a?(Array)
@@ -88,12 +88,12 @@ module CXML
88
88
  value_element = Ox::Element.new(camelize(name))
89
89
  if value.is_a? Hash
90
90
  value.each do |value_key, value_val|
91
- next value_element << value_val if value_key == :content
91
+ next value_element << value_val.to_s if value_key == :content
92
92
 
93
93
  value_element[value_key] = value_val
94
94
  end
95
95
  else
96
- value_element << value
96
+ value_element << value.to_s
97
97
  end
98
98
  element << value_element
99
99
  element
@@ -108,7 +108,7 @@ module CXML
108
108
  string_attr = if attr.to_sym == :xml_lang
109
109
  'xml:lang'
110
110
  else
111
- camelize(attr, false)
111
+ camelize(attr, uppercase_first_letter: false)
112
112
  end
113
113
  obj[string_attr] = value
114
114
  end
@@ -127,10 +127,18 @@ module CXML
127
127
  rescue NameError => e
128
128
  raise(e) unless e.to_s.match?(klass)
129
129
 
130
- send("#{key}=", val)
130
+ initialize_attribute_raw(key, val)
131
131
  end
132
132
 
133
- def camelize(string, uppercase_first_letter = true)
133
+ def initialize_attribute_raw(key, val)
134
+ if val.is_a?(Hash) && val.keys == [:content]
135
+ send("#{key}=", val[:content])
136
+ else
137
+ send("#{key}=", val)
138
+ end
139
+ end
140
+
141
+ def camelize(string, uppercase_first_letter: true)
134
142
  string = if uppercase_first_letter
135
143
  string.to_s.sub(/^[a-z\d]*/, &:capitalize)
136
144
  else
@@ -14,28 +14,28 @@ module CXML
14
14
  reference_date
15
15
  ]
16
16
  accessible_nodes %i[
17
- comments
18
- distribution
19
- extrinsics
17
+ unit_of_measure
18
+ unit_price
19
+ invoice_detail_item_reference
20
+ subtotal_amount
21
+ tax
22
+ invoice_detail_line_special_handling
23
+ invoice_detail_line_shipping
20
24
  gross_amount
21
25
  invoice_detail_discount
26
+ net_amount
27
+ distribution
28
+ comments
29
+ extrinsics
22
30
  invoice_detail_item_industry
23
- invoice_detail_item_reference
24
- invoice_detail_line_shipping
25
- invoice_detail_line_special_handling
26
31
  invoice_item_modifications
27
- net_amount
28
32
  packaging
29
33
  price_basis_quantity
30
34
  ship_notice_id_info
31
35
  ship_notice_line_item_reference
32
- subtotal_amount
33
- tax
34
36
  total_allowances
35
37
  total_amount_without_tax
36
38
  total_charges
37
- unit_of_measure
38
- unit_price
39
39
  ]
40
40
 
41
41
  def initialize_extrinsic(value)
@@ -6,12 +6,14 @@ module CXML
6
6
  line_number
7
7
  ]
8
8
  accessible_nodes %i[
9
- description
10
9
  item_id
10
+ description
11
11
  classification
12
12
  manufacturer_part_id
13
13
  manufacturer_name
14
14
  country
15
+ serial_number
16
+ supplier_batch_id
15
17
  ]
16
18
  end
17
19
  end
@@ -11,16 +11,16 @@ module CXML
11
11
  purpose
12
12
  ]
13
13
  accessible_nodes %i[
14
- comments
15
- document_reference
16
- extrinsics
17
14
  invoice_detail_header_indicator
18
15
  invoice_detail_line_indicator
19
- invoice_detail_shipping
20
- invoice_id_info
21
16
  invoice_partners
17
+ document_reference
18
+ invoice_id_info
19
+ invoice_detail_shipping
22
20
  payment_term
23
21
  period
22
+ comments
23
+ extrinsics
24
24
  ]
25
25
 
26
26
  def initialize_invoice_partner(value)
@@ -3,13 +3,15 @@
3
3
  module CXML
4
4
  class InvoiceDetailSummary < DocumentNode
5
5
  accessible_nodes %i[
6
+ subtotal_amount
7
+ tax
8
+ special_handling_amount
9
+ shipping_amount
6
10
  gross_amount
7
11
  invoice_detail_discount
8
12
  net_amount
9
- shipping_amount
10
- special_handling_amount
11
- subtotal_amount
12
- tax
13
+ deposit_amount
14
+ due_amount
13
15
  total_allowances
14
16
  total_amount_without_tax
15
17
  total_charges
@@ -3,10 +3,14 @@
3
3
  module CXML
4
4
  class ItemDetail < DocumentNode
5
5
  accessible_nodes %i[
6
+ unit_price
6
7
  description
7
8
  unit_of_measure
8
- unit_price
9
9
  classification
10
+ manufacturer_part_id
11
+ manufacturer_name
12
+ url
13
+ lead_time
10
14
  extrinsics
11
15
  ]
12
16
 
@@ -23,26 +23,27 @@ module CXML
23
23
  type
24
24
  ]
25
25
  accessible_nodes %i[
26
+ total
27
+ ship_to
26
28
  bill_to
27
- comments
29
+ shipping
30
+ tax
31
+ payment
32
+ payment_term
28
33
  contact
29
- control_keys
30
- delivery_period
34
+ comments
35
+ followup
31
36
  document_reference
37
+ supplier_order_info
32
38
  extrinsics
39
+ control_keys
40
+ delivery_period
33
41
  followup
34
42
  id_reference
35
43
  legal_identity
36
44
  order_request_header_industry
37
45
  organizational_unit
38
- payment
39
- payment_term
40
- ship_to
41
- shipping
42
- supplier_order_info
43
- tax
44
46
  terms_of_delivery
45
- total
46
47
  ]
47
48
 
48
49
  def type
@@ -11,27 +11,55 @@ module CXML
11
11
 
12
12
  def parse
13
13
  @parsed_data = node_to_hash document
14
+ if dtd_url
15
+ @parsed_data[:version] = version
16
+ @parsed_data[:dtd] = dtd
17
+ end
18
+ @parsed_data
14
19
  end
15
20
 
16
21
  def document
17
22
  doc = Ox.load(data)
23
+ @doc_type_string = doc.nodes.detect do |node|
24
+ node.value&.match?(/^cXML /)
25
+ end&.value
18
26
  doc.nodes.detect do |node|
19
27
  node.value&.match?(/^cxml$/i)
20
28
  end || doc
21
29
  end
22
30
 
31
+ def version
32
+ dtd_url&.match(%r{cXML/(.*)/.*\.dtd})&.to_a&.last
33
+ end
34
+
35
+ def dtd
36
+ dtd_url&.match(%r{cXML/.*/(.*)\.dtd})&.to_a&.last
37
+ end
38
+
39
+ def dtd_url
40
+ return nil unless @doc_type_string
41
+
42
+ @doc_type_string.match(/http.*\.dtd/)&.to_a&.first
43
+ end
44
+
23
45
  private
24
46
 
25
47
  def node_to_hash(node)
48
+ return node.value if node.is_a?(Ox::CData)
26
49
  return node if node.is_a? String
50
+ return node.nodes.first if node.nodes.all?(String) && node.attributes.empty?
51
+
52
+ transform_node_to_hash node
53
+ end
27
54
 
28
- hash = node.attributes || {}
55
+ def transform_node_to_hash(node)
56
+ hash = node.attributes
29
57
  hash.transform_keys!(&method(:underscore_key))
30
58
  node.nodes.reduce(hash) do |acc, child_node|
31
59
  next acc if child_node.is_a?(Ox::Comment)
32
60
 
33
61
  node_hash = {}
34
- name = child_node.is_a?(String) ? :content : child_node.value
62
+ name = child_node.is_a?(String) || child_node.is_a?(Ox::CData) ? :content : child_node.value
35
63
  node_hash[underscore_key(name)] = node_to_hash(child_node)
36
64
  acc.merge(node_hash) do |_key, val1, val2|
37
65
  [val1, val2].flatten