adf_builder 1.1.0 → 1.2.1

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: 24b99369efbb491a84bd7a472de00624914228c6de3334e806ab46c7adbb9fa9
4
+ data.tar.gz: c9b0e631fa1118938a976d80e3be092d958c7fc35b7d2fe547bb291c4d4bbff9
5
5
  SHA512:
6
- metadata.gz: b868564c56972b958839382c2d5c5706ce1b7864ea13e2c7eb38dddccb82f6481a66bc471f679f79589533a8fa9fd29897a387d53d4426ee8bc5490242912b45
7
- data.tar.gz: 0c25a32fec2c3015e402fdbe26e0c2af23a2a91ea64840097a7a86acd7c88c4e89a809362b1ea35b8eebaa112dc130534b82f1a3647173ab5f0d1f3aa2b19fa0
6
+ metadata.gz: 8e46cdda771a5c61882464d1e21a4e1117d156c13603877a7cb35e50bc0cd45ef480236a8aa2add261b983e5ed11142f655e9611e6f97cf04c7944305b6c3640
7
+ data.tar.gz: 4560e7057db7c62d8a6bdc90e3b5413c99ae273c5f70a1fd66d621e3a8c3d574ee80e619797c0faf32ce6a6c6fbfd33976fadc3cbda26d66c1a24e673b911447
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [1.2.1] - 2026-01-19
2
+ - **Strict Validations**: Added `presence` validation for `Vehicle` (year, make, model required).
3
+
4
+ ## [1.2.0] - 2026-01-19
5
+ - **Strict Validations**: Added validation for `Vehicle` condition, `Option` weighting (range), `Finance` method, and required `ID` source.
6
+
1
7
  ## [1.1.0] - 2026-01-19
2
8
  - **Feature Complete**: Implemented all ADF 1.0 nodes and attributes including `Vendor`, `Provider`, and complex `Vehicle` tags (`Finance`, `Option`, `Odometer`, `ColorCombination`, `ImageTag`, `Price`).
3
9
  - **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.0)
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,6 +26,47 @@ 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
+
29
70
  class Price < Node
30
71
  validates_inclusion_of :type, in: %i[quote offer msrp invoice call appraisal asking]
31
72
  validates_inclusion_of :delta, in: %i[absolute relative percentage]
@@ -76,7 +117,8 @@ module AdfBuilder
76
117
  end
77
118
 
78
119
  def method(value)
79
- add_child(GenericNode.new(:method, {}, value))
120
+ remove_children(:method)
121
+ add_child(FinanceMethod.new(value))
80
122
  end
81
123
 
82
124
  def amount(value, type: :total, limit: :maximum, currency: nil)
@@ -107,7 +149,7 @@ module AdfBuilder
107
149
  end
108
150
 
109
151
  def weighting(value)
110
- add_child(GenericNode.new(:weighting, {}, value))
152
+ add_child(Weighting.new(value))
111
153
  end
112
154
 
113
155
  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.1"
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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - marcus.salinas