cxml-ruby 0.4.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) 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 +20 -2
  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/country_code.rb +9 -0
  11. data/lib/cxml/credential.rb +1 -1
  12. data/lib/cxml/deposit_amount.rb +9 -0
  13. data/lib/cxml/document.rb +11 -6
  14. data/lib/cxml/document_node.rb +11 -5
  15. data/lib/cxml/invoice_detail_item.rb +11 -11
  16. data/lib/cxml/invoice_detail_item_reference.rb +3 -1
  17. data/lib/cxml/invoice_detail_request_header.rb +5 -5
  18. data/lib/cxml/invoice_detail_summary.rb +6 -4
  19. data/lib/cxml/item_detail.rb +5 -1
  20. data/lib/cxml/order_request_header.rb +11 -10
  21. data/lib/cxml/parser.rb +29 -2
  22. data/lib/cxml/phone.rb +13 -0
  23. data/lib/cxml/postal_address.rb +1 -1
  24. data/lib/cxml/telephone_number.rb +11 -0
  25. data/lib/cxml/version.rb +1 -1
  26. data/spec/document_spec.rb +53 -1
  27. data/spec/fixtures/1.2.014-cXML.dtd +4184 -0
  28. data/spec/fixtures/1.2.020-InvoiceDetail.dtd +4590 -0
  29. data/spec/fixtures/1.2.020-cXML.dtd +4917 -0
  30. data/spec/fixtures/1.2.037-InvoiceDetail.dtd +7287 -0
  31. data/spec/fixtures/document_node_with_unknown_attribute.xml +7 -0
  32. data/spec/fixtures/invoice_taxes_at_line_multiple_taxes.xml +1 -3
  33. data/spec/fixtures/item_in.xml +2 -1
  34. data/spec/fixtures/{order_request.cxml → order_request.xml} +0 -0
  35. data/spec/fixtures/punch_out_order_message_doc.xml +1 -1
  36. data/spec/fixtures/punch_out_setup_request_doc_with_ship_to.xml +7 -0
  37. data/spec/fixtures/response_status_200.xml +1 -1
  38. data/spec/fixtures/response_status_400.xml +2 -2
  39. data/spec/invoice_detail_request_spec.rb +21 -1
  40. data/spec/item_in_spec.rb +5 -0
  41. data/spec/output/.gitkeep +0 -0
  42. data/spec/punch_out_order_message_spec.rb +5 -0
  43. data/spec/punch_out_setup_request_spec.rb +2 -0
  44. data/spec/purchase_order_request_spec.rb +6 -1
  45. data/spec/sender_spec.rb +6 -0
  46. data/spec/spec_helper.rb +27 -0
  47. metadata +22 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38b4bf64619173383fb1d6bbb76d1877bc70746752d884b6f9d79f967fcb70c0
4
- data.tar.gz: 2f42907da9ebe23561a44f0a39ac1143e32660b465c5b19cea13dbbcd1328ee4
3
+ metadata.gz: 125ea774cb74155d9512b222d1d47a06945558d87d441dd1491d53caf8e9ca9a
4
+ data.tar.gz: 986ef57d03cf1706eedf08618dc5b52931171f42b147e1d81dfabaf6a6c32df8
5
5
  SHA512:
6
- metadata.gz: 18ab3bad5fb8231724c14e29eb19c71ed1abdca1c9efd45832de27d46125070a705790a7813926f2f21273fdc010238de30d2405a251bcc28af389f5e075db76
7
- data.tar.gz: 5b3206398d10770f12215be79a4f6877db2d6efafc9679e31cc08a756384247466c90cd061b3f044befbfc39e631f35cb0029f83792dce69220e9ca75e05347d
6
+ metadata.gz: ebfe04583c735c1635a14d89cc49d4f1494b1d49b79efa7d072d261b1d91d0831a5f5d644fcfde75c54c572528fcca499288b4393dfdf86b33958d065b167d3b
7
+ data.tar.gz: 3a97f6bcc16715eae27f2ef9a8f4ade47281f092116458fc784224bd46d97cdae1f29e65234d07946ef7027accdce0e91dd9635003f8064c8b4af7730d0c1857
@@ -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
data/.rubocop.yml CHANGED
@@ -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
 
data/CHANGELOG.md CHANGED
@@ -14,9 +14,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
14
 
15
15
  ---
16
16
 
17
- ## [0.4.1] - 2020-04-20
17
+ ## [0.8.0] - 2021-04-25
18
18
  ### Changed
19
- - Cast all node content values to strings before passing to Ox serializer.
19
+ - Support continued parsing of child nodes if unknown attribute is encountered with `raise_unknown_elements` set to `false`.
20
+
21
+ ## [0.7.0] - 2021-01-12
22
+ ### Added
23
+ - Support parsing Phone tags.
24
+
25
+ ## [0.6.1] - 2020-09-04
26
+ ### Fixed
27
+ - Handle parsing CDATA tags in cXML content (Thanks @CRiva!).
28
+
29
+ ## [0.6.0] - 2020-05-01
30
+ ### Fixed
31
+ - Conform to cXML DTD spec for ordered output of child nodes.
32
+ ### Added
33
+ - Support parsing and validating output against DTDs in specs.
34
+
35
+ ## [0.5.0] - 2020-04-30
36
+ ### Added
37
+ - Support parsing and specifying different DTDs.
20
38
 
21
39
  ## [0.4.0] - 2020-04-20
22
40
  ### Changed
data/cxml-ruby.gemspec CHANGED
@@ -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')
data/lib/cxml-ruby.rb CHANGED
@@ -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)
data/lib/cxml/address.rb CHANGED
@@ -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
data/lib/cxml/contact.rb CHANGED
@@ -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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CXML
4
+ class CountryCode < DocumentNode
5
+ accessible_attributes %i[
6
+ iso_country_code
7
+ ]
8
+ end
9
+ 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
data/lib/cxml/document.rb CHANGED
@@ -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
@@ -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
@@ -121,9 +121,7 @@ module CXML
121
121
  klass = "CXML::#{camelize(key)}"
122
122
  send("#{key}=", Object.const_get(klass).new(val))
123
123
  rescue NoMethodError => e
124
- raise(UnknownAttributeError, e) if CXML.raise_unknown_elements
125
-
126
- CXML.logger.warn(e)
124
+ handle_unknown_elements_error(e)
127
125
  rescue NameError => e
128
126
  raise(e) unless e.to_s.match?(klass)
129
127
 
@@ -136,9 +134,11 @@ module CXML
136
134
  else
137
135
  send("#{key}=", val)
138
136
  end
137
+ rescue NoMethodError => e
138
+ handle_unknown_elements_error(e)
139
139
  end
140
140
 
141
- def camelize(string, uppercase_first_letter = true)
141
+ def camelize(string, uppercase_first_letter: true)
142
142
  string = if uppercase_first_letter
143
143
  string.to_s.sub(/^[a-z\d]*/, &:capitalize)
144
144
  else
@@ -150,5 +150,11 @@ module CXML
150
150
  "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}"
151
151
  end.gsub('/', '::')
152
152
  end
153
+
154
+ def handle_unknown_elements_error(error)
155
+ raise(UnknownAttributeError, error) if CXML.raise_unknown_elements
156
+
157
+ CXML.logger.warn(error)
158
+ end
153
159
  end
154
160
  end
@@ -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
data/lib/cxml/parser.rb CHANGED
@@ -11,28 +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
- def node_to_hash(node) # rubocop:disable Metrics/AbcSize
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
27
50
  return node.nodes.first if node.nodes.all?(String) && node.attributes.empty?
28
51
 
52
+ transform_node_to_hash node
53
+ end
54
+
55
+ def transform_node_to_hash(node)
29
56
  hash = node.attributes
30
57
  hash.transform_keys!(&method(:underscore_key))
31
58
  node.nodes.reduce(hash) do |acc, child_node|
32
59
  next acc if child_node.is_a?(Ox::Comment)
33
60
 
34
61
  node_hash = {}
35
- 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
36
63
  node_hash[underscore_key(name)] = node_to_hash(child_node)
37
64
  acc.merge(node_hash) do |_key, val1, val2|
38
65
  [val1, val2].flatten