nokogiri-happymapper 0.9.0 → 0.10.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: df07426975e91e50aa2aec4abd1583c85021a314296b10b06079e09126198120
4
- data.tar.gz: 910f91cb9ed649317e82605c8571c9a59744e23d53a327e2b228f32a8f275758
3
+ metadata.gz: 0c2ddd36e3f39dfd51fdfdb4118c2cd3fe029dd145815384806759446e2c87ed
4
+ data.tar.gz: f48845f6496492351a180e93622ed99036ce2a30238a44cef1bb335a7f64a879
5
5
  SHA512:
6
- metadata.gz: 9e2c3a0b37ab88d2887b6c5b3e6043c33606102a2e8389e591f259d26b5bd1cc973dcdcb73316b287c0132f2ce5e9c9f4fd3ecfa8904f779eccc5833bfab4342
7
- data.tar.gz: ba72beaa7d76b55268c0da2e0957604a280d66cba3b334a6fc4a0ee483afc21e3f696d5fd0c5c5ac6d103f94f9a6cab9b20c6541fd5ef36043b5a28a59aa9e05
6
+ metadata.gz: bed47897fd2164b210b57e4a501c48424350db82de9b33992fa48f65377d4cddf51b77dcb353af129ed769dcb0cac506e704fd439a0a283c42b002621dbe910c
7
+ data.tar.gz: e531831bab32cd36eb3aa8471d4d85ae8213a9ecf78ef27cd41ca18087cc3d5782bf0092b6c94f0b03d989f1d7a4ae097e10639107de66bd3447e0e845cd2e91
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.1 / 2025-10-25
4
+
5
+ * Fix namespace prefix collisions ([#231] by [petersen])
6
+
7
+ [petersen]: https://github.com/petersen
8
+ [#231]: https://github.com/mvz/happymapper/pull/231
9
+
10
+ ## 0.10.0 / 2024-01-05
11
+
12
+ * Fix typo in README code sample ([#198] by [Spone])
13
+ * Improve custom parser option ([#219] by [dmke])
14
+ * Force namespace to be specified separately from tag ([#222] by [mvz])
15
+ * Pass options into wrapping element ([#225] by [jbennett])
16
+ * Support Ruby 3.0 through 3.3 and JRuby 9.4, dropping support for Ruby 2.6 and 2.7
17
+ ([#209], [#210], [#211] and [#230] by [mvz])
18
+
19
+ [Spone]: https://github.com/Spone
20
+ [dmke]: https://github.com/dmke
21
+ [jbennett]: https://github.com/jbennett
22
+ [mvz]: https://github.com/mvz
23
+
24
+ [#198]: https://github.com/mvz/happymapper/pull/198
25
+ [#209]: https://github.com/mvz/happymapper/pull/209
26
+ [#210]: https://github.com/mvz/happymapper/pull/210
27
+ [#211]: https://github.com/mvz/happymapper/pull/211
28
+ [#219]: https://github.com/mvz/happymapper/pull/219
29
+ [#222]: https://github.com/mvz/happymapper/pull/222
30
+ [#225]: https://github.com/mvz/happymapper/pull/225
31
+ [#230]: https://github.com/mvz/happymapper/pull/230
32
+
3
33
  ## 0.9.0 / 2022-01-21
4
34
 
5
35
  * Add official support for Ruby 2.7, 3.0 and 3.1
data/README.md CHANGED
@@ -241,7 +241,7 @@ the instance variable `@streets` if we ever need to the values as an array.
241
241
  Attributes are absolutely the same as `element` or `has_many`
242
242
 
243
243
  ```ruby
244
- attribute :location, String, tag: 'location
244
+ attribute :location, String, tag: 'location'
245
245
  ```
246
246
 
247
247
  Again, you can omit the tag if the attribute accessor symbol matches the name
@@ -3,8 +3,9 @@
3
3
  module HappyMapper
4
4
  class AnonymousMapper
5
5
  def parse(xml_content)
6
- # TODO: this should be able to handle all the types of functionality that parse is able
7
- # to handle which includes the text, xml document, node, fragment, etc.
6
+ # TODO: this should be able to handle all the types of functionality that
7
+ # parse is able to handle which includes the text, xml document, node,
8
+ # fragment, etc.
8
9
  xml = Nokogiri::XML(xml_content)
9
10
 
10
11
  klass = create_happymapper_class_from_node(xml.root)
@@ -26,7 +27,7 @@ module HappyMapper
26
27
  word = camel_cased_word.to_s.dup
27
28
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
28
29
  word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
29
- word.tr!('-', '_')
30
+ word.tr!("-", "_")
30
31
  word.downcase!
31
32
  word
32
33
  end
@@ -41,6 +42,7 @@ module HappyMapper
41
42
  klass = Class.new
42
43
  klass.class_eval do
43
44
  include HappyMapper
45
+
44
46
  tag tag_name
45
47
  end
46
48
  klass
@@ -80,7 +82,7 @@ module HappyMapper
80
82
  # some content.
81
83
 
82
84
  if node.text?
83
- klass.content :content, String if node.content.strip != ''
85
+ klass.content :content, String if node.content.strip != ""
84
86
  return
85
87
  end
86
88
 
@@ -101,7 +103,7 @@ module HappyMapper
101
103
  options[:tag] = node.name
102
104
  namespace = node.namespace
103
105
  options[:namespace] = namespace.prefix if namespace
104
- options[:xpath] = './' unless element_type == String
106
+ options[:xpath] = "./" unless element_type == String
105
107
 
106
108
  klass.send(method, element_name, element_type, options)
107
109
  end
@@ -0,0 +1,447 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "happymapper/syntax_error"
4
+
5
+ module HappyMapper
6
+ # Class methods to be applied to classes that include the HappyMapper module.
7
+ module ClassMethods
8
+ #
9
+ # The xml has the following attributes defined.
10
+ #
11
+ # @example
12
+ #
13
+ # "<country code='de'>Germany</country>"
14
+ #
15
+ # # definition of the 'code' attribute within the class
16
+ # attribute :code, String
17
+ #
18
+ # @param [Symbol] name the name of the accessor that is created
19
+ # @param [String,Class] type the class name of the name of the class whcih
20
+ # the object will be converted upon parsing
21
+ # @param [Hash] options additional parameters to send to the relationship
22
+ #
23
+ def attribute(name, type, options = {})
24
+ attribute = Attribute.new(name, type, options)
25
+ @attributes[name] = attribute
26
+ attr_accessor attribute.method_name.intern
27
+ end
28
+
29
+ #
30
+ # The elements defined through {#attribute}.
31
+ #
32
+ # @return [Array<Attribute>] a list of the attributes defined for this class;
33
+ # an empty array is returned when there have been no attributes defined.
34
+ #
35
+ def attributes
36
+ @attributes.values
37
+ end
38
+
39
+ #
40
+ # Register a namespace that is used to persist the object namespace back to
41
+ # XML.
42
+ #
43
+ # @example
44
+ #
45
+ # register_namespace 'prefix', 'http://www.unicornland.com/prefix'
46
+ #
47
+ # # the output will contain the namespace defined
48
+ #
49
+ # "<outputXML xmlns:prefix="http://www.unicornland.com/prefix">
50
+ # ...
51
+ # </outputXML>"
52
+ #
53
+ # @param [String] name the xml prefix
54
+ # @param [String] href url for the xml namespace
55
+ #
56
+ def register_namespace(name, href)
57
+ @registered_namespaces.merge!(name => href)
58
+ end
59
+
60
+ #
61
+ # An element defined in the XML that is parsed.
62
+ #
63
+ # @example
64
+ #
65
+ # "<address location='home'>
66
+ # <city>Oldenburg</city>
67
+ # </address>"
68
+ #
69
+ # # definition of the 'city' element within the class
70
+ #
71
+ # element :city, String
72
+ #
73
+ # @param [Symbol] name the name of the accessor that is created
74
+ # @param [String,Class] type the class name of the name of the class whcih
75
+ # the object will be converted upon parsing
76
+ # @param [Hash] options additional parameters to send to the relationship
77
+ #
78
+ def element(name, type, options = {})
79
+ element = Element.new(name, type, options)
80
+ attr_accessor element.method_name.intern unless @elements[name]
81
+ @elements[name] = element
82
+ end
83
+
84
+ #
85
+ # The elements defined through {#element}, {#has_one}, and {#has_many}.
86
+ #
87
+ # @return [Array<Element>] a list of the elements contained defined for this
88
+ # class; an empty array is returned when there have been no elements
89
+ # defined.
90
+ #
91
+ def elements
92
+ @elements.values
93
+ end
94
+
95
+ #
96
+ # The value stored in the text node of the current element.
97
+ #
98
+ # @example
99
+ #
100
+ # "<firstName>Michael Jackson</firstName>"
101
+ #
102
+ # # definition of the 'firstName' text node within the class
103
+ #
104
+ # content :first_name, String
105
+ #
106
+ # @param [Symbol] name the name of the accessor that is created
107
+ # @param [String,Class] type the class name of the name of the class whcih
108
+ # the object will be converted upon parsing. By Default String class will be taken.
109
+ # @param [Hash] options additional parameters to send to the relationship
110
+ #
111
+ def content(name, type = String, options = {})
112
+ @content = TextNode.new(name, type, options)
113
+ attr_accessor @content.method_name.intern
114
+ end
115
+
116
+ #
117
+ # Sets the object to have xml content, this will assign the XML contents
118
+ # that are parsed to the attribute accessor xml_content. The object will
119
+ # respond to the method #xml_content and will return the XML data that
120
+ # it has parsed.
121
+ #
122
+ def has_xml_content
123
+ attr_accessor :xml_content
124
+ end
125
+
126
+ #
127
+ # The object has one of these elements in the XML. If there are multiple,
128
+ # the last one will be set to this value.
129
+ #
130
+ # @param [Symbol] name the name of the accessor that is created
131
+ # @param [String,Class] type the class name of the name of the class whcih
132
+ # the object will be converted upon parsing
133
+ # @param [Hash] options additional parameters to send to the relationship
134
+ #
135
+ # @see #element
136
+ #
137
+ def has_one(name, type, options = {})
138
+ element name, type, { single: true }.merge(options)
139
+ end
140
+
141
+ #
142
+ # The object has many of these elements in the XML.
143
+ #
144
+ # @param [Symbol] name the name of accessor that is created
145
+ # @param [String,Class] type the class name or the name of the class which
146
+ # the object will be converted upon parsing.
147
+ # @param [Hash] options additional parameters to send to the relationship
148
+ #
149
+ # @see #element
150
+ #
151
+ def has_many(name, type, options = {})
152
+ element name, type, { single: false }.merge(options)
153
+ end
154
+
155
+ #
156
+ # The list of registered after_parse callbacks.
157
+ #
158
+ def after_parse_callbacks
159
+ @after_parse_callbacks ||= []
160
+ end
161
+
162
+ #
163
+ # Register a new after_parse callback, given as a block.
164
+ #
165
+ # @yield [object] Yields the newly-parsed object to the block after parsing.
166
+ # Sub-objects will be already populated.
167
+ def after_parse(&block)
168
+ after_parse_callbacks.push(block)
169
+ end
170
+
171
+ #
172
+ # Specify a namespace if a node and all its children are all namespaced
173
+ # elements. This is simpler than passing the :namespace option to each
174
+ # defined element.
175
+ #
176
+ # @param [String] namespace the namespace to set as default for the class
177
+ # element.
178
+ #
179
+ def namespace(namespace = nil)
180
+ @namespace = namespace if namespace
181
+ @namespace if defined? @namespace
182
+ end
183
+
184
+ #
185
+ # @param [String] new_tag_name the name for the tag
186
+ #
187
+ def tag(new_tag_name)
188
+ return if new_tag_name.nil? || (name = new_tag_name.to_s).empty?
189
+
190
+ raise SyntaxError, "Unexpected ':' in tag name #{new_tag_name}" if name.include? ":"
191
+
192
+ @tag_name = name
193
+ end
194
+
195
+ #
196
+ # The name of the tag
197
+ #
198
+ # @return [String] the name of the tag as a string, downcased
199
+ #
200
+ def tag_name
201
+ @tag_name ||= name && name.to_s.split("::")[-1].downcase
202
+ end
203
+
204
+ # There is an XML tag that needs to be known for parsing and should be generated
205
+ # during a to_xml. But it doesn't need to be a class and the contained elements should
206
+ # be made available on the parent class
207
+ #
208
+ # @param [String] name the name of the element that is just a place holder
209
+ # @param [Proc] blk the element definitions inside the place holder tag
210
+ #
211
+ def wrap(name, options = {}, &blk)
212
+ # Get an anonymous HappyMapper that has 'name' as its tag and defined
213
+ # in '&blk'. Then save that to a class instance variable for later use
214
+ wrapper = AnonymousWrapperClassFactory.get(name, &blk)
215
+ wrapper_key = wrapper.inspect
216
+ @wrapper_anonymous_classes[wrapper_key] = wrapper
217
+
218
+ # Create getter/setter for each element and attribute defined on the
219
+ # anonymous HappyMapper onto this class. They get/set the value by
220
+ # passing thru to the anonymous class.
221
+ passthrus = wrapper.attributes + wrapper.elements
222
+ passthrus.each do |item|
223
+ method_name = item.method_name
224
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
225
+ def #{method_name} # def property
226
+ @#{name} ||= # @wrapper ||=
227
+ wrapper_anonymous_classes['#{wrapper_key}'].new # wrapper_anonymous_classes['#<Class:0x0000555b7d0b9220>'].new
228
+ @#{name}.#{method_name} # @wrapper.property
229
+ end # end
230
+
231
+ def #{method_name}=(value) # def property=(value)
232
+ @#{name} ||= # @wrapper ||=
233
+ wrapper_anonymous_classes['#{wrapper_key}'].new # wrapper_anonymous_classes['#<Class:0x0000555b7d0b9220>'].new
234
+ @#{name}.#{method_name} = value # @wrapper.property = value
235
+ end # end
236
+ RUBY
237
+ end
238
+
239
+ has_one name, wrapper, options
240
+ end
241
+
242
+ # The callback defined through {.with_nokogiri_config}.
243
+ #
244
+ # @return [Proc] the proc to pass to Nokogiri to setup parse options. nil if empty.
245
+ #
246
+ attr_reader :nokogiri_config_callback
247
+
248
+ # Register a config callback according to the block Nokogori expects when
249
+ # calling Nokogiri::XML::Document.parse().
250
+ #
251
+ # See http://nokogiri.org/Nokogiri/XML/Document.html#method-c-parse
252
+ #
253
+ # @param [Proc] the proc to pass to Nokogiri to setup parse options
254
+ #
255
+ def with_nokogiri_config(&blk)
256
+ @nokogiri_config_callback = blk
257
+ end
258
+
259
+ #
260
+ # @param [Nokogiri::XML::Node,Nokogiri:XML::Document,String] xml the XML
261
+ # contents to convert into Object.
262
+ # @param [Hash] options additional information for parsing.
263
+ # :single => true if requesting a single object, otherwise it defaults
264
+ # to retuning an array of multiple items.
265
+ # :xpath information where to start the parsing
266
+ # :namespace is the namespace to use for additional information.
267
+ #
268
+ def parse(xml, options = {})
269
+ # Capture any provided namespaces and merge in any namespaces that have
270
+ # been registered on the object.
271
+ namespaces = options[:namespaces] || {}
272
+ namespaces = namespaces.merge(@registered_namespaces)
273
+
274
+ # If the XML specified is an Node then we have what we need.
275
+ if xml.is_a?(Nokogiri::XML::Node) && !xml.is_a?(Nokogiri::XML::Document)
276
+ node = xml
277
+ else
278
+
279
+ unless xml.is_a?(Nokogiri::XML::Document)
280
+ # Attempt to parse the xml value with Nokogiri XML as a document
281
+ # and select the root element
282
+ xml = Nokogiri::XML(
283
+ xml, nil, nil,
284
+ Nokogiri::XML::ParseOptions::STRICT,
285
+ &nokogiri_config_callback
286
+ )
287
+ end
288
+ # Now xml is certainly an XML document: Select the root node of the document
289
+ node = xml.root
290
+
291
+ # merge any namespaces found on the xml node into the namespace hash
292
+ namespaces = namespaces.merge(xml.collect_namespaces).merge(node.namespaces)
293
+
294
+ # if the node name is equal to the tag name then the we are parsing the
295
+ # root element and that is important to record so that we can apply
296
+ # the correct xpath on the elements of this document.
297
+
298
+ root = node.name == tag_name
299
+ end
300
+
301
+ # If the :single option has been specified or we are at the root element
302
+ # then we are going to return a single element or nil if no nodes are found
303
+ single = root || options[:single]
304
+
305
+ # if a namespace has been provided then set the current namespace to it
306
+ # or use the namespace provided by the class
307
+ # or use the 'xmlns' namespace if defined
308
+
309
+ namespace =
310
+ options[:namespace] ||
311
+ self.namespace ||
312
+ namespaces.key?("xmlns") && "xmlns"
313
+
314
+ # from the options grab any nodes present and if none are present then
315
+ # perform the following to find the nodes for the given class
316
+
317
+ nodes = options.fetch(:nodes) do
318
+ find_nodes_to_parse(options, namespace, tag_name, namespaces, node, root)
319
+ end
320
+
321
+ # Nothing matching found, we can go ahead and return
322
+ return (single ? nil : []) if nodes.empty?
323
+
324
+ # If the :limit option has been specified then we are going to slice
325
+ # our node results by that amount to allow us the ability to deal with
326
+ # a large result set of data.
327
+
328
+ limit = options[:in_groups_of] || nodes.size
329
+
330
+ # If the limit of 0 has been specified then the user obviously wants
331
+ # none of the nodes that we are serving within this batch of nodes.
332
+
333
+ return [] if limit == 0
334
+
335
+ collection = []
336
+
337
+ nodes.each_slice(limit) do |slice|
338
+ part = slice.map do |n|
339
+ parse_node(n, options, namespace, namespaces)
340
+ end
341
+
342
+ # If a block has been provided and the user has requested that the objects
343
+ # be handled in groups then we should yield the slice of the objects to them
344
+ # otherwise continue to lump them together
345
+
346
+ if block_given? && options[:in_groups_of]
347
+ yield part
348
+ else
349
+ collection += part
350
+ end
351
+ end
352
+
353
+ # If we're parsing a single element then we are going to return the first
354
+ # item in the collection. Otherwise the return response is going to be an
355
+ # entire array of items.
356
+
357
+ if single
358
+ collection.first
359
+ else
360
+ collection
361
+ end
362
+ end
363
+
364
+ # @private
365
+ def defined_content
366
+ @content if defined? @content
367
+ end
368
+
369
+ private
370
+
371
+ def find_nodes_to_parse(options, namespace, tag_name, namespaces, node, root)
372
+ # when at the root use the xpath '/' otherwise use a more gready './/'
373
+ # unless an xpath has been specified, which should overwrite default
374
+ # and finally attach the current namespace if one has been defined
375
+ xpath = if options[:xpath]
376
+ options[:xpath].to_s.sub(%r{([^/])$}, '\1/')
377
+ elsif root
378
+ "/"
379
+ else
380
+ ".//"
381
+ end
382
+ if namespace
383
+ unless namespaces.find { |name, _| ["xmlns:#{namespace}", namespace].include? name }
384
+ return []
385
+ end
386
+
387
+ xpath += "#{namespace}:"
388
+ end
389
+
390
+ # Find the nodes by the :tag option if given, or by tag_name (derived
391
+ # from the class name by default). If the :tag option is given, the
392
+ # tag_name will not be used.
393
+ if (xpath_ext = options[:tag] || tag_name)
394
+ node.xpath(xpath + xpath_ext.to_s, namespaces)
395
+ else
396
+ []
397
+ end
398
+ end
399
+
400
+ def parse_node(node, options, namespace, namespaces)
401
+ # If an existing HappyMapper object is provided, update it with the
402
+ # values from the xml being parsed. Otherwise, create a new object
403
+
404
+ obj = options[:update] || new
405
+
406
+ attributes.each do |attr|
407
+ value = attr.from_xml_node(node, namespace, namespaces)
408
+ value = attr.default if value.nil?
409
+ obj.send(:"#{attr.method_name}=", value)
410
+ end
411
+
412
+ elements.each do |elem|
413
+ obj.send(:"#{elem.method_name}=", elem.from_xml_node(node, namespace, namespaces))
414
+ end
415
+
416
+ if (content = defined_content)
417
+ obj.send(:"#{content.method_name}=",
418
+ content.from_xml_node(node, namespace, namespaces))
419
+ end
420
+
421
+ # If the HappyMapper class has the method #xml_value=,
422
+ # attr_writer :xml_value, or attr_accessor :xml_value then we want to
423
+ # assign the current xml that we just parsed to the xml_value
424
+
425
+ if obj.respond_to?(:xml_value=)
426
+ obj.xml_value = node.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
427
+ end
428
+
429
+ # If the HappyMapper class has the method #xml_content=,
430
+ # attr_write :xml_content, or attr_accessor :xml_content then we want to
431
+ # assign the child xml that we just parsed to the xml_content
432
+
433
+ if obj.respond_to?(:xml_content=)
434
+ node = node.children if node.respond_to?(:children)
435
+ obj.xml_content = node.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
436
+ end
437
+
438
+ # Call any registered after_parse callbacks for the object's class
439
+
440
+ obj.class.after_parse_callbacks.each { |callback| callback.call(obj) }
441
+
442
+ # collect the object that we have created
443
+
444
+ obj
445
+ end
446
+ end
447
+ end
@@ -44,7 +44,7 @@ module HappyMapper
44
44
  attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options)
45
45
  .from_xml_node(result, namespace, xpath_options)
46
46
 
47
- method_name = xml_attribute.name.tr('-', '_')
47
+ method_name = xml_attribute.name.tr("-", "_")
48
48
  value.define_singleton_method(method_name) { attribute_value }
49
49
  end
50
50
  end
@@ -20,7 +20,7 @@ module HappyMapper
20
20
  self.tag = options[:tag] || name.to_s
21
21
  self.options = { single: true }.merge(options.merge(name: self.name))
22
22
 
23
- @xml_type = self.class.to_s.split('::').last.downcase
23
+ @xml_type = self.class.to_s.split("::").last.downcase
24
24
  end
25
25
 
26
26
  def constant
@@ -35,20 +35,20 @@ module HappyMapper
35
35
  def from_xml_node(node, namespace, xpath_options)
36
36
  namespace = options[:namespace] if options.key?(:namespace)
37
37
 
38
- if suported_type_registered?
38
+ if custom_parser_defined?
39
+ find(node, namespace, xpath_options) { |n| process_node_with_custom_parser(n) }
40
+ elsif suported_type_registered?
39
41
  find(node, namespace, xpath_options) { |n| process_node_as_supported_type(n) }
40
42
  elsif constant == XmlContent
41
43
  find(node, namespace, xpath_options) { |n| process_node_as_xml_content(n) }
42
- elsif custom_parser_defined?
43
- find(node, namespace, xpath_options) { |n| process_node_with_custom_parser(n) }
44
44
  else
45
45
  process_node_with_default_parser(node, namespaces: xpath_options)
46
46
  end
47
47
  end
48
48
 
49
49
  def xpath(namespace = self.namespace)
50
- xpath = ''
51
- xpath += './/' if options[:deep]
50
+ xpath = ""
51
+ xpath += ".//" if options[:deep]
52
52
  xpath += "#{namespace}:" if namespace
53
53
  xpath += tag
54
54
  # puts "xpath: #{xpath}"
@@ -56,7 +56,7 @@ module HappyMapper
56
56
  end
57
57
 
58
58
  def method_name
59
- @method_name ||= name.tr('-', '_')
59
+ @method_name ||= name.tr("-", "_")
60
60
  end
61
61
 
62
62
  #
@@ -118,11 +118,17 @@ module HappyMapper
118
118
  node.to_s
119
119
  end
120
120
 
121
- begin
122
- constant.send(options[:parser].to_sym, value)
123
- rescue StandardError
124
- nil
125
- end
121
+ custom_parser = create_custom_parser(options[:parser])
122
+
123
+ custom_parser.call(value)
124
+ end
125
+
126
+ def create_custom_parser(parser)
127
+ return parser if parser.respond_to?(:call)
128
+
129
+ proc { |value|
130
+ constant.send(parser.to_sym, value)
131
+ }
126
132
  end
127
133
 
128
134
  def process_node_with_default_parser(node, parse_options)
@@ -142,7 +148,7 @@ module HappyMapper
142
148
  end
143
149
 
144
150
  def convert_string_to_constant(type)
145
- names = type.split('::')
151
+ names = type.split("::")
146
152
  constant = Object
147
153
  names.each do |name|
148
154
  constant =
@@ -104,7 +104,7 @@ module HappyMapper
104
104
 
105
105
  register_type Time do |value|
106
106
  Time.parse(value.to_s)
107
- rescue StandardError
107
+ rescue ArgumentError
108
108
  Time.at(value.to_i)
109
109
  end
110
110
 
@@ -122,7 +122,7 @@ module HappyMapper
122
122
 
123
123
  register_type Integer do |value|
124
124
  value_to_i = value.to_i
125
- if value_to_i == 0 && !value.to_s.start_with?('0')
125
+ if value_to_i == 0 && !value.to_s.start_with?("0")
126
126
  nil
127
127
  else
128
128
  value_to_i
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HappyMapper
4
+ class SyntaxError < ::StandardError
5
+ end
6
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HappyMapper
4
- VERSION = '0.9.0'
4
+ VERSION = "0.10.1"
5
5
  end