adf_builder 1.0.0 → 1.1.0

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: efabe67ca191eb4a8d3cb985d6fa47869b2d719989337213d691734c07263cd0
4
- data.tar.gz: 9b83a052a0198a37a8942bbdf39400681e0268a034ec05a0c5663d038ffbb378
3
+ metadata.gz: ded72167a643d102957459b6d439bbaae8f01833303a3eca082ce4a6f0e264dd
4
+ data.tar.gz: 64486e697bd4feefa5fdbc8c6cbe01d3d0492878225748ffdf3c8ed1650b8b09
5
5
  SHA512:
6
- metadata.gz: 61da2f959f608ea29bbd29a0c3a63591ec3ef4d5a94dd0df50b8c7ea9f0eec4a254e696e05fe2f58d229d01081a837870a337852801afcb57a3090122036aefc
7
- data.tar.gz: e219d9a2b74c562295a72464042cec57160e3f9ec8b99cc2b90f7bd4deb96fd956dad7c9632f74741470467443a10d2c4472e87468b299af2e50c381347a0087
6
+ metadata.gz: b868564c56972b958839382c2d5c5706ce1b7864ea13e2c7eb38dddccb82f6481a66bc471f679f79589533a8fa9fd29897a387d53d4426ee8bc5490242912b45
7
+ data.tar.gz: 0c25a32fec2c3015e402fdbe26e0c2af23a2a91ea64840097a7a86acd7c88c4e89a809362b1ea35b8eebaa112dc130534b82f1a3647173ab5f0d1f3aa2b19fa0
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  .rspec_status
12
12
 
13
13
  deploy.txt
14
+ *.gem
data/CHANGELOG.md CHANGED
@@ -1,4 +1,20 @@
1
- ## [Unreleased]
1
+ ## [1.1.0] - 2026-01-19
2
+ - **Feature Complete**: Implemented all ADF 1.0 nodes and attributes including `Vendor`, `Provider`, and complex `Vehicle` tags (`Finance`, `Option`, `Odometer`, `ColorCombination`, `ImageTag`, `Price`).
3
+ - **Singular Field logic**: Methods for singular fields (e.g. `vehicle.year`) now correctly replace existing values instead of appending.
4
+ - **Removed Legacy Code**: Cleaned up deprecated legacy implementation directories.
5
+
6
+ ## [1.0.0] - 2026-01-19
7
+ - **MAJOR OVERHAUL**: Complete rewrtie of the library architecture.
8
+ - **New Block-based DSL**: Intuitive API for building ADF documents (`AdfBuilder.build { vehicle { ... } }`).
9
+ - **Validation**: Strict enforcement of ADF enumerations and structure (e.g. `vehicle status: :new`).
10
+ - **Editing**: New `AdfBuilder.tree` method for programmatic modifications after construction.
11
+ - **Robustness**: Complete rewrite of XML generation using robust heuristics and strict `Ox` serialization.
12
+ - **Compatibility**: Verified for Ruby 3.4.x.
13
+ - **Features**:
14
+ - Support for multiple vehicles/customers.
15
+ - Support for singular vs multiple item logic.
16
+ - Dynamic support for arbitrary/custom tags (`method_missing`).
17
+ - Automatic handling of XML attributes vs simple elements.
2
18
 
3
19
  ## [0.4.0] - 2026-01-19
4
20
  - Modernized dependencies
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- adf_builder (1.0.0)
4
+ adf_builder (1.1.0)
5
5
  ox (~> 2.14)
6
6
 
7
7
  GEM
@@ -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
- class Contact < Node
14
- def name(value, part: nil, type: nil)
15
- name_node = Name.new(value, part: part, type: type)
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 email(value)
20
- add_child(SimpleElement.new(:email, value))
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 phone(value, type: nil)
24
- phone_node = Phone.new(value, type: type)
25
- add_child(phone_node)
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 Name < Node
30
- def initialize(value, part: nil, type: nil)
31
- super()
32
- @value = value
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
- class Phone < Node
40
- def initialize(value, type: nil)
41
- super()
42
- @value = value
43
- @attributes[:type] = type if type
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
- # Simple Helper for tags like <email>foo</email>
49
- class SimpleElement < Node
50
- def initialize(tag_name, value)
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,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdfBuilder
4
+ module Nodes
5
+ class Id < Node
6
+ def initialize(value, sequence: nil, source: nil)
7
+ super()
8
+ @tag_name = :id
9
+ @value = value
10
+ @attributes[:sequence] = sequence if sequence
11
+ @attributes[:source] = source if source
12
+ end
13
+ end
14
+
15
+ class Phone < Node
16
+ def initialize(value, type: nil, time: nil, preferredcontact: nil)
17
+ super()
18
+ @tag_name = :phone
19
+ @value = value
20
+ @attributes[:type] = type if type
21
+ @attributes[:time] = time if time
22
+ @attributes[:preferredcontact] = preferredcontact if preferredcontact
23
+ end
24
+ end
25
+
26
+ class Email < Node
27
+ def initialize(value, preferredcontact: nil)
28
+ super()
29
+ @tag_name = :email
30
+ @value = value
31
+ @attributes[:preferredcontact] = preferredcontact if preferredcontact
32
+ end
33
+ end
34
+
35
+ class Name < Node
36
+ def initialize(value, part: nil, type: nil)
37
+ super()
38
+ @tag_name = :name
39
+ @value = value
40
+ @attributes[:part] = part if part
41
+ @attributes[:type] = type if type
42
+ end
43
+ end
44
+
45
+ class Address < Node
46
+ def initialize(type: nil)
47
+ super()
48
+ @tag_name = :address
49
+ @attributes[:type] = type if type
50
+ end
51
+
52
+ def street(value, line: nil)
53
+ node = GenericNode.new(:street, { line: line }.compact, value)
54
+ add_child(node)
55
+ end
56
+
57
+ # Simple elements
58
+ %i[apartment city regioncode postalcode country].each do |tag|
59
+ define_method(tag) do |value|
60
+ add_child(GenericNode.new(tag, {}, value))
61
+ end
62
+ end
63
+ end
64
+
65
+ class Contact < Node
66
+ def initialize(primary_contact: false)
67
+ super()
68
+ @tag_name = :contact
69
+ # primary_contact might be useful for logic but not an attribute
70
+ end
71
+
72
+ def name(value, part: nil, type: nil)
73
+ add_child(Name.new(value, part: part, type: type))
74
+ end
75
+
76
+ def email(value, preferredcontact: nil)
77
+ add_child(Email.new(value, preferredcontact: preferredcontact))
78
+ end
79
+
80
+ def phone(value, type: nil, time: nil, preferredcontact: nil)
81
+ add_child(Phone.new(value, type: type, time: time, preferredcontact: preferredcontact))
82
+ end
83
+
84
+ def address(type: nil, &block)
85
+ addr = Address.new(type: type)
86
+ addr.instance_eval(&block) if block_given?
87
+ add_child(addr)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -4,22 +4,72 @@ 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]
7
8
 
8
- def year(value)
9
- @attributes[:year] = value
9
+ def initialize
10
+ super
11
+ @tag_name = :vehicle
12
+ @attributes[:status] = :new
13
+ @attributes[:interest] = :buy
10
14
  end
11
15
 
12
- def make(value)
13
- @attributes[:make] = value
16
+ # Simple Text Elements (Singular)
17
+ %i[year make model vin stock trim doors bodystyle transmission condition pricecomments comments].each do |tag|
18
+ define_method(tag) do |value|
19
+ remove_children(tag)
20
+ add_child(GenericNode.new(tag, {}, value))
21
+ end
14
22
  end
15
23
 
16
- def model(value)
17
- @attributes[:model] = value
24
+ def interest(value)
25
+ @attributes[:interest] = value
18
26
  end
19
27
 
20
28
  def status(value)
21
29
  @attributes[:status] = value
22
30
  end
31
+
32
+ # Complex Elements
33
+ def id(value, sequence: nil, source: nil)
34
+ # id* is multiple, so just add
35
+ add_child(Id.new(value, sequence: sequence, source: source))
36
+ end
37
+
38
+ def odometer(value, status: nil, units: nil)
39
+ remove_children(:odometer)
40
+ add_child(Odometer.new(value, status: status, units: units))
41
+ end
42
+
43
+ def imagetag(value, width: nil, height: nil, alttext: nil)
44
+ remove_children(:imagetag)
45
+ add_child(ImageTag.new(value, width: width, height: height, alttext: alttext))
46
+ end
47
+
48
+ def price(value, **attrs)
49
+ remove_children(:price)
50
+ add_child(Price.new(value, **attrs))
51
+ end
52
+
53
+ def option(&block)
54
+ # option* is multiple
55
+ opt = Option.new
56
+ opt.instance_eval(&block) if block_given?
57
+ add_child(opt)
58
+ end
59
+
60
+ def finance(&block)
61
+ remove_children(:finance)
62
+ fin = Finance.new
63
+ fin.instance_eval(&block) if block_given?
64
+ add_child(fin)
65
+ end
66
+
67
+ def colorcombination(&block)
68
+ # colorcombination* is multiple
69
+ cc = ColorCombination.new
70
+ cc.instance_eval(&block) if block_given?
71
+ add_child(cc)
72
+ end
23
73
  end
24
74
  end
25
75
  end
@@ -0,0 +1,137 @@
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 Price < Node
30
+ validates_inclusion_of :type, in: %i[quote offer msrp invoice call appraisal asking]
31
+ validates_inclusion_of :delta, in: %i[absolute relative percentage]
32
+ validates_inclusion_of :relativeto, in: %i[msrp invoice]
33
+
34
+ def initialize(value, type: :quote, currency: nil, delta: nil, relativeto: nil, source: nil)
35
+ super()
36
+ @tag_name = :price
37
+ @value = value
38
+ @attributes[:type] = type
39
+ @attributes[:currency] = currency if currency
40
+ @attributes[:delta] = delta if delta
41
+ @attributes[:relativeto] = relativeto if relativeto
42
+ @attributes[:source] = source if source
43
+ end
44
+ end
45
+
46
+ class Amount < Node
47
+ validates_inclusion_of :type, in: %i[downpayment monthly total]
48
+ validates_inclusion_of :limit, in: %i[maximum minimum exact]
49
+
50
+ def initialize(value, type: :total, limit: :maximum, currency: nil)
51
+ super()
52
+ @tag_name = :amount
53
+ @value = value
54
+ @attributes[:type] = type
55
+ @attributes[:limit] = limit
56
+ @attributes[:currency] = currency if currency
57
+ end
58
+ end
59
+
60
+ class Balance < Node
61
+ validates_inclusion_of :type, in: %i[finance residual]
62
+
63
+ def initialize(value, type: :finance, currency: nil)
64
+ super()
65
+ @tag_name = :balance
66
+ @value = value
67
+ @attributes[:type] = type
68
+ @attributes[:currency] = currency if currency
69
+ end
70
+ end
71
+
72
+ class Finance < Node
73
+ def initialize
74
+ super
75
+ @tag_name = :finance
76
+ end
77
+
78
+ def method(value)
79
+ add_child(GenericNode.new(:method, {}, value))
80
+ end
81
+
82
+ def amount(value, type: :total, limit: :maximum, currency: nil)
83
+ add_child(Amount.new(value, type: type, limit: limit, currency: currency))
84
+ end
85
+
86
+ def balance(value, type: :finance, currency: nil)
87
+ add_child(Balance.new(value, type: type, currency: currency))
88
+ end
89
+ end
90
+
91
+ class Option < Node
92
+ def initialize
93
+ super
94
+ @tag_name = :option
95
+ end
96
+
97
+ def optionname(value)
98
+ add_child(GenericNode.new(:optionname, {}, value))
99
+ end
100
+
101
+ def manufacturercode(value)
102
+ add_child(GenericNode.new(:manufacturercode, {}, value))
103
+ end
104
+
105
+ def stock(value)
106
+ add_child(GenericNode.new(:stock, {}, value))
107
+ end
108
+
109
+ def weighting(value)
110
+ add_child(GenericNode.new(:weighting, {}, value))
111
+ end
112
+
113
+ def price(value, **attrs)
114
+ add_child(Price.new(value, **attrs))
115
+ end
116
+ end
117
+
118
+ class ColorCombination < Node
119
+ def initialize
120
+ super
121
+ @tag_name = :colorcombination
122
+ end
123
+
124
+ def interiorcolor(value)
125
+ add_child(GenericNode.new(:interiorcolor, {}, value))
126
+ end
127
+
128
+ def exteriorcolor(value)
129
+ add_child(GenericNode.new(:exteriorcolor, {}, value))
130
+ end
131
+
132
+ def preference(value)
133
+ add_child(GenericNode.new(:preference, {}, value))
134
+ end
135
+ end
136
+ end
137
+ 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
@@ -41,7 +41,7 @@ module AdfBuilder
41
41
  def serialize_node(node, parent_element)
42
42
  # Determine element name using tag_name to avoid conflict with DSL methods like 'name'
43
43
  # Safest check: Does it have the instance variable set?
44
- element_name = if node.instance_variable_defined?(:@tag_name)
44
+ element_name = if node.instance_variable_defined?(:@tag_name) && node.instance_variable_get(:@tag_name)
45
45
  node.instance_variable_get(:@tag_name).to_s
46
46
  else
47
47
  node.class.name.split("::").last.downcase
@@ -75,7 +75,10 @@ module AdfBuilder
75
75
  end
76
76
 
77
77
  def attribute?(key)
78
- %i[part type status sequence source id valid preferredcontact time].include?(key)
78
+ %i[
79
+ part type status sequence source id valid preferredcontact time
80
+ interest units width height alttext limit currency delta relativeto line
81
+ ].include?(key)
79
82
  end
80
83
  end
81
84
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AdfBuilder
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/adf_builder.rb CHANGED
@@ -4,164 +4,22 @@ require "ox"
4
4
  require "json"
5
5
 
6
6
  require_relative "adf_builder/version"
7
-
8
- # CUSTOMER
9
- require_relative "adf_builder/customer/customer"
10
- require_relative "adf_builder/customer/timeframe"
11
-
12
- # BASE
13
- require_relative "adf_builder/base/base"
14
- require_relative "adf_builder/base/prospect"
15
- require_relative "adf_builder/base/request_date"
16
-
17
- # PROVIDER
18
- require_relative "adf_builder/provider/provider"
19
-
20
- # SHARED
21
- require_relative "adf_builder/shared/id"
22
- require_relative "adf_builder/shared/contact"
23
-
24
- # VEHICLES
25
- require_relative "adf_builder/vehicles/vehicles"
26
- require_relative "adf_builder/vehicles/colorcombinations"
27
- require_relative "adf_builder/vehicles/price"
28
-
29
- # VENDOR
30
- require_relative "adf_builder/vendor/vendor"
31
-
32
- # v1.0 DSL
33
- # v1.0 DSL
34
7
  require_relative "adf_builder/validations"
8
+
9
+ # Nodes
35
10
  require_relative "adf_builder/nodes/node"
36
11
  require_relative "adf_builder/nodes/prospect"
12
+ require_relative "adf_builder/nodes/shared"
37
13
  require_relative "adf_builder/nodes/vehicle"
14
+ require_relative "adf_builder/nodes/vehicle_nodes"
38
15
  require_relative "adf_builder/nodes/customer"
16
+ require_relative "adf_builder/nodes/vendor"
17
+ require_relative "adf_builder/nodes/provider"
18
+
19
+ # Core
39
20
  require_relative "adf_builder/serializer"
40
21
  require_relative "adf_builder/dsl"
41
22
 
42
23
  module AdfBuilder
43
24
  class Error < StandardError; end
44
-
45
- class Builder
46
- def initialize
47
- @doc = init_doc
48
- @base = Base.new(@doc)
49
- end
50
-
51
- def prospect
52
- @base.prospect
53
- end
54
-
55
- # output the XML
56
- def to_xml
57
- Ox.dump(@doc, {})
58
- end
59
-
60
- # def an example of minimal XML taken from ADF spec file http://adfxml.info/adf_spec.pdf
61
- def minimal_lead
62
- prospect = Ox::Element.new("prospect")
63
-
64
- request_date = Ox::Element.new("requestdate")
65
- request_date << "2000-03-30T15:30:20-08:00"
66
-
67
- vehicle = Ox::Element.new("vehicle")
68
- year = Ox::Element.new("year")
69
- year << "1999"
70
-
71
- make = Ox::Element.new("make")
72
- make << "Chevrolet"
73
-
74
- model = Ox::Element.new("model")
75
- model << "Blazer"
76
-
77
- vehicle << year << make << model
78
-
79
- customer = Ox::Element.new("customer")
80
-
81
- contact = Ox::Element.new("contact")
82
-
83
- name = Ox::Element.new("name")
84
- name[:part] = "full"
85
- name << "John Doe"
86
-
87
- phone = Ox::Element.new("phone")
88
- phone << "393-999-3922"
89
-
90
- contact << name << phone
91
- customer << contact
92
-
93
- vendor = Ox::Element.new("vendor")
94
-
95
- contact = Ox::Element.new("contact")
96
- name = Ox::Element.new("name")
97
- name[:part] = "full"
98
- name << "Acura of Bellevue"
99
-
100
- contact << name
101
- vendor << contact
102
-
103
- prospect << request_date << vehicle << customer << vendor
104
- @doc.remove_children_by_path("adf/prospect")
105
- @doc.adf << prospect
106
- Ox.dump(@doc, {})
107
- end
108
-
109
- # go back to the initial structure
110
- def reset_doc
111
- @doc.adf.prospect.remove_children_by_path("*")
112
- end
113
-
114
- # all the files will start with this same header
115
- def init_doc
116
- doc = Ox::Document.new
117
- instruct = Ox::Instruct.new(:xml)
118
- instruct[:version] = "1.0"
119
- doc << instruct
120
- doc << Ox::Raw.new("")
121
- instruct = Ox::Instruct.new("ADF")
122
- instruct[:version] = "1.0"
123
- doc << instruct
124
- adf = Ox::Element.new("adf")
125
- doc << adf
126
- doc
127
- end
128
-
129
- # we will either create a new node with the value or replace the one if it is available
130
- def self.update_node(parent_node, key, value, params = {})
131
- key = key.to_s
132
- value = value.to_s
133
- if parent_node.locate(key).size.positive?
134
- node = parent_node.locate(key).first
135
- node.replace_text(value)
136
- else
137
- node = (Ox::Element.new(key) << value)
138
- parent_node << node
139
- end
140
-
141
- AdfBuilder::Builder.update_params(node, key, params)
142
- end
143
-
144
- # update the params by first checking if they are valid params and then checking if the values are valid if necessary
145
- def self.update_params(node, key, params)
146
- return true if params.empty?
147
-
148
- key = key.to_sym
149
- valid_values = params[:valid_values]
150
- valid_parameters = params[:valid_parameters]
151
- _params = AdfBuilder::Builder.whitelabel_params(params, valid_parameters, key)
152
- _params.each do |k, v|
153
- node[k] = v if (valid_values[key][k] == true) || valid_values[key][k].include?(v.to_s)
154
- end
155
- end
156
-
157
- # clear out the opts that don't match valid keys
158
- def self.whitelabel_params(opts, valid_parameters, key)
159
- opts.slice(*valid_parameters[key])
160
- end
161
-
162
- def self.valid_child?(parent, tag_name, index)
163
- child = parent.locate(tag_name)[index]
164
- [!child.nil?, child]
165
- end
166
- end
167
25
  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.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - marcus.salinas
@@ -100,26 +100,18 @@ files:
100
100
  - bin/console
101
101
  - bin/setup
102
102
  - lib/adf_builder.rb
103
- - lib/adf_builder/base/base.rb
104
- - lib/adf_builder/base/prospect.rb
105
- - lib/adf_builder/base/request_date.rb
106
- - lib/adf_builder/customer/customer.rb
107
- - lib/adf_builder/customer/timeframe.rb
108
103
  - lib/adf_builder/data/iso-4217-currency-codes.json
109
104
  - lib/adf_builder/dsl.rb
110
105
  - lib/adf_builder/nodes/customer.rb
111
106
  - lib/adf_builder/nodes/node.rb
112
107
  - lib/adf_builder/nodes/prospect.rb
108
+ - lib/adf_builder/nodes/provider.rb
109
+ - lib/adf_builder/nodes/shared.rb
113
110
  - lib/adf_builder/nodes/vehicle.rb
114
- - lib/adf_builder/provider/provider.rb
111
+ - lib/adf_builder/nodes/vehicle_nodes.rb
112
+ - lib/adf_builder/nodes/vendor.rb
115
113
  - lib/adf_builder/serializer.rb
116
- - lib/adf_builder/shared/contact.rb
117
- - lib/adf_builder/shared/id.rb
118
114
  - lib/adf_builder/validations.rb
119
- - lib/adf_builder/vehicles/colorcombinations.rb
120
- - lib/adf_builder/vehicles/price.rb
121
- - lib/adf_builder/vehicles/vehicles.rb
122
- - lib/adf_builder/vendor/vendor.rb
123
115
  - lib/adf_builder/version.rb
124
116
  homepage: https://github.com/jippylong12/adf_builder
125
117
  licenses:
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Base
5
- # initialize the prospect, id, and requestdate node
6
- def initialize(doc)
7
- @doc = doc
8
- @prospect = Prospect.new(@doc)
9
- end
10
-
11
- attr_reader :prospect
12
- end
13
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Prospect
5
- STATUSES = {
6
- new: :new,
7
- resend: :resend
8
- }.freeze
9
-
10
- def initialize(doc)
11
- @doc = doc
12
- @doc.adf << Ox::Element.new("prospect")
13
- @prospect = @doc.adf.prospect
14
- @prospect[:status] = STATUSES[:new]
15
-
16
- @request_date = RequestDate.new(@prospect)
17
- @vehicles = Vehicles.new(@prospect)
18
- @customer = Customer.new(@prospect)
19
- @vendor = Vendor.new(@prospect)
20
- @provider = Provider.new(@prospect)
21
- end
22
-
23
- attr_reader :request_date, :vehicles, :customer, :vendor, :provider
24
-
25
- # set status to renew
26
- def set_renew
27
- @prospect[:status] = STATUSES[:resend]
28
- end
29
-
30
- def add_id(value, source = nil, sequence = 1)
31
- Id.new.add(@prospect, value, source, sequence)
32
- end
33
- end
34
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class RequestDate
5
- WITH_SYMBOLS = "%FT%T%:z"
6
- WITHOUT_SYMBOLS = "%Y%m%dT%H%M%S%z"
7
-
8
- def initialize(prospect_node)
9
- @request_date_node = Ox::Element.new("requestdate")
10
- @request_date_node << DateTime.now.strftime("%FT%T%:z")
11
- prospect_node << @request_date_node
12
- end
13
-
14
- def update_val(datetime_value, format = 1)
15
- if format == 1
16
- @request_date_node.replace_text(datetime_value.strftime(WITH_SYMBOLS))
17
- elsif format == 2
18
- @request_date_node.replace_text(datetime_value.strftime(WITHOUT_SYMBOLS))
19
- end
20
- end
21
- end
22
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Customer
5
- def initialize(prospect)
6
- @customer = Ox::Element.new("customer")
7
- @contact = nil
8
- @timeframe = nil
9
-
10
- prospect << @customer
11
- end
12
-
13
- attr_reader :contact, :timeframe
14
-
15
- def add(name, opts = {})
16
- @contact = Contact.new(@customer, name, opts)
17
- end
18
-
19
- def add_id(index, value, source = nil, sequence = 1)
20
- if @prospect.locate("customer").empty?
21
- false
22
- else
23
- Id.new.add(@prospect.customer(index), value, source, sequence)
24
- end
25
- end
26
-
27
- # @param descriptin [String] - Description of customer’s timing intention.
28
- # @param earliest_date [DateTime] - Earliest date customer is interested in. If timeframe tag
29
- # is present, it is required to specify earliestdate and/or
30
- # latestdate
31
- # @param latest_date [DateTime] - Latest date customer is interested in. If timeframe tag
32
- # is present, it is required to specify earliestdate and/or
33
- # latestdate
34
- def add_timeframe(description, earliest_date = nil, latest_date = nil)
35
- return false if earliest_date.nil? && latest_date.nil?
36
-
37
- return false if earliest_date && (earliest_date.class != DateTime)
38
-
39
- return false if latest_date && (latest_date.class != DateTime)
40
-
41
- @timeframe = Timeframe.new(@customer, description, earliest_date, latest_date) if @timeframe.nil?
42
- end
43
-
44
- def update_comments(comments)
45
- return false if comments.class != String
46
-
47
- AdfBuilder::Builder.update_node(@customer, :comments, comments)
48
- end
49
- end
50
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Timeframe
5
- def initialize(customer, description, earliest_date, latest_date)
6
- begin
7
- earliest_date = earliest_date.strftime("%FT%T%:z") if earliest_date
8
- latest_date = latest_date.strftime("%FT%T%:z") if latest_date
9
- rescue StandardError
10
- return nil
11
- end
12
-
13
- @timeframe = Ox::Element.new("timeframe")
14
-
15
- @timeframe << (Ox::Element.new("description") << description)
16
- @timeframe << (Ox::Element.new("earliestdate") << earliest_date) if earliest_date
17
- @timeframe << (Ox::Element.new("latestdate") << latest_date) if latest_date
18
- customer << @timeframe
19
- end
20
-
21
- def update_description(description)
22
- AdfBuilder::Builder.update_node(@timeframe, :description, description)
23
- end
24
-
25
- def update_earliest_date(date)
26
- begin
27
- date = date.strftime("%FT%T%:z")
28
- rescue StandardError
29
- return false
30
- end
31
- AdfBuilder::Builder.update_node(@timeframe, :earliestdate, date)
32
- end
33
-
34
- def update_latest_date(date)
35
- begin
36
- date = date.strftime("%FT%T%:z")
37
- rescue StandardError
38
- return false
39
- end
40
-
41
- AdfBuilder::Builder.update_node(@timeframe, :latestdate, date)
42
- end
43
- end
44
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Provider
5
- FREE_TEXT_OPTIONAL_TAGS = %i[service url].freeze
6
-
7
- def initialize(prospect)
8
- @prospect = prospect
9
- @provider = nil
10
- @contact = nil
11
- end
12
-
13
- attr_reader :contact
14
-
15
- def add(name, params = {})
16
- @provider = Ox::Element.new("provider")
17
- params.merge!({ valid_values: AdfBuilder::Contact::VALID_VALUES,
18
- valid_parameters: AdfBuilder::Contact::VALID_PARAMETERS })
19
- AdfBuilder::Builder.update_node(@provider, :name, name, params)
20
- @prospect << @provider
21
- end
22
-
23
- def add_contact(name, opts = {})
24
- @contact = Contact.new(@provider, name, opts)
25
- end
26
-
27
- def add_phone(phone, params = {})
28
- params.merge!({ valid_values: AdfBuilder::Contact::VALID_VALUES,
29
- valid_parameters: AdfBuilder::Contact::VALID_PARAMETERS })
30
- AdfBuilder::Builder.update_node(@provider, :phone, phone, params)
31
- end
32
-
33
- def add_email(email, params = {})
34
- params.merge!({ valid_values: AdfBuilder::Contact::VALID_VALUES,
35
- valid_parameters: AdfBuilder::Contact::VALID_PARAMETERS })
36
- AdfBuilder::Builder.update_node(@provider, :email, email, params)
37
- end
38
-
39
- def update_tags_with_free_text(tags)
40
- tags.each do |key, value|
41
- AdfBuilder::Builder.update_node(@provider, key, value) if FREE_TEXT_OPTIONAL_TAGS.include? key.to_sym
42
- end
43
- end
44
-
45
- def add_id(index, value, source = nil, sequence = 1)
46
- if @prospect.locate("provider").empty?
47
- false
48
- else
49
- Id.new.add(@prospect.provider(index), value, source, sequence)
50
- end
51
- end
52
- end
53
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Contact
5
- VALID_PARAMETERS = {
6
- name: %i[part type primarycontact],
7
- email: [:preferredcontact],
8
- phone: %i[type time preferredcontact]
9
- }.freeze
10
-
11
- VALID_VALUES = {
12
- name: {
13
- part: %w[first middle suffix last full],
14
- type: %w[individual business],
15
- primarycontact: %w[0 1]
16
- },
17
- email: {
18
- preferredcontact: %w[0 1]
19
- },
20
- phone: {
21
- preferredcontact: %w[0 1],
22
- type: %w[phone fax cellphone pager],
23
- time: %w[morning afternoon evening nopreference day]
24
- }
25
- }.freeze
26
-
27
- def initialize(parent_node, name, params = {})
28
- @contact = Ox::Element.new("contact")
29
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
30
- AdfBuilder::Builder.update_node(@contact, :name, name, params)
31
- parent_node << @contact
32
- end
33
-
34
- def add_phone(phone, params = {})
35
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
36
- AdfBuilder::Builder.update_node(@contact, :phone, phone, params)
37
- end
38
-
39
- def add_email(email, params = {})
40
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
41
- AdfBuilder::Builder.update_node(@contact, :email, email, params)
42
- end
43
- end
44
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Id
5
- def initialize; end
6
-
7
- # add id tag to the form
8
- def add(parent_node, value, source = nil, sequence = 1)
9
- id_node = Ox::Element.new("id")
10
- id_node << value
11
- id_node[:sequence] = sequence
12
-
13
- id_node[:source] = source if source
14
-
15
- parent_node.prepend_child(id_node)
16
- end
17
- end
18
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class ColorCombinations
5
- FREE_TEXT_OPTIONAL_TAGS = %i[interiorcolor exteriorcolor preference].freeze
6
-
7
- def initialize(vehicle)
8
- @vehicle = vehicle
9
- @color_combination = nil
10
- end
11
-
12
- def add(interior_color, exterior_color, preference)
13
- @color_combination = Ox::Element.new("colorcombination")
14
- @color_combination <<
15
- (Ox::Element.new("interiorcolor") << interior_color) <<
16
- (Ox::Element.new("exteriorcolor") << exterior_color) <<
17
- (Ox::Element.new("preference") << preference.to_s)
18
- @vehicle << @color_combination
19
- end
20
-
21
- def update_tags(index, tags)
22
- valid, vehicle = AdfBuilder::Builder.valid_child?(@vehicle, "colorcombination", index)
23
- return unless valid
24
-
25
- tags.each do |key, value|
26
- AdfBuilder::Builder.update_node(vehicle, key, value) if FREE_TEXT_OPTIONAL_TAGS.include? key.to_sym
27
- end
28
- end
29
- end
30
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Price
5
- VALID_PARAMETERS = {
6
- price: %i[type currency delta relativeto source]
7
- }.freeze
8
-
9
- VALID_VALUES = {
10
- price: {
11
- type: %w[quote offer msrp invoice call appraisal asking],
12
- currency: true,
13
- delta: %w[absolute relative percentage],
14
- relativeto: %w[msrp invoice],
15
- source: true
16
- }
17
- }.freeze
18
-
19
- def initialize(parent_node, value, params = {})
20
- @parent_node = parent_node
21
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
22
- validate_currency(params)
23
- AdfBuilder::Builder.update_node(@parent_node, :price, value, params)
24
- @price = @parent_node.price
25
- end
26
-
27
- def update(value, params = {})
28
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
29
- AdfBuilder::Builder.update_node(@parent_node, :price, value, params)
30
- end
31
-
32
- def validate_currency(params)
33
- code = params[:currency]
34
- return unless code
35
-
36
- json = JSON.parse(File.read("./lib/adf_builder/data/iso-4217-currency-codes.json"))
37
- codes = json.map { |j| j["Alphabetic_Code"] }.reject(&:nil?)
38
- return if codes.include? code
39
-
40
- params.delete(:currency)
41
- end
42
- end
43
- end
@@ -1,123 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Vehicles
5
- VALID_PARAMETERS = {
6
- vehicle: %i[interest status],
7
- odometer: %i[status units],
8
- imagetag: %i[width height alttext]
9
- }.freeze
10
-
11
- VALID_VALUES = {
12
- vehicle: {
13
- interest: %w[buy lease sell trade-in test-drive],
14
- status: %w[new used]
15
- },
16
- odometer: {
17
- status: %w[unknown rolledover replaced original],
18
- units: %w[km mi]
19
- },
20
- imagetag: {
21
- width: true,
22
- height: true,
23
- alttext: true
24
- }
25
- }.freeze
26
-
27
- FREE_TEXT_OPTIONAL_TAGS = %i[year make model vin stock
28
- trim doors bodystyle transmission pricecomments comments].freeze
29
-
30
- CONDITIONS = %w[excellent good fair poor unknown].freeze
31
-
32
- def initialize(prospect)
33
- @prospect = prospect
34
- @color_combinations = []
35
- @prices = []
36
- end
37
-
38
- def add_color_combination(v_index, interior_color, exterior_color, preference)
39
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", v_index)
40
- return unless valid
41
-
42
- cc = ColorCombinations.new(vehicle)
43
- cc.add(interior_color, exterior_color, preference)
44
- @color_combinations.push(cc)
45
- end
46
-
47
- def color_combination(index)
48
- @color_combinations[index]
49
- end
50
-
51
- def price(index)
52
- @prices[index]
53
- end
54
-
55
- def add(year, make, model, params = {})
56
- vehicle = Ox::Element.new("vehicle")
57
-
58
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
59
- AdfBuilder::Builder.update_params(vehicle, :vehicle, params)
60
-
61
- vehicle << (Ox::Element.new("year") << year.to_s)
62
- vehicle << (Ox::Element.new("make") << make)
63
- vehicle << (Ox::Element.new("model") << model)
64
-
65
- @prospect << vehicle
66
- end
67
-
68
- def update_odometer(index, value, params = {})
69
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", index)
70
- return unless valid
71
-
72
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
73
- AdfBuilder::Builder.update_node(vehicle, "odometer", value, params)
74
- end
75
-
76
- def update_condition(index, value)
77
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", index)
78
- return unless valid && CONDITIONS.include?(value)
79
-
80
- AdfBuilder::Builder.update_node(vehicle, "condition", value)
81
- end
82
-
83
- def update_imagetag(index, value, params = {})
84
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", index)
85
- return unless valid
86
-
87
- params.merge!({ valid_values: VALID_VALUES, valid_parameters: VALID_PARAMETERS })
88
- AdfBuilder::Builder.update_node(vehicle, "imagetag", value, params)
89
- end
90
-
91
- def update_tags_with_free_text(index, tags)
92
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", index)
93
- return unless valid
94
-
95
- tags.each do |key, value|
96
- AdfBuilder::Builder.update_node(vehicle, key, value) if FREE_TEXT_OPTIONAL_TAGS.include? key.to_sym
97
- end
98
- end
99
-
100
- def add_id(index, value, source = nil, sequence = 1)
101
- if @prospect.locate("vehicle").empty? || @prospect.vehicle(index).nil?
102
- false
103
- else
104
- Id.new.add(@prospect.vehicle(index), value, source, sequence)
105
- end
106
- end
107
-
108
- def add_price(index, value, params = {})
109
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", index)
110
- return unless valid
111
-
112
- price = Price.new(vehicle, value, params)
113
- @prices.push(price)
114
- end
115
-
116
- def add_comments(index, value)
117
- valid, vehicle = AdfBuilder::Builder.valid_child?(@prospect, "vehicle", index)
118
- return unless valid
119
-
120
- AdfBuilder::Builder.update_node(vehicle, "comments", value)
121
- end
122
- end
123
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AdfBuilder
4
- class Vendor
5
- def initialize(prospect)
6
- @vendor = Ox::Element.new("vendor")
7
- @contact = nil
8
- prospect << @vendor
9
- end
10
-
11
- attr_reader :contact
12
-
13
- def add(name, contact_name, opts = {})
14
- @vendor << (Ox::Element.new("vendorname") << name)
15
- @contact = Contact.new(@vendor, contact_name, opts)
16
- end
17
-
18
- def add_url(url)
19
- @vendor.remove_children(@vendor.url) if @vendor.locate("url").size.positive?
20
- @vendor << (Ox::Element.new("url") << url)
21
- end
22
-
23
- def add_id(index, value, source = nil, sequence = 1)
24
- if @prospect.locate("vendor").empty?
25
- false
26
- else
27
- Id.new.add(@prospect.vendor(index), value, source, sequence)
28
- end
29
- end
30
- end
31
- end