adf_builder 1.0.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 +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +23 -1
- data/Gemfile.lock +1 -1
- data/lib/adf_builder/nodes/customer.rb +32 -32
- data/lib/adf_builder/nodes/node.rb +7 -2
- data/lib/adf_builder/nodes/prospect.rb +12 -0
- data/lib/adf_builder/nodes/provider.rb +48 -0
- data/lib/adf_builder/nodes/shared.rb +93 -0
- data/lib/adf_builder/nodes/vehicle.rb +63 -6
- data/lib/adf_builder/nodes/vehicle_nodes.rb +179 -0
- data/lib/adf_builder/nodes/vendor.rb +33 -0
- data/lib/adf_builder/serializer.rb +13 -5
- data/lib/adf_builder/validations.rb +22 -8
- data/lib/adf_builder/version.rb +1 -1
- data/lib/adf_builder.rb +8 -150
- metadata +5 -13
- data/lib/adf_builder/base/base.rb +0 -13
- data/lib/adf_builder/base/prospect.rb +0 -34
- data/lib/adf_builder/base/request_date.rb +0 -22
- data/lib/adf_builder/customer/customer.rb +0 -50
- data/lib/adf_builder/customer/timeframe.rb +0 -44
- data/lib/adf_builder/provider/provider.rb +0 -53
- data/lib/adf_builder/shared/contact.rb +0 -44
- data/lib/adf_builder/shared/id.rb +0 -18
- data/lib/adf_builder/vehicles/colorcombinations.rb +0 -30
- data/lib/adf_builder/vehicles/price.rb +0 -43
- data/lib/adf_builder/vehicles/vehicles.rb +0 -123
- data/lib/adf_builder/vendor/vendor.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24b99369efbb491a84bd7a472de00624914228c6de3334e806ab46c7adbb9fa9
|
|
4
|
+
data.tar.gz: c9b0e631fa1118938a976d80e3be092d958c7fc35b7d2fe547bb291c4d4bbff9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e46cdda771a5c61882464d1e21a4e1117d156c13603877a7cb35e50bc0cd45ef480236a8aa2add261b983e5ed11142f655e9611e6f97cf04c7944305b6c3640
|
|
7
|
+
data.tar.gz: 4560e7057db7c62d8a6bdc90e3b5413c99ae273c5f70a1fd66d621e3a8c3d574ee80e619797c0faf32ce6a6c6fbfd33976fadc3cbda26d66c1a24e673b911447
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
|
-
## [
|
|
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
|
+
|
|
7
|
+
## [1.1.0] - 2026-01-19
|
|
8
|
+
- **Feature Complete**: Implemented all ADF 1.0 nodes and attributes including `Vendor`, `Provider`, and complex `Vehicle` tags (`Finance`, `Option`, `Odometer`, `ColorCombination`, `ImageTag`, `Price`).
|
|
9
|
+
- **Singular Field logic**: Methods for singular fields (e.g. `vehicle.year`) now correctly replace existing values instead of appending.
|
|
10
|
+
- **Removed Legacy Code**: Cleaned up deprecated legacy implementation directories.
|
|
11
|
+
|
|
12
|
+
## [1.0.0] - 2026-01-19
|
|
13
|
+
- **MAJOR OVERHAUL**: Complete rewrtie of the library architecture.
|
|
14
|
+
- **New Block-based DSL**: Intuitive API for building ADF documents (`AdfBuilder.build { vehicle { ... } }`).
|
|
15
|
+
- **Validation**: Strict enforcement of ADF enumerations and structure (e.g. `vehicle status: :new`).
|
|
16
|
+
- **Editing**: New `AdfBuilder.tree` method for programmatic modifications after construction.
|
|
17
|
+
- **Robustness**: Complete rewrite of XML generation using robust heuristics and strict `Ox` serialization.
|
|
18
|
+
- **Compatibility**: Verified for Ruby 3.4.x.
|
|
19
|
+
- **Features**:
|
|
20
|
+
- Support for multiple vehicles/customers.
|
|
21
|
+
- Support for singular vs multiple item logic.
|
|
22
|
+
- Dynamic support for arbitrary/custom tags (`method_missing`).
|
|
23
|
+
- Automatic handling of XML attributes vs simple elements.
|
|
2
24
|
|
|
3
25
|
## [0.4.0] - 2026-01-19
|
|
4
26
|
- Modernized dependencies
|
data/Gemfile.lock
CHANGED
|
@@ -3,56 +3,56 @@
|
|
|
3
3
|
module AdfBuilder
|
|
4
4
|
module Nodes
|
|
5
5
|
class Customer < Node
|
|
6
|
+
def initialize
|
|
7
|
+
super
|
|
8
|
+
@tag_name = :customer
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
def contact(&block)
|
|
12
|
+
remove_children(:contact)
|
|
7
13
|
contact = Contact.new
|
|
8
14
|
contact.instance_eval(&block) if block_given?
|
|
9
15
|
add_child(contact)
|
|
10
16
|
end
|
|
11
|
-
end
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
add_child(name_node)
|
|
18
|
+
def id(value, sequence: nil, source: nil)
|
|
19
|
+
# id* is multiple
|
|
20
|
+
add_child(Id.new(value, sequence: sequence, source: source))
|
|
17
21
|
end
|
|
18
22
|
|
|
19
|
-
def
|
|
20
|
-
|
|
23
|
+
def timeframe(&block)
|
|
24
|
+
remove_children(:timeframe)
|
|
25
|
+
tf = Timeframe.new
|
|
26
|
+
tf.instance_eval(&block) if block_given?
|
|
27
|
+
add_child(tf)
|
|
21
28
|
end
|
|
22
29
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
add_child(
|
|
30
|
+
def comments(value)
|
|
31
|
+
remove_children(:comments)
|
|
32
|
+
add_child(GenericNode.new(:comments, {}, value))
|
|
26
33
|
end
|
|
27
34
|
end
|
|
28
35
|
|
|
29
|
-
class
|
|
30
|
-
def initialize
|
|
31
|
-
super
|
|
32
|
-
@
|
|
33
|
-
@attributes[:part] = part if part
|
|
34
|
-
@attributes[:type] = type if type
|
|
36
|
+
class Timeframe < Node
|
|
37
|
+
def initialize
|
|
38
|
+
super
|
|
39
|
+
@tag_name = :timeframe
|
|
35
40
|
end
|
|
36
|
-
attr_reader :value
|
|
37
|
-
end
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
def description(value)
|
|
43
|
+
remove_children(:description)
|
|
44
|
+
add_child(GenericNode.new(:description, {}, value))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def earliestdate(value)
|
|
48
|
+
remove_children(:earliestdate)
|
|
49
|
+
add_child(GenericNode.new(:earliestdate, {}, value))
|
|
44
50
|
end
|
|
45
|
-
attr_reader :value
|
|
46
|
-
end
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
super()
|
|
52
|
-
@tag_name = tag_name
|
|
53
|
-
@value = value
|
|
52
|
+
def latestdate(value)
|
|
53
|
+
remove_children(:latestdate)
|
|
54
|
+
add_child(GenericNode.new(:latestdate, {}, value))
|
|
54
55
|
end
|
|
55
|
-
attr_reader :value, :tag_name
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
end
|
|
@@ -5,17 +5,23 @@ module AdfBuilder
|
|
|
5
5
|
class Node
|
|
6
6
|
include AdfBuilder::Validations
|
|
7
7
|
|
|
8
|
-
attr_reader :children, :attributes
|
|
8
|
+
attr_reader :children, :attributes, :value, :tag_name
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
@children = []
|
|
12
12
|
@attributes = {}
|
|
13
|
+
@value = nil
|
|
14
|
+
@tag_name = nil
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def add_child(node)
|
|
16
18
|
@children << node
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
def remove_children(tag_name)
|
|
22
|
+
@children.reject! { |c| c.tag_name == tag_name }
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
def to_xml
|
|
20
26
|
Serializer.to_xml(self)
|
|
21
27
|
end
|
|
@@ -57,7 +63,6 @@ module AdfBuilder
|
|
|
57
63
|
@attributes = attributes
|
|
58
64
|
@value = value
|
|
59
65
|
end
|
|
60
|
-
attr_reader :tag_name, :value
|
|
61
66
|
end
|
|
62
67
|
|
|
63
68
|
class Root < Node
|
|
@@ -19,6 +19,18 @@ module AdfBuilder
|
|
|
19
19
|
add_child(customer)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def vendor(&block)
|
|
23
|
+
vendor = Vendor.new
|
|
24
|
+
vendor.instance_eval(&block) if block_given?
|
|
25
|
+
add_child(vendor)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def provider(&block)
|
|
29
|
+
provider = Provider.new
|
|
30
|
+
provider.instance_eval(&block) if block_given?
|
|
31
|
+
add_child(provider)
|
|
32
|
+
end
|
|
33
|
+
|
|
22
34
|
# Helpers for Editing
|
|
23
35
|
def vehicles
|
|
24
36
|
@children.select { |c| c.is_a?(Vehicle) }
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdfBuilder
|
|
4
|
+
module Nodes
|
|
5
|
+
class Provider < Node
|
|
6
|
+
def initialize
|
|
7
|
+
super
|
|
8
|
+
@tag_name = :provider
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def id(value, sequence: nil, source: nil)
|
|
12
|
+
add_child(Id.new(value, sequence: sequence, source: source))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name(value, part: nil, type: nil)
|
|
16
|
+
remove_children(:name)
|
|
17
|
+
add_child(Name.new(value, part: part, type: type))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def service(value)
|
|
21
|
+
remove_children(:service)
|
|
22
|
+
add_child(GenericNode.new(:service, {}, value))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def url(value)
|
|
26
|
+
remove_children(:url)
|
|
27
|
+
add_child(GenericNode.new(:url, {}, value))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def email(value, preferredcontact: nil)
|
|
31
|
+
remove_children(:email)
|
|
32
|
+
add_child(Email.new(value, preferredcontact: preferredcontact))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def phone(value, type: nil, time: nil, preferredcontact: nil)
|
|
36
|
+
remove_children(:phone)
|
|
37
|
+
add_child(Phone.new(value, type: type, time: time, preferredcontact: preferredcontact))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def contact(primary_contact: false, &block)
|
|
41
|
+
remove_children(:contact)
|
|
42
|
+
contact = Contact.new(primary_contact: primary_contact)
|
|
43
|
+
contact.instance_eval(&block) if block_given?
|
|
44
|
+
add_child(contact)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdfBuilder
|
|
4
|
+
module Nodes
|
|
5
|
+
class Id < Node
|
|
6
|
+
def initialize(value, source:, sequence: nil)
|
|
7
|
+
super()
|
|
8
|
+
raise ArgumentError, "Source is required" if source.nil?
|
|
9
|
+
|
|
10
|
+
@tag_name = :id
|
|
11
|
+
@value = value
|
|
12
|
+
@attributes[:sequence] = sequence if sequence
|
|
13
|
+
@attributes[:source] = source
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Phone < Node
|
|
18
|
+
def initialize(value, type: nil, time: nil, preferredcontact: nil)
|
|
19
|
+
super()
|
|
20
|
+
@tag_name = :phone
|
|
21
|
+
@value = value
|
|
22
|
+
@attributes[:type] = type if type
|
|
23
|
+
@attributes[:time] = time if time
|
|
24
|
+
@attributes[:preferredcontact] = preferredcontact if preferredcontact
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Email < Node
|
|
29
|
+
def initialize(value, preferredcontact: nil)
|
|
30
|
+
super()
|
|
31
|
+
@tag_name = :email
|
|
32
|
+
@value = value
|
|
33
|
+
@attributes[:preferredcontact] = preferredcontact if preferredcontact
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class Name < Node
|
|
38
|
+
def initialize(value, part: nil, type: nil)
|
|
39
|
+
super()
|
|
40
|
+
@tag_name = :name
|
|
41
|
+
@value = value
|
|
42
|
+
@attributes[:part] = part if part
|
|
43
|
+
@attributes[:type] = type if type
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class Address < Node
|
|
48
|
+
def initialize(type: nil)
|
|
49
|
+
super()
|
|
50
|
+
@tag_name = :address
|
|
51
|
+
@attributes[:type] = type if type
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def street(value, line: nil)
|
|
55
|
+
node = GenericNode.new(:street, { line: line }.compact, value)
|
|
56
|
+
add_child(node)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Simple elements
|
|
60
|
+
%i[apartment city regioncode postalcode country].each do |tag|
|
|
61
|
+
define_method(tag) do |value|
|
|
62
|
+
add_child(GenericNode.new(tag, {}, value))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Contact < Node
|
|
68
|
+
def initialize(primary_contact: false)
|
|
69
|
+
super()
|
|
70
|
+
@tag_name = :contact
|
|
71
|
+
# primary_contact might be useful for logic but not an attribute
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def name(value, part: nil, type: nil)
|
|
75
|
+
add_child(Name.new(value, part: part, type: type))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def email(value, preferredcontact: nil)
|
|
79
|
+
add_child(Email.new(value, preferredcontact: preferredcontact))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def phone(value, type: nil, time: nil, preferredcontact: nil)
|
|
83
|
+
add_child(Phone.new(value, type: type, time: time, preferredcontact: preferredcontact))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def address(type: nil, &block)
|
|
87
|
+
addr = Address.new(type: type)
|
|
88
|
+
addr.instance_eval(&block) if block_given?
|
|
89
|
+
add_child(addr)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -4,22 +4,79 @@ module AdfBuilder
|
|
|
4
4
|
module Nodes
|
|
5
5
|
class Vehicle < Node
|
|
6
6
|
validates_inclusion_of :status, in: %i[new used]
|
|
7
|
+
validates_inclusion_of :interest, in: %i[buy lease sell trade-in test-drive]
|
|
8
|
+
validates_presence_of :year, :make, :model
|
|
7
9
|
|
|
8
|
-
def
|
|
9
|
-
|
|
10
|
+
def initialize
|
|
11
|
+
super
|
|
12
|
+
@tag_name = :vehicle
|
|
13
|
+
@attributes[:status] = :new
|
|
14
|
+
@attributes[:interest] = :buy
|
|
10
15
|
end
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
# Simple Text Elements (Singular)
|
|
18
|
+
# Simple Text Elements (Singular)
|
|
19
|
+
%i[year make model vin stock trim doors bodystyle transmission pricecomments comments].each do |tag|
|
|
20
|
+
define_method(tag) do |value|
|
|
21
|
+
remove_children(tag)
|
|
22
|
+
add_child(GenericNode.new(tag, {}, value))
|
|
23
|
+
end
|
|
14
24
|
end
|
|
15
25
|
|
|
16
|
-
def
|
|
17
|
-
|
|
26
|
+
def condition(value)
|
|
27
|
+
remove_children(:condition)
|
|
28
|
+
add_child(Condition.new(value))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def interest(value)
|
|
32
|
+
@attributes[:interest] = value
|
|
18
33
|
end
|
|
19
34
|
|
|
20
35
|
def status(value)
|
|
21
36
|
@attributes[:status] = value
|
|
22
37
|
end
|
|
38
|
+
|
|
39
|
+
# Complex Elements
|
|
40
|
+
def id(value, sequence: nil, source: nil)
|
|
41
|
+
# id* is multiple, so just add
|
|
42
|
+
add_child(Id.new(value, sequence: sequence, source: source))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def odometer(value, status: nil, units: nil)
|
|
46
|
+
remove_children(:odometer)
|
|
47
|
+
add_child(Odometer.new(value, status: status, units: units))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def imagetag(value, width: nil, height: nil, alttext: nil)
|
|
51
|
+
remove_children(:imagetag)
|
|
52
|
+
add_child(ImageTag.new(value, width: width, height: height, alttext: alttext))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def price(value, **attrs)
|
|
56
|
+
remove_children(:price)
|
|
57
|
+
add_child(Price.new(value, **attrs))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def option(&block)
|
|
61
|
+
# option* is multiple
|
|
62
|
+
opt = Option.new
|
|
63
|
+
opt.instance_eval(&block) if block_given?
|
|
64
|
+
add_child(opt)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def finance(&block)
|
|
68
|
+
remove_children(:finance)
|
|
69
|
+
fin = Finance.new
|
|
70
|
+
fin.instance_eval(&block) if block_given?
|
|
71
|
+
add_child(fin)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def colorcombination(&block)
|
|
75
|
+
# colorcombination* is multiple
|
|
76
|
+
cc = ColorCombination.new
|
|
77
|
+
cc.instance_eval(&block) if block_given?
|
|
78
|
+
add_child(cc)
|
|
79
|
+
end
|
|
23
80
|
end
|
|
24
81
|
end
|
|
25
82
|
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdfBuilder
|
|
4
|
+
module Nodes
|
|
5
|
+
class Odometer < Node
|
|
6
|
+
validates_inclusion_of :status, in: %i[unknown rolledover replaced original]
|
|
7
|
+
validates_inclusion_of :units, in: %i[km mi]
|
|
8
|
+
|
|
9
|
+
def initialize(value, status: nil, units: nil)
|
|
10
|
+
super()
|
|
11
|
+
@tag_name = :odometer
|
|
12
|
+
@value = value
|
|
13
|
+
@attributes[:status] = status if status
|
|
14
|
+
@attributes[:units] = units if units
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class ImageTag < Node
|
|
19
|
+
def initialize(value, width: nil, height: nil, alttext: nil)
|
|
20
|
+
super()
|
|
21
|
+
@tag_name = :imagetag
|
|
22
|
+
@value = value
|
|
23
|
+
@attributes[:width] = width if width
|
|
24
|
+
@attributes[:height] = height if height
|
|
25
|
+
@attributes[:alttext] = alttext if alttext
|
|
26
|
+
end
|
|
27
|
+
end
|
|
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
|
+
class Price < Node
|
|
71
|
+
validates_inclusion_of :type, in: %i[quote offer msrp invoice call appraisal asking]
|
|
72
|
+
validates_inclusion_of :delta, in: %i[absolute relative percentage]
|
|
73
|
+
validates_inclusion_of :relativeto, in: %i[msrp invoice]
|
|
74
|
+
|
|
75
|
+
def initialize(value, type: :quote, currency: nil, delta: nil, relativeto: nil, source: nil)
|
|
76
|
+
super()
|
|
77
|
+
@tag_name = :price
|
|
78
|
+
@value = value
|
|
79
|
+
@attributes[:type] = type
|
|
80
|
+
@attributes[:currency] = currency if currency
|
|
81
|
+
@attributes[:delta] = delta if delta
|
|
82
|
+
@attributes[:relativeto] = relativeto if relativeto
|
|
83
|
+
@attributes[:source] = source if source
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class Amount < Node
|
|
88
|
+
validates_inclusion_of :type, in: %i[downpayment monthly total]
|
|
89
|
+
validates_inclusion_of :limit, in: %i[maximum minimum exact]
|
|
90
|
+
|
|
91
|
+
def initialize(value, type: :total, limit: :maximum, currency: nil)
|
|
92
|
+
super()
|
|
93
|
+
@tag_name = :amount
|
|
94
|
+
@value = value
|
|
95
|
+
@attributes[:type] = type
|
|
96
|
+
@attributes[:limit] = limit
|
|
97
|
+
@attributes[:currency] = currency if currency
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class Balance < Node
|
|
102
|
+
validates_inclusion_of :type, in: %i[finance residual]
|
|
103
|
+
|
|
104
|
+
def initialize(value, type: :finance, currency: nil)
|
|
105
|
+
super()
|
|
106
|
+
@tag_name = :balance
|
|
107
|
+
@value = value
|
|
108
|
+
@attributes[:type] = type
|
|
109
|
+
@attributes[:currency] = currency if currency
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class Finance < Node
|
|
114
|
+
def initialize
|
|
115
|
+
super
|
|
116
|
+
@tag_name = :finance
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def method(value)
|
|
120
|
+
remove_children(:method)
|
|
121
|
+
add_child(FinanceMethod.new(value))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def amount(value, type: :total, limit: :maximum, currency: nil)
|
|
125
|
+
add_child(Amount.new(value, type: type, limit: limit, currency: currency))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def balance(value, type: :finance, currency: nil)
|
|
129
|
+
add_child(Balance.new(value, type: type, currency: currency))
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class Option < Node
|
|
134
|
+
def initialize
|
|
135
|
+
super
|
|
136
|
+
@tag_name = :option
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def optionname(value)
|
|
140
|
+
add_child(GenericNode.new(:optionname, {}, value))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def manufacturercode(value)
|
|
144
|
+
add_child(GenericNode.new(:manufacturercode, {}, value))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def stock(value)
|
|
148
|
+
add_child(GenericNode.new(:stock, {}, value))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def weighting(value)
|
|
152
|
+
add_child(Weighting.new(value))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def price(value, **attrs)
|
|
156
|
+
add_child(Price.new(value, **attrs))
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class ColorCombination < Node
|
|
161
|
+
def initialize
|
|
162
|
+
super
|
|
163
|
+
@tag_name = :colorcombination
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def interiorcolor(value)
|
|
167
|
+
add_child(GenericNode.new(:interiorcolor, {}, value))
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def exteriorcolor(value)
|
|
171
|
+
add_child(GenericNode.new(:exteriorcolor, {}, value))
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def preference(value)
|
|
175
|
+
add_child(GenericNode.new(:preference, {}, value))
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdfBuilder
|
|
4
|
+
module Nodes
|
|
5
|
+
class Vendor < Node
|
|
6
|
+
def initialize
|
|
7
|
+
super
|
|
8
|
+
@tag_name = :vendor
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def id(value, sequence: nil, source: nil)
|
|
12
|
+
add_child(Id.new(value, sequence: sequence, source: source))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vendorname(value)
|
|
16
|
+
remove_children(:vendorname)
|
|
17
|
+
add_child(GenericNode.new(:vendorname, {}, value))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def url(value)
|
|
21
|
+
remove_children(:url)
|
|
22
|
+
add_child(GenericNode.new(:url, {}, value))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def contact(primary_contact: false, &block)
|
|
26
|
+
remove_children(:contact)
|
|
27
|
+
contact = Contact.new(primary_contact: primary_contact)
|
|
28
|
+
contact.instance_eval(&block) if block_given?
|
|
29
|
+
add_child(contact)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -4,8 +4,10 @@ require "ox"
|
|
|
4
4
|
|
|
5
5
|
module AdfBuilder
|
|
6
6
|
class Serializer
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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)
|
|
@@ -41,7 +46,7 @@ module AdfBuilder
|
|
|
41
46
|
def serialize_node(node, parent_element)
|
|
42
47
|
# Determine element name using tag_name to avoid conflict with DSL methods like 'name'
|
|
43
48
|
# Safest check: Does it have the instance variable set?
|
|
44
|
-
element_name = if node.instance_variable_defined?(:@tag_name)
|
|
49
|
+
element_name = if node.instance_variable_defined?(:@tag_name) && node.instance_variable_get(:@tag_name)
|
|
45
50
|
node.instance_variable_get(:@tag_name).to_s
|
|
46
51
|
else
|
|
47
52
|
node.class.name.split("::").last.downcase
|
|
@@ -75,7 +80,10 @@ module AdfBuilder
|
|
|
75
80
|
end
|
|
76
81
|
|
|
77
82
|
def attribute?(key)
|
|
78
|
-
%i[
|
|
83
|
+
%i[
|
|
84
|
+
part type status sequence source id valid preferredcontact time
|
|
85
|
+
interest units width height alttext limit currency delta relativeto line
|
|
86
|
+
].include?(key)
|
|
79
87
|
end
|
|
80
88
|
end
|
|
81
89
|
end
|