nokogiri-happymapper 0.5.6 → 0.5.7

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.
Files changed (38) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +5 -1
  3. data/README.md +74 -53
  4. data/lib/happymapper/anonymous_mapper.rb +114 -0
  5. data/lib/happymapper/attribute.rb +20 -2
  6. data/lib/happymapper/element.rb +52 -2
  7. data/lib/happymapper/item.rb +89 -182
  8. data/lib/happymapper/supported_types.rb +140 -0
  9. data/lib/happymapper/text_node.rb +6 -1
  10. data/lib/happymapper/version.rb +3 -0
  11. data/lib/happymapper.rb +42 -22
  12. data/spec/attribute_default_value_spec.rb +50 -0
  13. data/spec/fixtures/default_namespace_combi.xml +2 -1
  14. data/spec/happymapper/attribute_spec.rb +12 -0
  15. data/spec/happymapper/element_spec.rb +9 -0
  16. data/spec/{happymapper_item_spec.rb → happymapper/item_spec.rb} +5 -5
  17. data/spec/happymapper/text_node_spec.rb +9 -0
  18. data/spec/happymapper_parse_spec.rb +87 -0
  19. data/spec/happymapper_spec.rb +9 -3
  20. data/spec/ignay_spec.rb +22 -22
  21. data/spec/inheritance_spec.rb +61 -0
  22. data/spec/parse_with_object_to_update_spec.rb +111 -0
  23. data/spec/spec_helper.rb +1 -1
  24. data/spec/to_xml_spec.rb +200 -0
  25. data/spec/to_xml_with_namespaces_spec.rb +196 -0
  26. data/spec/wilcard_tag_name_spec.rb +96 -0
  27. data/spec/wrap_spec.rb +82 -0
  28. data/spec/xpath_spec.rb +60 -59
  29. metadata +34 -33
  30. data/TODO +0 -0
  31. data/spec/happymapper_attribute_spec.rb +0 -21
  32. data/spec/happymapper_element_spec.rb +0 -21
  33. data/spec/happymapper_generic_base_spec.rb +0 -92
  34. data/spec/happymapper_text_node_spec.rb +0 -21
  35. data/spec/happymapper_to_xml_namespaces_spec.rb +0 -196
  36. data/spec/happymapper_to_xml_spec.rb +0 -203
  37. data/spec/happymapper_wrap_spec.rb +0 -69
  38. data/spec/parse_instance_spec.rb +0 -129
@@ -2,8 +2,6 @@ module HappyMapper
2
2
  class Item
3
3
  attr_accessor :name, :type, :tag, :options, :namespace
4
4
 
5
- Types = [String, Float, Time, Date, DateTime, Integer, Boolean]
6
-
7
5
  # options:
8
6
  # :deep => Boolean False to only parse element's children, True to include
9
7
  # grandchildren and all others down the chain (// in xpath)
@@ -26,56 +24,24 @@ module HappyMapper
26
24
  def constant
27
25
  @constant ||= constantize(type)
28
26
  end
29
-
27
+
30
28
  #
31
29
  # @param [XMLNode] node the xml node that is being parsed
32
30
  # @param [String] namespace the name of the namespace
33
31
  # @param [Hash] xpath_options additional xpath options
34
- #
32
+ #
35
33
  def from_xml_node(node, namespace, xpath_options)
36
-
37
- # If the item is defined as a primitive type then cast the value to that type
38
- # else if the type is XMLContent then store the xml value
39
- # else the type, specified, needs to handle the parsing.
40
- #
41
-
42
- if primitive?
43
- find(node, namespace, xpath_options) do |n|
44
- if n.respond_to?(:content)
45
- typecast(n.content)
46
- else
47
- typecast(n)
48
- end
49
- end
34
+
35
+ if suported_type_registered?
36
+ find(node, namespace, xpath_options) { |n| process_node_as_supported_type(n) }
50
37
  elsif constant == XmlContent
51
- find(node, namespace, xpath_options) do |n|
52
- n = n.children if n.respond_to?(:children)
53
- n.respond_to?(:to_xml) ? n.to_xml : n.to_s
54
- end
38
+ find(node, namespace, xpath_options) { |n| process_node_as_xml_content(n) }
39
+ elsif custom_parser_defined?
40
+ find(node, namespace, xpath_options) { |n| process_node_with_custom_parser(n) }
55
41
  else
56
-
57
- # When not a primitive type or XMLContent then default to using the
58
- # class method #parse of the type class. If the option 'parser' has been
59
- # defined then call that method on the type class instead of #parse
60
-
61
- if options[:parser]
62
- find(node, namespace, xpath_options) do |n|
63
- if n.respond_to?(:content) && !options[:raw]
64
- value = n.content
65
- else
66
- value = n.to_s
67
- end
68
-
69
- begin
70
- constant.send(options[:parser].to_sym, value)
71
- rescue
72
- nil
73
- end
74
- end
75
- else
76
- constant.parse(node, options.merge(:namespaces => xpath_options))
77
- end
42
+ process_node_with_default_parser(node,:namespaces => xpath_options)
78
43
  end
44
+
79
45
  end
80
46
 
81
47
  def xpath(namespace = self.namespace)
@@ -86,166 +52,107 @@ module HappyMapper
86
52
  #puts "xpath: #{xpath}"
87
53
  xpath
88
54
  end
89
-
90
- # @return [Boolean] true if the type defined for the item is defined in the
91
- # list of primite types {Types}.
92
- def primitive?
93
- Types.include?(constant)
94
- end
95
-
96
- def element?
97
- @xml_type == 'element'
98
- end
99
-
100
- def attribute?
101
- @xml_type == 'attribute'
102
- end
103
-
104
- def text_node?
105
- @xml_type == 'textnode'
106
- end
107
55
 
108
56
  def method_name
109
57
  @method_name ||= name.tr('-', '_')
110
58
  end
111
59
 
112
60
  #
113
- # When the type of the item is a primitive type, this will convert value specifed
114
- # to the particular primitive type. If it fails during this process it will
115
- # return the original String value.
116
- #
61
+ # Convert the value into the correct type.
62
+ #
117
63
  # @param [String] value the string value parsed from the XML value that will
118
64
  # be converted to the particular primitive type.
119
- #
65
+ #
120
66
  # @return [String,Float,Time,Date,DateTime,Boolean,Integer] the converted value
121
67
  # to the new type.
122
68
  #
123
69
  def typecast(value)
124
- return value if value.kind_of?(constant) || value.nil?
125
- begin
126
- if constant == String then value.to_s
127
- elsif constant == Float then value.to_f
128
- elsif constant == Time then Time.parse(value.to_s) rescue Time.at(value.to_i)
129
- elsif constant == Date then Date.parse(value.to_s)
130
- elsif constant == DateTime then DateTime.parse(value.to_s)
131
- elsif constant == Boolean then ['true', 't', '1'].include?(value.to_s.downcase)
132
- elsif constant == Integer
133
- # ganked from datamapper
134
- value_to_i = value.to_i
135
- if value_to_i == 0 && value != '0'
136
- value_to_s = value.to_s
137
- begin
138
- Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
139
- rescue ArgumentError
140
- nil
141
- end
142
- else
143
- value_to_i
144
- end
145
- else
146
- value
147
- end
148
- rescue
149
- value
150
- end
70
+ typecaster(value).apply(value)
151
71
  end
152
72
 
73
+
153
74
  private
154
75
 
155
- #
156
- # Convert any String defined types into their constant version so that
157
- # the method #parse or the custom defined parser method would be used.
158
- #
159
- # @param [String,Constant] type is the name of the class or the constant
160
- # for the class.
161
- # @return [Constant] the constant of the type
162
- #
163
- def constantize(type)
164
- if type.is_a?(String)
165
- names = type.split('::')
166
- constant = Object
167
- names.each do |name|
168
- constant =
169
- if constant.const_defined?(name)
170
- constant.const_get(name)
171
- else
172
- constant.const_missing(name)
173
- end
174
- end
175
- constant
176
- else
177
- type
178
- end
76
+ # @return [Boolean] true if the type defined for the item is defined in the
77
+ # list of support types.
78
+ def suported_type_registered?
79
+ SupportedTypes.types.map {|caster| caster.type }.include?(constant)
80
+ end
81
+
82
+ # @return [#apply] the typecaster object that will be able to convert
83
+ # the value into a value with the correct type.
84
+ def typecaster(value)
85
+ SupportedTypes.types.find { |caster| caster.apply?(value,constant) }
86
+ end
87
+
88
+ #
89
+ # Processes a Nokogiri::XML::Node as a supported type
90
+ #
91
+ def process_node_as_supported_type(node)
92
+ content = node.respond_to?(:content) ? node.content : node
93
+ typecast(content)
94
+ end
95
+
96
+ #
97
+ # Process a Nokogiri::XML::Node as XML Content
98
+ #
99
+ def process_node_as_xml_content(node)
100
+ node = node.children if node.respond_to?(:children)
101
+ node.respond_to?(:to_xml) ? node.to_xml : node.to_s
102
+ end
103
+
104
+ #
105
+ # A custom parser is a custom parse method on the class. When the parser
106
+ # option has been set this value is the name of the method which will be
107
+ # used to parse the node content.
108
+ #
109
+ def custom_parser_defined?
110
+ options[:parser]
111
+ end
112
+
113
+ def process_node_with_custom_parser(node)
114
+ if node.respond_to?(:content) && !options[:raw]
115
+ value = node.content
116
+ else
117
+ value = node.to_s
179
118
  end
180
119
 
120
+ begin
121
+ constant.send(options[:parser].to_sym, value)
122
+ rescue
123
+ nil
124
+ end
125
+ end
181
126
 
182
- def find(node, namespace, xpath_options, &block)
183
- if self.namespace && xpath_options["xmlns:#{self.namespace}"]
184
- # from the class definition
185
- namespace = self.namespace
186
- elsif options[:namespace] && xpath_options["xmlns:#{options[:namespace]}"]
187
- namespace = options[:namespace]
188
- end
189
-
190
- if element?
191
- if options[:single]
192
-
193
- result = nil
194
-
195
- if options[:xpath]
196
- result = node.xpath(options[:xpath], xpath_options)
197
- else
198
- result = node.xpath(xpath(namespace), xpath_options)
199
- end
200
-
201
- if result
202
- value = options[:single] ? yield(result.first) : result.map {|r| yield r }
203
- handle_attributes_option(result, value, xpath_options)
204
-
205
- value
206
- end
207
- else
208
-
209
- target_path = options[:xpath] ? options[:xpath] : xpath(namespace)
210
-
211
- results = node.xpath(target_path, xpath_options).collect do |result|
212
- value = yield(result)
213
- handle_attributes_option(result, value, xpath_options)
214
- value
215
- end
216
- results
217
- end
218
- elsif attribute?
219
-
220
- if options[:xpath]
221
- yield(node.xpath(options[:xpath],xpath_options))
127
+ def process_node_with_default_parser(node,parse_options)
128
+ constant.parse(node,options.merge(parse_options))
129
+ end
130
+
131
+ #
132
+ # Convert any String defined types into their constant version so that
133
+ # the method #parse or the custom defined parser method would be used.
134
+ #
135
+ # @param [String,Constant] type is the name of the class or the constant
136
+ # for the class.
137
+ # @return [Constant] the constant of the type
138
+ #
139
+ def constantize(type)
140
+ type.is_a?(String) ? convert_string_to_constant(type) : type
141
+ end
142
+
143
+ def convert_string_to_constant(type)
144
+ names = type.split('::')
145
+ constant = Object
146
+ names.each do |name|
147
+ constant =
148
+ if constant.const_defined?(name)
149
+ constant.const_get(name)
222
150
  else
223
- yield(node[tag])
151
+ constant.const_missing(name)
224
152
  end
225
-
226
- else # text node
227
- yield(node.children.detect{|c| c.text?})
228
- end
229
153
  end
154
+ constant
155
+ end
230
156
 
231
- def handle_attributes_option(result, value, xpath_options)
232
- if options[:attributes].is_a?(Hash)
233
- result = result.first unless result.respond_to?(:attribute_nodes)
234
-
235
- result.attribute_nodes.each do |xml_attribute|
236
- if attribute_options = options[:attributes][xml_attribute.name.to_sym]
237
- attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace, xpath_options)
238
-
239
- result.instance_eval <<-EOV
240
- def value.#{xml_attribute.name}
241
- #{attribute_value.inspect}
242
- end
243
- EOV
244
- end # if attributes_options
245
- end # attribute_nodes.each
246
- end # if options[:attributes]
247
- end # def handle...
248
-
249
- # end private methods
250
157
  end
251
158
  end
@@ -0,0 +1,140 @@
1
+ module HappyMapper
2
+ module SupportedTypes
3
+ extend self
4
+
5
+ #
6
+ # All of the registerd supported types that can be parsed.
7
+ #
8
+ # All types defined here are set through #register.
9
+ #
10
+ def types
11
+ @types ||= []
12
+ end
13
+
14
+ #
15
+ # Add a new converter to the list of supported types. A converter
16
+ # is an object that adheres to the protocol which is defined with two
17
+ # methods #apply?(value,convert_to_type) and #apply(value).
18
+ #
19
+ # @example Defining a class that would process `nil` or values that have
20
+ # already been converted.
21
+ #
22
+ # class NilOrAlreadyConverted
23
+ # def apply?(value,convert_to_type)
24
+ # value.kind_of?(convert_to_type) || value.nil?
25
+ # end
26
+ #
27
+ # def apply(value)
28
+ # value
29
+ # end
30
+ # end
31
+ #
32
+ #
33
+ def register(type_converter)
34
+ types.push type_converter
35
+ end
36
+
37
+ #
38
+ # An additional shortcut registration method that assumes that you want
39
+ # to perform a conversion on a specific type. A block is provided which
40
+ # is the operation to perform when #apply(value) has been called.
41
+ #
42
+ # @example Registering a DateTime parser
43
+ #
44
+ # HappyMapper::SupportedTypes.register_type DateTime do |value|
45
+ # DateTime.parse(value,to_s)
46
+ # end
47
+ #
48
+ def register_type(type,&block)
49
+ register CastWhenType.new(type,&block)
50
+ end
51
+
52
+ #
53
+ # Many of the conversions are based on type. When the type specified
54
+ # matches then perform the action specified in the specified block.
55
+ # If no block is provided the value is simply returned.
56
+ #
57
+ class CastWhenType
58
+ attr_reader :type
59
+
60
+ def initialize(type,&block)
61
+ @type = type
62
+ @apply_block = block || no_operation
63
+ end
64
+
65
+ def no_operation
66
+ lambda {|value| value }
67
+ end
68
+
69
+ def apply?(value,convert_to_type)
70
+ convert_to_type == type
71
+ end
72
+
73
+ def apply(value)
74
+ @apply_block.call(value)
75
+ end
76
+ end
77
+
78
+ #
79
+ # For the cases when the value is nil or is already the
80
+ # intended type then no work needs to be done and the
81
+ # value simply can be returned.
82
+ #
83
+ class NilOrAlreadyConverted
84
+
85
+ def type
86
+ NilClass
87
+ end
88
+
89
+ def apply?(value,convert_to_type)
90
+ value.kind_of?(convert_to_type) || value.nil?
91
+ end
92
+
93
+ def apply(value)
94
+ value
95
+ end
96
+ end
97
+
98
+ register NilOrAlreadyConverted.new
99
+
100
+ register_type String do |value|
101
+ value.to_s
102
+ end
103
+
104
+ register_type Float do |value|
105
+ value.to_f
106
+ end
107
+
108
+ register_type Time do |value|
109
+ Time.parse(value.to_s) rescue Time.at(value.to_i)
110
+ end
111
+
112
+ register_type Date do |value|
113
+ Date.parse(value.to_s)
114
+ end
115
+
116
+ register_type DateTime do |value|
117
+ DateTime.parse(value.to_s)
118
+ end
119
+
120
+ register_type Boolean do |value|
121
+ ['true', 't', '1'].include?(value.to_s.downcase)
122
+ end
123
+
124
+ register_type Integer do |value|
125
+ value_to_i = value.to_i
126
+ if value_to_i == 0 && value != '0'
127
+ value_to_s = value.to_s
128
+ begin
129
+ Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
130
+ rescue ArgumentError
131
+ nil
132
+ end
133
+ else
134
+ value_to_i
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ end
@@ -1,3 +1,8 @@
1
1
  module HappyMapper
2
- class TextNode < Item; end
2
+ class TextNode < Item
3
+
4
+ def find(node, namespace, xpath_options)
5
+ yield(node.children.detect{|c| c.text?})
6
+ end
7
+ end
3
8
  end
@@ -0,0 +1,3 @@
1
+ module HappyMapper
2
+ VERSION = "0.5.7"
3
+ end
data/lib/happymapper.rb CHANGED
@@ -1,21 +1,34 @@
1
1
  require 'nokogiri'
2
2
  require 'date'
3
3
  require 'time'
4
-
5
- class Boolean; end
6
- class XmlContent; end
4
+ require 'happymapper/anonymous_mapper'
7
5
 
8
6
  module HappyMapper
7
+ class Boolean; end
8
+ class XmlContent; end
9
9
 
10
- VERSION = "0.5.6"
10
+ extend AnonymousMapper
11
11
 
12
12
  DEFAULT_NS = "happymapper"
13
13
 
14
14
  def self.included(base)
15
- base.instance_variable_set("@attributes", {})
16
- base.instance_variable_set("@elements", {})
17
- base.instance_variable_set("@registered_namespaces", {})
18
- base.instance_variable_set("@wrapper_anonymous_classes", {})
15
+ if !(base.superclass <= HappyMapper)
16
+ base.instance_eval do
17
+ @attributes = []
18
+ @elements = []
19
+ @registered_namespaces = {}
20
+ @wrapper_anonymous_classes = {}
21
+ end
22
+ else
23
+ base.instance_eval do
24
+ @attributes = superclass.attributes.dup
25
+ @elements = superclass.elements.dup
26
+ @registered_namespaces =
27
+ superclass.instance_variable_get(:@registered_namespaces).dup
28
+ @wrapper_anonymous_classes =
29
+ superclass.instance_variable_get(:@wrapper_anonymous_classes).dup
30
+ end
31
+ end
19
32
 
20
33
  base.extend ClassMethods
21
34
  end
@@ -39,8 +52,7 @@ module HappyMapper
39
52
  #
40
53
  def attribute(name, type, options={})
41
54
  attribute = Attribute.new(name, type, options)
42
- @attributes[to_s] ||= []
43
- @attributes[to_s] << attribute
55
+ @attributes << attribute
44
56
  attr_accessor attribute.method_name.intern
45
57
  end
46
58
 
@@ -51,7 +63,7 @@ module HappyMapper
51
63
  # an empty array is returned when there have been no attributes defined.
52
64
  #
53
65
  def attributes
54
- @attributes[to_s] || []
66
+ @attributes
55
67
  end
56
68
 
57
69
  #
@@ -95,8 +107,7 @@ module HappyMapper
95
107
  #
96
108
  def element(name, type, options={})
97
109
  element = Element.new(name, type, options)
98
- @elements[to_s] ||= []
99
- @elements[to_s] << element
110
+ @elements << element
100
111
  attr_accessor element.method_name.intern
101
112
  end
102
113
 
@@ -108,7 +119,7 @@ module HappyMapper
108
119
  # defined.
109
120
  #
110
121
  def elements
111
- @elements[to_s] || []
122
+ @elements
112
123
  end
113
124
 
114
125
  #
@@ -305,9 +316,7 @@ module HappyMapper
305
316
  namespace = options[:namespace]
306
317
  elsif namespaces.has_key?("xmlns")
307
318
  namespace ||= DEFAULT_NS
308
- default_namespace = namespaces.delete("xmlns")
309
- namespaces[namespace] ||= default_namespace
310
- namespaces["xmlns:#{namespaces.key(default_namespace)}"] = default_namespace
319
+ namespaces[DEFAULT_NS] = namespaces.delete("xmlns")
311
320
  elsif namespaces.has_key?(DEFAULT_NS)
312
321
  namespace ||= DEFAULT_NS
313
322
  end
@@ -372,7 +381,9 @@ module HappyMapper
372
381
  obj = options[:update] ? options[:update] : new
373
382
 
374
383
  attributes.each do |attr|
375
- obj.send("#{attr.method_name}=",attr.from_xml_node(n, namespace, namespaces))
384
+ value = attr.from_xml_node(n, namespace, namespaces)
385
+ value = attr.default if value.nil?
386
+ obj.send("#{attr.method_name}=", value)
376
387
  end
377
388
 
378
389
  elements.each do |elem|
@@ -431,7 +442,14 @@ module HappyMapper
431
442
  collection
432
443
  end
433
444
  end
445
+ end
434
446
 
447
+ # Set all attributes with a default to their default values
448
+ def initialize
449
+ super
450
+ self.class.attributes.reject {|attr| attr.default.nil?}.each do |attr|
451
+ send("#{attr.method_name}=", attr.default)
452
+ end
435
453
  end
436
454
 
437
455
  #
@@ -481,6 +499,7 @@ module HappyMapper
481
499
  unless attribute.options[:read_only]
482
500
 
483
501
  value = send(attribute.method_name)
502
+ value = nil if value == attribute.default
484
503
 
485
504
  #
486
505
  # If the attribute defines an on_save lambda/proc or value that maps to
@@ -701,7 +720,8 @@ module HappyMapper
701
720
 
702
721
  end
703
722
 
704
- require File.dirname(__FILE__) + '/happymapper/item'
705
- require File.dirname(__FILE__) + '/happymapper/attribute'
706
- require File.dirname(__FILE__) + '/happymapper/element'
707
- require File.dirname(__FILE__) + '/happymapper/text_node'
723
+ require 'happymapper/supported_types'
724
+ require 'happymapper/item'
725
+ require 'happymapper/attribute'
726
+ require 'happymapper/element'
727
+ require 'happymapper/text_node'
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Attribute Default Value" do
4
+
5
+ context "when given a default value" do
6
+
7
+ class Meal
8
+ include HappyMapper
9
+ tag 'meal'
10
+ attribute :type, String, :default => 'omnivore'
11
+ end
12
+
13
+ let(:subject) { Meal }
14
+ let(:default_meal_type) { 'omnivore' }
15
+
16
+ context "when no value has been specified" do
17
+ it "returns the default value" do
18
+ meal = subject.parse('<meal />')
19
+ expect(meal.type).to eq default_meal_type
20
+ end
21
+ end
22
+
23
+ context "when saving to xml" do
24
+
25
+ let(:expected_xml) { %{<?xml version="1.0"?>\n<meal/>\n} }
26
+
27
+ it "the default value is not included" do
28
+ meal = subject.new
29
+ expect(meal.to_xml).to eq expected_xml
30
+ end
31
+ end
32
+
33
+ context "when a new, non-nil value has been set" do
34
+ it "returns the new value" do
35
+ meal = subject.parse('<meal />')
36
+ meal.type = 'vegan'
37
+
38
+ expect(meal.type).to_not eq default_meal_type
39
+ end
40
+
41
+ let(:expected_xml) { %{<?xml version="1.0"?>\n<meal type="kosher"/>\n} }
42
+
43
+ it "saves the new value to the xml" do
44
+ meal = subject.new
45
+ meal.type = 'kosher'
46
+ expect(meal.to_xml).to eq expected_xml
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,6 @@
1
1
  <?xml version="1.0"?>
2
- <bk:book xmlns="urn:ISBN:0-395-36341-6" xmlns:bk="urn:loc.gov:books">
2
+ <bk:book xmlns:bk="urn:loc.gov:books" xmlns:p="urn:loc.gov:people" xmlns="urn:ISBN:0-395-36341-6">
3
+ <p:author>Frank Gilbreth</p:author>
3
4
  <bk:title>Cheaper by the Dozen</bk:title>
4
5
  <number>1568491379</number>
5
6
  </bk:book>
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe HappyMapper::Attribute do
4
+
5
+ describe "initialization" do
6
+ it 'accepts :default option' do
7
+ attr = described_class.new(:foo, String, :default => 'foobar')
8
+ attr.default.should == 'foobar'
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe HappyMapper::Element do
4
+ describe "initialization" do
5
+ before do
6
+ @attr = HappyMapper::Element.new(:foo, String)
7
+ end
8
+ end
9
+ end