nokogiri-happymapper 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
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