nokogiri-happymapper 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
4
  module AnonymousMapper
3
-
4
5
  def parse(xml_content)
5
-
6
6
  # TODO: this should be able to handle all the types of functionality that parse is able
7
7
  # to handle which includes the text, xml document, node, fragment, etc.
8
8
  xml = Nokogiri::XML(xml_content)
@@ -13,8 +13,7 @@ module HappyMapper
13
13
  # for the class to actually use the normal HappyMapper powers to parse
14
14
  # the content. At this point this code is utilizing all of the existing
15
15
  # code implemented for parsing.
16
- happymapper_class.parse(xml_content, :single => true)
17
-
16
+ happymapper_class.parse(xml_content, single: true)
18
17
  end
19
18
 
20
19
  private
@@ -25,9 +24,9 @@ module HappyMapper
25
24
  #
26
25
  def underscore(camel_cased_word)
27
26
  word = camel_cased_word.to_s.dup
28
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
29
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
30
- word.tr!("-", "_")
27
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
28
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
29
+ word.tr!('-', '_')
31
30
  word.downcase!
32
31
  word
33
32
  end
@@ -56,59 +55,58 @@ module HappyMapper
56
55
 
57
56
  happymapper_class.namespace element.namespace.prefix if element.namespace
58
57
 
59
- element.namespaces.each do |prefix,namespace|
58
+ element.namespaces.each do |prefix, namespace|
60
59
  happymapper_class.register_namespace prefix, namespace
61
60
  end
62
61
 
63
- element.attributes.each do |name,attribute|
64
- define_attribute_on_class(happymapper_class,attribute)
62
+ element.attributes.each_value do |attribute|
63
+ define_attribute_on_class(happymapper_class, attribute)
65
64
  end
66
65
 
67
66
  element.children.each do |child|
68
- define_element_on_class(happymapper_class,child)
67
+ define_element_on_class(happymapper_class, child)
69
68
  end
70
69
 
71
70
  happymapper_class
72
71
  end
73
72
 
74
-
75
73
  #
76
74
  # Define a HappyMapper element on the provided class based on
77
75
  # the element provided.
78
76
  #
79
- def define_element_on_class(class_instance,element)
80
-
77
+ def define_element_on_class(class_instance, element)
81
78
  # When a text element has been provided create the necessary
82
- # HappyMapper content attribute if the text happens to content
79
+ # HappyMapper content attribute if the text happens to contain
83
80
  # some content.
84
81
 
85
- if element.text? and element.content.strip != ""
86
- class_instance.content :content, String
87
- end
82
+ class_instance.content :content, String if element.text? && (element.content.strip != '')
88
83
 
89
84
  # When the element has children elements, that are not text
90
85
  # elements, then we want to recursively define a new HappyMapper
91
86
  # class that will have elements and attributes.
92
87
 
93
- element_type = if !element.elements.reject {|e| e.text? }.empty? or !element.attributes.empty?
94
- create_happymapper_class_with_element(element)
95
- else
96
- String
97
- end
88
+ element_type = if !element.elements.reject(&:text?).empty? || !element.attributes.empty?
89
+ create_happymapper_class_with_element(element)
90
+ else
91
+ String
92
+ end
93
+
94
+ method = class_instance.elements.find { |e| e.name == element.name } ? :has_many : :has_one
98
95
 
99
- method = class_instance.elements.find {|e| e.name == element.name } ? :has_many : :has_one
96
+ options = {}
97
+ options[:tag] = element.name
98
+ namespace = element.namespace
99
+ options[:namespace] = namespace.prefix if namespace
100
100
 
101
- class_instance.send(method,underscore(element.name),element_type)
101
+ class_instance.send(method, underscore(element.name), element_type, options)
102
102
  end
103
103
 
104
104
  #
105
105
  # Define a HappyMapper attribute on the provided class based on
106
106
  # the attribute provided.
107
107
  #
108
- def define_attribute_on_class(class_instance,attribute)
109
- class_instance.attribute underscore(attribute.name), String
108
+ def define_attribute_on_class(class_instance, attribute)
109
+ class_instance.attribute underscore(attribute.name), String, tag: attribute.name
110
110
  end
111
-
112
111
  end
113
-
114
112
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
4
  class Attribute < Item
3
5
  attr_accessor :default
@@ -5,16 +7,16 @@ module HappyMapper
5
7
  # @see Item#initialize
6
8
  # Additional options:
7
9
  # :default => Object The default value for this
8
- def initialize(name, type, o={})
10
+ def initialize(name, type, options = {})
9
11
  super
10
- self.default = o[:default]
12
+ self.default = options[:default]
11
13
  end
12
14
 
13
- def find(node, namespace, xpath_options)
15
+ def find(node, _namespace, xpath_options)
14
16
  if options[:xpath]
15
- yield(node.xpath(options[:xpath],xpath_options))
17
+ yield(node.xpath(options[:xpath], xpath_options))
16
18
  else
17
- yield(node[tag])
19
+ yield(node.attributes[tag])
18
20
  end
19
21
  end
20
22
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
4
  class Element < Item
3
-
4
5
  def find(node, namespace, xpath_options)
5
6
  if self.namespace
6
7
  # from the class definition
@@ -10,11 +11,11 @@ module HappyMapper
10
11
  end
11
12
 
12
13
  if options[:single]
13
- if options[:xpath]
14
- result = node.xpath(options[:xpath], xpath_options)
15
- else
16
- result = node.xpath(xpath(namespace), xpath_options)
17
- end
14
+ result = if options[:xpath]
15
+ node.xpath(options[:xpath], xpath_options)
16
+ else
17
+ node.xpath(xpath(namespace), xpath_options)
18
+ end
18
19
 
19
20
  if result
20
21
  value = yield(result.first)
@@ -22,7 +23,7 @@ module HappyMapper
22
23
  value
23
24
  end
24
25
  else
25
- target_path = options[:xpath] ? options[:xpath] : xpath(namespace)
26
+ target_path = options[:xpath] || xpath(namespace)
26
27
  node.xpath(target_path, xpath_options).collect do |item|
27
28
  value = yield(item)
28
29
  handle_attributes_option(item, value, xpath_options)
@@ -32,24 +33,18 @@ module HappyMapper
32
33
  end
33
34
 
34
35
  def handle_attributes_option(result, value, xpath_options)
35
- if options[:attributes].is_a?(Hash)
36
- result = result.first unless result.respond_to?(:attribute_nodes)
37
-
38
- return unless result.respond_to?(:attribute_nodes)
36
+ return unless options[:attributes].is_a?(Hash)
37
+ result = result.first unless result.respond_to?(:attribute_nodes)
38
+ return unless result.respond_to?(:attribute_nodes)
39
39
 
40
- result.attribute_nodes.each do |xml_attribute|
41
- if attribute_options = options[:attributes][xml_attribute.name.to_sym]
42
- attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace, xpath_options)
43
-
44
- result.instance_eval <<-EOV
45
- def value.#{xml_attribute.name.gsub(/\-/, '_')}
46
- #{attribute_value.inspect}
47
- end
48
- EOV
49
- end # if attributes_options
50
- end # attribute_nodes.each
51
- end # if options[:attributes]
52
- end # def handle...
40
+ result.attribute_nodes.each do |xml_attribute|
41
+ next unless (attribute_options = options[:attributes][xml_attribute.name.to_sym])
42
+ attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).
43
+ from_xml_node(result, namespace, xpath_options)
53
44
 
45
+ method_name = xml_attribute.name.tr('-', '_')
46
+ value.define_singleton_method(method_name) { attribute_value }
47
+ end
48
+ end
54
49
  end
55
50
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
4
  class Item
3
5
  attr_accessor :name, :type, :tag, :options, :namespace
@@ -11,12 +13,12 @@ module HappyMapper
11
13
  # :raw => Boolean Use raw node value (inc. tags) when parsing.
12
14
  # :single => Boolean False if object should be collection, True for single object
13
15
  # :tag => String Element name if it doesn't match the specified name.
14
- def initialize(name, type, o={})
16
+ def initialize(name, type, options = {})
15
17
  self.name = name.to_s
16
18
  self.type = type
17
- #self.tag = o.delete(:tag) || name.to_s
18
- self.tag = o[:tag] || name.to_s
19
- self.options = { :single => true }.merge(o.merge(:name => self.name))
19
+ # self.tag = options.delete(:tag) || name.to_s
20
+ self.tag = options[:tag] || name.to_s
21
+ self.options = { single: true }.merge(options.merge(name: self.name))
20
22
 
21
23
  @xml_type = self.class.to_s.split('::').last.downcase
22
24
  end
@@ -31,7 +33,6 @@ module HappyMapper
31
33
  # @param [Hash] xpath_options additional xpath options
32
34
  #
33
35
  def from_xml_node(node, namespace, xpath_options)
34
-
35
36
  namespace = options[:namespace] if options.key?(:namespace)
36
37
 
37
38
  if suported_type_registered?
@@ -41,9 +42,8 @@ module HappyMapper
41
42
  elsif custom_parser_defined?
42
43
  find(node, namespace, xpath_options) { |n| process_node_with_custom_parser(n) }
43
44
  else
44
- process_node_with_default_parser(node,:namespaces => xpath_options)
45
+ process_node_with_default_parser(node, namespaces: xpath_options)
45
46
  end
46
-
47
47
  end
48
48
 
49
49
  def xpath(namespace = self.namespace)
@@ -51,7 +51,7 @@ module HappyMapper
51
51
  xpath += './/' if options[:deep]
52
52
  xpath += "#{namespace}:" if namespace
53
53
  xpath += tag
54
- #puts "xpath: #{xpath}"
54
+ # puts "xpath: #{xpath}"
55
55
  xpath
56
56
  end
57
57
 
@@ -72,19 +72,18 @@ module HappyMapper
72
72
  typecaster(value).apply(value)
73
73
  end
74
74
 
75
-
76
75
  private
77
76
 
78
77
  # @return [Boolean] true if the type defined for the item is defined in the
79
78
  # list of support types.
80
79
  def suported_type_registered?
81
- SupportedTypes.types.map {|caster| caster.type }.include?(constant)
80
+ SupportedTypes.types.map(&:type).include?(constant)
82
81
  end
83
82
 
84
83
  # @return [#apply] the typecaster object that will be able to convert
85
84
  # the value into a value with the correct type.
86
85
  def typecaster(value)
87
- SupportedTypes.types.find { |caster| caster.apply?(value,constant) }
86
+ SupportedTypes.types.find { |caster| caster.apply?(value, constant) }
88
87
  end
89
88
 
90
89
  #
@@ -113,21 +112,21 @@ module HappyMapper
113
112
  end
114
113
 
115
114
  def process_node_with_custom_parser(node)
116
- if node.respond_to?(:content) && !options[:raw]
117
- value = node.content
118
- else
119
- value = node.to_s
120
- end
115
+ value = if node.respond_to?(:content) && !options[:raw]
116
+ node.content
117
+ else
118
+ node.to_s
119
+ end
121
120
 
122
121
  begin
123
122
  constant.send(options[:parser].to_sym, value)
124
- rescue
123
+ rescue StandardError
125
124
  nil
126
125
  end
127
126
  end
128
127
 
129
- def process_node_with_default_parser(node,parse_options)
130
- constant.parse(node,options.merge(parse_options))
128
+ def process_node_with_default_parser(node, parse_options)
129
+ constant.parse(node, options.merge(parse_options))
131
130
  end
132
131
 
133
132
  #
@@ -155,6 +154,5 @@ module HappyMapper
155
154
  end
156
155
  constant
157
156
  end
158
-
159
157
  end
160
158
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
4
  module SupportedTypes
3
- extend self
5
+ module_function
4
6
 
5
7
  #
6
8
  # All of the registerd supported types that can be parsed.
@@ -45,8 +47,8 @@ module HappyMapper
45
47
  # DateTime.parse(value,to_s)
46
48
  # end
47
49
  #
48
- def register_type(type,&block)
49
- register CastWhenType.new(type,&block)
50
+ def register_type(type, &block)
51
+ register CastWhenType.new(type, &block)
50
52
  end
51
53
 
52
54
  #
@@ -57,16 +59,16 @@ module HappyMapper
57
59
  class CastWhenType
58
60
  attr_reader :type
59
61
 
60
- def initialize(type,&block)
62
+ def initialize(type, &block)
61
63
  @type = type
62
64
  @apply_block = block || no_operation
63
65
  end
64
66
 
65
67
  def no_operation
66
- lambda {|value| value }
68
+ ->(value) { value }
67
69
  end
68
70
 
69
- def apply?(value,convert_to_type)
71
+ def apply?(_value, convert_to_type)
70
72
  convert_to_type == type
71
73
  end
72
74
 
@@ -81,13 +83,12 @@ module HappyMapper
81
83
  # value simply can be returned.
82
84
  #
83
85
  class NilOrAlreadyConverted
84
-
85
86
  def type
86
87
  NilClass
87
88
  end
88
89
 
89
- def apply?(value,convert_to_type)
90
- value.kind_of?(convert_to_type) || value.nil?
90
+ def apply?(value, convert_to_type)
91
+ value.is_a?(convert_to_type) || value.nil?
91
92
  end
92
93
 
93
94
  def apply(value)
@@ -97,28 +98,30 @@ module HappyMapper
97
98
 
98
99
  register NilOrAlreadyConverted.new
99
100
 
100
- register_type String do |value|
101
- value.to_s
102
- end
101
+ register_type String, &:to_s
103
102
 
104
- register_type Float do |value|
105
- value.to_f
106
- end
103
+ register_type Float, &:to_f
107
104
 
108
105
  register_type Time do |value|
109
- Time.parse(value.to_s) rescue Time.at(value.to_i)
106
+ begin
107
+ Time.parse(value.to_s)
108
+ rescue StandardError
109
+ Time.at(value.to_i)
110
+ end
110
111
  end
111
112
 
113
+ # rubocop:disable Style/DateTime
112
114
  register_type DateTime do |value|
113
115
  DateTime.parse(value.to_s) if value && !value.empty?
114
116
  end
117
+ # rubocop:enable Style/DateTime
115
118
 
116
119
  register_type Date do |value|
117
120
  Date.parse(value.to_s) if value && !value.empty?
118
121
  end
119
122
 
120
123
  register_type Boolean do |value|
121
- ['true', 't', '1'].include?(value.to_s.downcase)
124
+ %w(true t 1).include?(value.to_s.downcase)
122
125
  end
123
126
 
124
127
  register_type Integer do |value|
@@ -134,7 +137,5 @@ module HappyMapper
134
137
  value_to_i
135
138
  end
136
139
  end
137
-
138
140
  end
139
-
140
141
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
4
  class TextNode < Item
3
-
4
- def find(node, namespace, xpath_options)
5
- yield(node.children.detect{|c| c.text?})
5
+ def find(node, _namespace, _xpath_options)
6
+ yield(node.children.detect(&:text?))
6
7
  end
7
8
  end
8
9
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HappyMapper
2
- VERSION = "0.6.0"
4
+ VERSION = '0.7.0'
3
5
  end
@@ -1,46 +1,45 @@
1
- require 'spec_helper'
2
-
3
- describe "Attribute Default Value" do
1
+ # frozen_string_literal: true
4
2
 
5
- context "when given a default value" do
3
+ require 'spec_helper'
6
4
 
5
+ describe 'Attribute Default Value' do
6
+ context 'when given a default value' do
7
7
  class Meal
8
8
  include HappyMapper
9
9
  tag 'meal'
10
- attribute :type, String, :default => 'omnivore'
10
+ attribute :type, String, default: 'omnivore'
11
11
  end
12
12
 
13
13
  let(:subject) { Meal }
14
14
  let(:default_meal_type) { 'omnivore' }
15
15
 
16
- context "when no value has been specified" do
17
- it "returns the default value" do
16
+ context 'when no value has been specified' do
17
+ it 'returns the default value' do
18
18
  meal = subject.parse('<meal />')
19
19
  expect(meal.type).to eq default_meal_type
20
20
  end
21
21
  end
22
22
 
23
- context "when saving to xml" do
24
-
25
- let(:expected_xml) { %{<?xml version="1.0"?>\n<meal/>\n} }
23
+ context 'when saving to xml' do
24
+ let(:expected_xml) { %(<?xml version="1.0"?>\n<meal/>\n) }
26
25
 
27
- it "the default value is not included" do
26
+ it 'the default value is not included' do
28
27
  meal = subject.new
29
28
  expect(meal.to_xml).to eq expected_xml
30
29
  end
31
30
  end
32
31
 
33
- context "when a new, non-nil value has been set" do
34
- it "returns the new value" do
32
+ context 'when a new, non-nil value has been set' do
33
+ it 'returns the new value' do
35
34
  meal = subject.parse('<meal />')
36
35
  meal.type = 'vegan'
37
36
 
38
37
  expect(meal.type).to_not eq default_meal_type
39
38
  end
40
39
 
41
- let(:expected_xml) { %{<?xml version="1.0"?>\n<meal type="kosher"/>\n} }
40
+ let(:expected_xml) { %(<?xml version="1.0"?>\n<meal type="kosher"/>\n) }
42
41
 
43
- it "saves the new value to the xml" do
42
+ it 'saves the new value to the xml' do
44
43
  meal = subject.new
45
44
  meal.type = 'kosher'
46
45
  expect(meal.to_xml).to eq expected_xml