adf_builder 1.1.0 → 1.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ded72167a643d102957459b6d439bbaae8f01833303a3eca082ce4a6f0e264dd
4
- data.tar.gz: 64486e697bd4feefa5fdbc8c6cbe01d3d0492878225748ffdf3c8ed1650b8b09
3
+ metadata.gz: f5c6497310cd559356bd6e358243d2dd2ed5d2a859330fe403ae959b32592b0b
4
+ data.tar.gz: 77e66f74d658adc2022d401d85a037549f8f92c91d984c265db337a33c05353c
5
5
  SHA512:
6
- metadata.gz: b868564c56972b958839382c2d5c5706ce1b7864ea13e2c7eb38dddccb82f6481a66bc471f679f79589533a8fa9fd29897a387d53d4426ee8bc5490242912b45
7
- data.tar.gz: 0c25a32fec2c3015e402fdbe26e0c2af23a2a91ea64840097a7a86acd7c88c4e89a809362b1ea35b8eebaa112dc130534b82f1a3647173ab5f0d1f3aa2b19fa0
6
+ metadata.gz: 8aba468994e77c3af346c7f6dccbdd4eed16fd858ccf6206226c90d97a5e1126d75bc5083440d40ce1ac3233d2b6f0395b899fcce70afc76ccba7691867e779f
7
+ data.tar.gz: 215970d63f001f71e21c55651368c9ebab28dafd9e5f26d19519dbff6c897ed4859443623f01d3a99a5c014743d00f8e74b351eda0b371d28e578edc744d5582
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [1.2.2] - 2026-01-19
2
+ - **Strict Validations**: Added ISO 4217 currency validation for `Price`, `Amount`, and `Balance`.
3
+
4
+ ## [1.2.1] - 2026-01-19
5
+ - **Strict Validations**: Added `presence` validation for `Vehicle` (year, make, model required).
6
+
7
+ ## [1.2.0] - 2026-01-19
8
+ - **Strict Validations**: Added validation for `Vehicle` condition, `Option` weighting (range), `Finance` method, and required `ID` source.
9
+
1
10
  ## [1.1.0] - 2026-01-19
2
11
  - **Feature Complete**: Implemented all ADF 1.0 nodes and attributes including `Vendor`, `Provider`, and complex `Vehicle` tags (`Finance`, `Option`, `Odometer`, `ColorCombination`, `ImageTag`, `Price`).
3
12
  - **Singular Field logic**: Methods for singular fields (e.g. `vehicle.year`) now correctly replace existing values instead of appending.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- adf_builder (1.1.0)
4
+ adf_builder (1.2.1)
5
5
  ox (~> 2.14)
6
6
 
7
7
  GEM
@@ -3,12 +3,14 @@
3
3
  module AdfBuilder
4
4
  module Nodes
5
5
  class Id < Node
6
- def initialize(value, sequence: nil, source: nil)
6
+ def initialize(value, source:, sequence: nil)
7
7
  super()
8
+ raise ArgumentError, "Source is required" if source.nil?
9
+
8
10
  @tag_name = :id
9
11
  @value = value
10
12
  @attributes[:sequence] = sequence if sequence
11
- @attributes[:source] = source if source
13
+ @attributes[:source] = source
12
14
  end
13
15
  end
14
16
 
@@ -5,6 +5,7 @@ module AdfBuilder
5
5
  class Vehicle < Node
6
6
  validates_inclusion_of :status, in: %i[new used]
7
7
  validates_inclusion_of :interest, in: %i[buy lease sell trade-in test-drive]
8
+ validates_presence_of :year, :make, :model
8
9
 
9
10
  def initialize
10
11
  super
@@ -14,13 +15,19 @@ module AdfBuilder
14
15
  end
15
16
 
16
17
  # Simple Text Elements (Singular)
17
- %i[year make model vin stock trim doors bodystyle transmission condition pricecomments comments].each do |tag|
18
+ # Simple Text Elements (Singular)
19
+ %i[year make model vin stock trim doors bodystyle transmission pricecomments comments].each do |tag|
18
20
  define_method(tag) do |value|
19
21
  remove_children(tag)
20
22
  add_child(GenericNode.new(tag, {}, value))
21
23
  end
22
24
  end
23
25
 
26
+ def condition(value)
27
+ remove_children(:condition)
28
+ add_child(Condition.new(value))
29
+ end
30
+
24
31
  def interest(value)
25
32
  @attributes[:interest] = value
26
33
  end
@@ -26,10 +26,62 @@ module AdfBuilder
26
26
  end
27
27
  end
28
28
 
29
+ class Condition < Node
30
+ VALID_VALUES = %w[excellent good fair poor unknown].freeze
31
+
32
+ def initialize(value)
33
+ super()
34
+ @tag_name = :condition
35
+ unless VALID_VALUES.include?(value.to_s.downcase)
36
+ raise AdfBuilder::Error, "Invalid condition: #{value}. Must be one of: #{VALID_VALUES.join(", ")}"
37
+ end
38
+
39
+ @value = value
40
+ end
41
+ end
42
+
43
+ class Weighting < Node
44
+ def initialize(value)
45
+ super()
46
+ @tag_name = :weighting
47
+ int_val = value.to_i
48
+ unless int_val.between?(-100, 100)
49
+ raise AdfBuilder::Error, "Weighting must be between -100 and 100. Got: #{value}"
50
+ end
51
+
52
+ @value = value
53
+ end
54
+ end
55
+
56
+ class FinanceMethod < Node
57
+ VALID_VALUES = %w[cash finance lease].freeze
58
+
59
+ def initialize(value)
60
+ super()
61
+ @tag_name = :method
62
+ unless VALID_VALUES.include?(value.to_s.downcase)
63
+ raise AdfBuilder::Error, "Invalid finance method: #{value}. Must be one of: #{VALID_VALUES.join(", ")}"
64
+ end
65
+
66
+ @value = value
67
+ end
68
+ end
69
+
70
+ # Common ISO 4217 codes (Top valid ones per spec/common usage)
71
+ # Keeping it as a constant for reuse.
72
+ # This list can be expanded but covers major currencies.
73
+ ISO_4217 = %w[
74
+ USD EUR GBP JPY AUD CAD CHF CNY SEK NZD
75
+ MXN SGD HKD NOK KRW TRY RUB INR BRL ZAR
76
+ DKK PLN TWD THB IDR HUF CZK ILS CLP PHP
77
+ AED COP SAR MYR RON PEN VND NGN
78
+ ].freeze
79
+
29
80
  class Price < Node
30
81
  validates_inclusion_of :type, in: %i[quote offer msrp invoice call appraisal asking]
31
82
  validates_inclusion_of :delta, in: %i[absolute relative percentage]
32
83
  validates_inclusion_of :relativeto, in: %i[msrp invoice]
84
+ validates_inclusion_of :currency, in: ISO_4217
33
85
 
34
86
  def initialize(value, type: :quote, currency: nil, delta: nil, relativeto: nil, source: nil)
35
87
  super()
@@ -46,6 +98,7 @@ module AdfBuilder
46
98
  class Amount < Node
47
99
  validates_inclusion_of :type, in: %i[downpayment monthly total]
48
100
  validates_inclusion_of :limit, in: %i[maximum minimum exact]
101
+ validates_inclusion_of :currency, in: ISO_4217
49
102
 
50
103
  def initialize(value, type: :total, limit: :maximum, currency: nil)
51
104
  super()
@@ -59,6 +112,7 @@ module AdfBuilder
59
112
 
60
113
  class Balance < Node
61
114
  validates_inclusion_of :type, in: %i[finance residual]
115
+ validates_inclusion_of :currency, in: ISO_4217
62
116
 
63
117
  def initialize(value, type: :finance, currency: nil)
64
118
  super()
@@ -76,7 +130,8 @@ module AdfBuilder
76
130
  end
77
131
 
78
132
  def method(value)
79
- add_child(GenericNode.new(:method, {}, value))
133
+ remove_children(:method)
134
+ add_child(FinanceMethod.new(value))
80
135
  end
81
136
 
82
137
  def amount(value, type: :total, limit: :maximum, currency: nil)
@@ -107,7 +162,7 @@ module AdfBuilder
107
162
  end
108
163
 
109
164
  def weighting(value)
110
- add_child(GenericNode.new(:weighting, {}, value))
165
+ add_child(Weighting.new(value))
111
166
  end
112
167
 
113
168
  def price(value, **attrs)
@@ -4,8 +4,10 @@ require "ox"
4
4
 
5
5
  module AdfBuilder
6
6
  class Serializer
7
- def self.to_xml(node)
8
- new(node).to_xml
7
+ class << self
8
+ def to_xml(root_node)
9
+ new(root_node).to_xml
10
+ end
9
11
  end
10
12
 
11
13
  def initialize(root_node)
@@ -13,7 +15,10 @@ module AdfBuilder
13
15
  end
14
16
 
15
17
  def to_xml
16
- doc = Ox::Document.new
18
+ # Validate the entire tree before serializing to ensure data integrity
19
+ @root_node.validate!
20
+
21
+ doc = Ox::Document.new(version: "1.0")
17
22
 
18
23
  # XML Instruction
19
24
  instruct = Ox::Instruct.new(:xml)
@@ -12,6 +12,13 @@ module AdfBuilder
12
12
  @validations << { type: :inclusion, attribute: attribute, in: binding.local_variable_get(:in) }
13
13
  end
14
14
 
15
+ def validates_presence_of(*attributes)
16
+ @validations ||= []
17
+ attributes.each do |attr|
18
+ @validations << { type: :presence, attribute: attr }
19
+ end
20
+ end
21
+
15
22
  def validations
16
23
  @validations || []
17
24
  end
@@ -19,15 +26,22 @@ module AdfBuilder
19
26
 
20
27
  def validate!
21
28
  self.class.validations.each do |validation|
22
- value = @attributes[validation[:attribute]]
23
- next if value.nil? # Allow nil unless presence validation is added
24
-
25
- next unless validation[:type] == :inclusion
29
+ if validation[:type] == :inclusion
30
+ value = @attributes[validation[:attribute]]
31
+ next if value.nil?
26
32
 
27
- allowed = validation[:in]
28
- unless allowed.include?(value)
29
- raise AdfBuilder::Error,
30
- "Invalid value for #{validation[:attribute]}: #{value}. Allowed: #{allowed.join(", ")}"
33
+ allowed = validation[:in]
34
+ unless allowed.include?(value)
35
+ raise AdfBuilder::Error,
36
+ "Invalid value for #{validation[:attribute]}: #{value}. Allowed: #{allowed.join(", ")}"
37
+ end
38
+ elsif validation[:type] == :presence
39
+ # Check children for a node with tag_name == attribute
40
+ target_tag = validation[:attribute]
41
+ found = @children.any? { |c| c.tag_name == target_tag }
42
+ unless found
43
+ raise AdfBuilder::Error, "Missing required Element: #{target_tag} in #{tag_name || self.class.name}"
44
+ end
31
45
  end
32
46
  end
33
47
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AdfBuilder
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adf_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - marcus.salinas