jnunemaker-happymapper 0.2.0 → 0.2.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.
- data/History +7 -0
- data/Rakefile +1 -1
- data/TODO +0 -1
- data/examples/amazon.rb +5 -0
- data/examples/current_weather.rb +2 -0
- data/examples/dashed_elements.rb +1 -1
- data/happymapper.gemspec +4 -4
- data/lib/happymapper.rb +48 -36
- data/lib/happymapper/item.rb +45 -14
- data/lib/happymapper/version.rb +1 -1
- data/lib/libxml_ext/libxml_helper.rb +1 -3
- data/spec/fixtures/pita.xml +2 -1
- data/spec/happymapper_item_spec.rb +1 -1
- data/spec/happymapper_spec.rb +31 -8
- metadata +3 -3
data/History
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.2.1
|
2
|
+
* 1 minor fix, 3 major enhancements
|
3
|
+
* fixed warnings about using XML::Parser (mojodna)
|
4
|
+
* Improved namespace support, now handles multiple namespaces and allows namespaces to be set item wide or on a per element basis (mojodna)
|
5
|
+
* Auto detect root nodes (mojodna)
|
6
|
+
* Type coercion (mojodna)
|
7
|
+
|
1
8
|
== 0.2.0
|
2
9
|
* 1 major enhancement, 2 minor ehancements
|
3
10
|
* Automatic handling of namespaces (part by Robert Lowrey and rest by John Nunemaker)
|
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ Echoe.new(ProjectName, HappyMapper::Version) do |p|
|
|
13
13
|
p.url = "http://#{ProjectName}.rubyforge.org"
|
14
14
|
p.author = "John Nunemaker"
|
15
15
|
p.email = "nunemaker@gmail.com"
|
16
|
-
p.extra_deps = [['libxml-ruby', '
|
16
|
+
p.extra_deps = [['libxml-ruby', '= 0.9.8']]
|
17
17
|
p.need_tar_gz = false
|
18
18
|
p.docs_host = WebsitePath
|
19
19
|
end
|
data/TODO
CHANGED
@@ -1 +0,0 @@
|
|
1
|
-
* doesn't do xml namespaces really (does work with default namespace though)
|
data/examples/amazon.rb
CHANGED
@@ -3,6 +3,8 @@ require File.join(dir, 'happymapper')
|
|
3
3
|
|
4
4
|
file_contents = File.read(dir + '/../spec/fixtures/pita.xml')
|
5
5
|
|
6
|
+
# The document `pita.xml` contains both a default namespace and the 'georss'
|
7
|
+
# namespace (for the 'point' element).
|
6
8
|
module PITA
|
7
9
|
class Item
|
8
10
|
include HappyMapper
|
@@ -11,6 +13,9 @@ module PITA
|
|
11
13
|
element :asin, String, :tag => 'ASIN'
|
12
14
|
element :detail_page_url, String, :tag => 'DetailPageURL'
|
13
15
|
element :manufacturer, String, :tag => 'Manufacturer', :deep => true
|
16
|
+
# this is the only element that exists in a different namespace, so it
|
17
|
+
# must be explicitly specified
|
18
|
+
element :point, String, :tag => 'point', :namespace => 'georss'
|
14
19
|
end
|
15
20
|
|
16
21
|
class Items
|
data/examples/current_weather.rb
CHANGED
@@ -5,7 +5,9 @@ file_contents = File.read(dir + '/../spec/fixtures/current_weather.xml')
|
|
5
5
|
|
6
6
|
class CurrentWeather
|
7
7
|
include HappyMapper
|
8
|
+
|
8
9
|
tag 'ob'
|
10
|
+
namespace 'aws'
|
9
11
|
element :temperature, Integer, :tag => 'temp'
|
10
12
|
element :feels_like, Integer, :tag => 'feels-like'
|
11
13
|
element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
|
data/examples/dashed_elements.rb
CHANGED
data/happymapper.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{happymapper}
|
5
|
-
s.version = "0.2.
|
5
|
+
s.version = "0.2.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["John Nunemaker"]
|
@@ -25,14 +25,14 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.specification_version = 2
|
26
26
|
|
27
27
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
-
s.add_runtime_dependency(%q<libxml-ruby>, ["
|
28
|
+
s.add_runtime_dependency(%q<libxml-ruby>, ["= 0.9.8"])
|
29
29
|
s.add_development_dependency(%q<echoe>, [">= 0"])
|
30
30
|
else
|
31
|
-
s.add_dependency(%q<libxml-ruby>, ["
|
31
|
+
s.add_dependency(%q<libxml-ruby>, ["= 0.9.8"])
|
32
32
|
s.add_dependency(%q<echoe>, [">= 0"])
|
33
33
|
end
|
34
34
|
else
|
35
|
-
s.add_dependency(%q<libxml-ruby>, ["
|
35
|
+
s.add_dependency(%q<libxml-ruby>, ["= 0.9.8"])
|
36
36
|
s.add_dependency(%q<echoe>, [">= 0"])
|
37
37
|
end
|
38
38
|
end
|
data/lib/happymapper.rb
CHANGED
@@ -5,7 +5,7 @@ require 'date'
|
|
5
5
|
require 'time'
|
6
6
|
require 'rubygems'
|
7
7
|
|
8
|
-
gem 'libxml-ruby', '
|
8
|
+
gem 'libxml-ruby', '= 0.9.8'
|
9
9
|
require 'xml'
|
10
10
|
require 'libxml_ext/libxml_helper'
|
11
11
|
|
@@ -13,7 +13,9 @@ require 'libxml_ext/libxml_helper'
|
|
13
13
|
class Boolean; end
|
14
14
|
|
15
15
|
module HappyMapper
|
16
|
-
|
16
|
+
|
17
|
+
DEFAULT_NS = "happymapper"
|
18
|
+
|
17
19
|
def self.included(base)
|
18
20
|
base.instance_variable_set("@attributes", {})
|
19
21
|
base.instance_variable_set("@elements", {})
|
@@ -50,12 +52,16 @@ module HappyMapper
|
|
50
52
|
def has_many(name, type, options={})
|
51
53
|
element name, type, {:single => false}.merge(options)
|
52
54
|
end
|
53
|
-
|
54
|
-
#
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
@
|
55
|
+
|
56
|
+
# Specify a namespace if a node and all its children are all namespaced
|
57
|
+
# elements. This is simpler than passing the :namespace option to each
|
58
|
+
# defined element.
|
59
|
+
def namespace(namespace = nil)
|
60
|
+
@namespace = namespace if namespace
|
61
|
+
@namespace
|
62
|
+
end
|
63
|
+
|
64
|
+
def tag(new_tag_name)
|
59
65
|
@tag_name = new_tag_name.to_s
|
60
66
|
end
|
61
67
|
|
@@ -65,55 +71,61 @@ module HappyMapper
|
|
65
71
|
end
|
66
72
|
end
|
67
73
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
node.
|
82
|
-
# warn "Default XML namespace present -- results are unpredictable"
|
74
|
+
def parse(xml, options = {})
|
75
|
+
# locally scoped copy of namespace for this parse run
|
76
|
+
namespace = @namespace
|
77
|
+
|
78
|
+
if xml.is_a?(XML::Node)
|
79
|
+
node = xml
|
80
|
+
else
|
81
|
+
if xml.is_a?(XML::Document)
|
82
|
+
node = xml.root
|
83
|
+
else
|
84
|
+
node = xml.to_libxml_doc.root
|
85
|
+
end
|
86
|
+
|
87
|
+
root = node.name == get_tag_name
|
83
88
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
89
|
+
|
90
|
+
# This is the entry point into the parsing pipeline, so the default
|
91
|
+
# namespace prefix registered here will propagate down
|
92
|
+
namespaces = node.namespaces
|
93
|
+
if namespaces && namespaces.default
|
94
|
+
namespaces.default_prefix = DEFAULT_NS
|
95
|
+
namespace ||= DEFAULT_NS
|
87
96
|
end
|
88
|
-
|
89
|
-
|
90
|
-
xpath +=
|
91
|
-
xpath += namespace if namespace
|
97
|
+
|
98
|
+
xpath = root ? '/' : './/'
|
99
|
+
xpath += "#{namespace}:" if namespace
|
92
100
|
xpath += get_tag_name
|
93
101
|
# puts "parse: #{xpath}"
|
94
102
|
|
95
103
|
nodes = node.find(xpath)
|
96
|
-
nodes.
|
104
|
+
collection = nodes.collect do |n|
|
97
105
|
obj = new
|
98
106
|
|
99
107
|
attributes.each do |attr|
|
100
108
|
obj.send("#{attr.method_name}=",
|
101
|
-
attr.from_xml_node(n))
|
109
|
+
attr.from_xml_node(n, namespace))
|
102
110
|
end
|
103
111
|
|
104
112
|
elements.each do |elem|
|
105
|
-
elem.namespace = namespace
|
106
113
|
obj.send("#{elem.method_name}=",
|
107
|
-
elem.from_xml_node(n))
|
114
|
+
elem.from_xml_node(n, namespace))
|
108
115
|
end
|
109
|
-
|
116
|
+
|
117
|
+
obj
|
110
118
|
end
|
111
119
|
|
112
120
|
# per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
|
113
121
|
nodes = nil
|
114
122
|
GC.start
|
115
123
|
|
116
|
-
options[:single] ||
|
124
|
+
if options[:single] || root
|
125
|
+
collection.first
|
126
|
+
else
|
127
|
+
collection
|
128
|
+
end
|
117
129
|
end
|
118
130
|
end
|
119
131
|
end
|
data/lib/happymapper/item.rb
CHANGED
@@ -7,33 +7,55 @@ module HappyMapper
|
|
7
7
|
# options:
|
8
8
|
# :deep => Boolean False to only parse element's children, True to include
|
9
9
|
# grandchildren and all others down the chain (// in expath)
|
10
|
+
# :namespace => String Element's namespace if it's not the global or inherited
|
11
|
+
# default
|
12
|
+
# :parser => Symbol Class method to use for type coercion.
|
13
|
+
# :raw => Boolean Use raw node value (inc. tags) when parsing.
|
10
14
|
# :single => Boolean False if object should be collection, True for single object
|
15
|
+
# :tag => String Element name if it doesn't match the specified name.
|
11
16
|
def initialize(name, type, o={})
|
12
17
|
self.name = name.to_s
|
13
18
|
self.type = type
|
14
19
|
self.tag = o.delete(:tag) || name.to_s
|
15
|
-
self.options =
|
16
|
-
:single => false,
|
17
|
-
:deep => false,
|
18
|
-
}.merge(o)
|
20
|
+
self.options = o
|
19
21
|
|
20
22
|
@xml_type = self.class.to_s.split('::').last.downcase
|
21
23
|
end
|
22
24
|
|
23
|
-
def from_xml_node(node)
|
25
|
+
def from_xml_node(node, namespace)
|
24
26
|
if primitive?
|
25
|
-
|
26
|
-
|
27
|
+
find(node, namespace) do |n|
|
28
|
+
if n.respond_to?(:content)
|
29
|
+
typecast(n.content)
|
30
|
+
else
|
31
|
+
typecast(n.to_s)
|
32
|
+
end
|
27
33
|
end
|
28
34
|
else
|
29
|
-
|
35
|
+
if options[:parser]
|
36
|
+
find(node, namespace) do |n|
|
37
|
+
if n.respond_to?(:content) && !options[:raw]
|
38
|
+
value = n.content
|
39
|
+
else
|
40
|
+
value = n.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
type.send(options[:parser].to_sym, value)
|
45
|
+
rescue
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
else
|
50
|
+
type.parse(node, options)
|
51
|
+
end
|
30
52
|
end
|
31
53
|
end
|
32
54
|
|
33
|
-
def xpath
|
55
|
+
def xpath(namespace = self.namespace)
|
34
56
|
xpath = ''
|
35
57
|
xpath += './/' if options[:deep]
|
36
|
-
xpath += namespace if namespace
|
58
|
+
xpath += "#{namespace}:" if namespace
|
37
59
|
xpath += tag
|
38
60
|
# puts "xpath: #{xpath}"
|
39
61
|
xpath
|
@@ -86,16 +108,25 @@ module HappyMapper
|
|
86
108
|
end
|
87
109
|
|
88
110
|
private
|
89
|
-
def
|
111
|
+
def find(node, namespace, &block)
|
112
|
+
# this node has a custom namespace (that is present in the doc)
|
113
|
+
if self.namespace && node.namespaces.find_by_prefix(self.namespace)
|
114
|
+
# from the class definition
|
115
|
+
namespace = self.namespace
|
116
|
+
elsif options[:namespace] && node.namespaces.find_by_prefix(options[:namespace])
|
117
|
+
# from an element definition
|
118
|
+
namespace = options[:namespace]
|
119
|
+
end
|
120
|
+
|
90
121
|
if element?
|
91
|
-
result = node.find_first(xpath)
|
122
|
+
result = node.find_first(xpath(namespace))
|
92
123
|
# puts "vfxn: #{xpath} #{result.inspect}"
|
93
124
|
if result
|
94
|
-
value = yield(result
|
125
|
+
value = yield(result)
|
95
126
|
if options[:attributes].is_a?(Hash)
|
96
127
|
result.attributes.each do |xml_attribute|
|
97
128
|
if attribute_options = options[:attributes][xml_attribute.name.to_sym]
|
98
|
-
attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result)
|
129
|
+
attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace)
|
99
130
|
result.instance_eval <<-EOV
|
100
131
|
def value.#{xml_attribute.name}
|
101
132
|
#{attribute_value.inspect}
|
data/lib/happymapper/version.rb
CHANGED
data/spec/fixtures/pita.xml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
|
2
|
+
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05" xmlns:georss="http://www.georss.org/georss">
|
3
3
|
<OperationRequest>
|
4
4
|
<HTTPHeaders>
|
5
5
|
<Header Name="UserAgent">
|
@@ -27,6 +27,7 @@
|
|
27
27
|
<TotalPages>3</TotalPages>
|
28
28
|
<Item>
|
29
29
|
<ASIN>0321480791</ASIN>
|
30
|
+
<georss:point>38.5351715088 -121.7948684692</georss:point>
|
30
31
|
<DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
|
31
32
|
<ItemAttributes>
|
32
33
|
<Author>Michael Hartl</Author>
|
data/spec/happymapper_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
require 'pp'
|
3
|
+
require 'uri'
|
3
4
|
|
4
5
|
class Feature
|
5
6
|
include HappyMapper
|
@@ -38,7 +39,7 @@ module FamilySearch
|
|
38
39
|
class FamilyTree
|
39
40
|
include HappyMapper
|
40
41
|
|
41
|
-
tag 'familytree'
|
42
|
+
tag 'familytree'
|
42
43
|
attribute :version, String
|
43
44
|
attribute :status_message, String, :tag => 'statusMessage'
|
44
45
|
attribute :status_code, String, :tag => 'statusCode'
|
@@ -51,6 +52,7 @@ module FedEx
|
|
51
52
|
include HappyMapper
|
52
53
|
|
53
54
|
tag 'Address'
|
55
|
+
namespace 'v2'
|
54
56
|
element :city, String, :tag => 'City'
|
55
57
|
element :state, String, :tag => 'StateOrProvinceCode'
|
56
58
|
element :zip, String, :tag => 'PostalCode'
|
@@ -62,6 +64,7 @@ module FedEx
|
|
62
64
|
include HappyMapper
|
63
65
|
|
64
66
|
tag 'Events'
|
67
|
+
namespace 'v2'
|
65
68
|
element :timestamp, String, :tag => 'Timestamp'
|
66
69
|
element :eventtype, String, :tag => 'EventType'
|
67
70
|
element :eventdescription, String, :tag => 'EventDescription'
|
@@ -72,6 +75,7 @@ module FedEx
|
|
72
75
|
include HappyMapper
|
73
76
|
|
74
77
|
tag 'PackageWeight'
|
78
|
+
namespace 'v2'
|
75
79
|
element :units, String, :tag => 'Units'
|
76
80
|
element :value, Integer, :tag => 'Value'
|
77
81
|
end
|
@@ -80,6 +84,7 @@ module FedEx
|
|
80
84
|
include HappyMapper
|
81
85
|
|
82
86
|
tag 'TrackDetails'
|
87
|
+
namespace 'v2'
|
83
88
|
element :tracking_number, String, :tag => 'TrackingNumber'
|
84
89
|
element :status_code, String, :tag => 'StatusCode'
|
85
90
|
element :status_desc, String, :tag => 'StatusDescription'
|
@@ -94,6 +99,7 @@ module FedEx
|
|
94
99
|
include HappyMapper
|
95
100
|
|
96
101
|
tag 'Notifications'
|
102
|
+
namespace 'v2'
|
97
103
|
element :severity, String, :tag => 'Severity'
|
98
104
|
element :source, String, :tag => 'Source'
|
99
105
|
element :code, Integer, :tag => 'Code'
|
@@ -105,13 +111,15 @@ module FedEx
|
|
105
111
|
include HappyMapper
|
106
112
|
|
107
113
|
tag 'TransactionDetail'
|
114
|
+
namespace 'v2'
|
108
115
|
element :cust_tran_id, String, :tag => 'CustomerTransactionId'
|
109
116
|
end
|
110
117
|
|
111
118
|
class TrackReply
|
112
119
|
include HappyMapper
|
113
120
|
|
114
|
-
tag 'TrackReply'
|
121
|
+
tag 'TrackReply'
|
122
|
+
namespace 'v2'
|
115
123
|
element :highest_severity, String, :tag => 'HighestSeverity'
|
116
124
|
element :more_data, Boolean, :tag => 'MoreData'
|
117
125
|
has_many :notifications, Notification, :tag => 'Notifications'
|
@@ -167,6 +175,7 @@ class Status
|
|
167
175
|
element :in_reply_to_status_id, Integer
|
168
176
|
element :in_reply_to_user_id, Integer
|
169
177
|
element :favorited, Boolean
|
178
|
+
element :non_existent, String, :tag => 'dummy', :namespace => 'fake'
|
170
179
|
has_one :user, User
|
171
180
|
end
|
172
181
|
|
@@ -174,6 +183,7 @@ class CurrentWeather
|
|
174
183
|
include HappyMapper
|
175
184
|
|
176
185
|
tag 'ob'
|
186
|
+
namespace 'aws'
|
177
187
|
element :temperature, Integer, :tag => 'temp'
|
178
188
|
element :feels_like, Integer, :tag => 'feels-like'
|
179
189
|
element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
|
@@ -182,7 +192,7 @@ end
|
|
182
192
|
class Address
|
183
193
|
include HappyMapper
|
184
194
|
|
185
|
-
tag 'address'
|
195
|
+
tag 'address'
|
186
196
|
element :street, String
|
187
197
|
element :postcode, String
|
188
198
|
element :housenumber, String
|
@@ -190,14 +200,19 @@ class Address
|
|
190
200
|
element :country, String
|
191
201
|
end
|
192
202
|
|
203
|
+
# for type coercion
|
204
|
+
class ProductGroup < String; end
|
205
|
+
|
193
206
|
module PITA
|
194
207
|
class Item
|
195
208
|
include HappyMapper
|
196
209
|
|
197
210
|
tag 'Item' # if you put class in module you need tag
|
198
211
|
element :asin, String, :tag => 'ASIN'
|
199
|
-
element :detail_page_url,
|
212
|
+
element :detail_page_url, URI, :tag => 'DetailPageURL', :parser => :parse
|
200
213
|
element :manufacturer, String, :tag => 'Manufacturer', :deep => true
|
214
|
+
element :point, String, :tag => 'point', :namespace => 'georss'
|
215
|
+
element :product_group, ProductGroup, :tag => 'ProductGroup', :deep => true, :parser => :new, :raw => true
|
201
216
|
end
|
202
217
|
|
203
218
|
class Items
|
@@ -214,7 +229,7 @@ module GitHub
|
|
214
229
|
class Commit
|
215
230
|
include HappyMapper
|
216
231
|
|
217
|
-
tag "commit"
|
232
|
+
tag "commit"
|
218
233
|
element :url, String
|
219
234
|
element :tree, String
|
220
235
|
element :message, String
|
@@ -290,7 +305,7 @@ describe HappyMapper do
|
|
290
305
|
element.type.should == User
|
291
306
|
element.options[:single] = false
|
292
307
|
end
|
293
|
-
|
308
|
+
|
294
309
|
it "should default tag name to lowercase class" do
|
295
310
|
Foo.get_tag_name.should == 'foo'
|
296
311
|
end
|
@@ -305,6 +320,11 @@ describe HappyMapper do
|
|
305
320
|
Foo.get_tag_name.should == 'FooBar'
|
306
321
|
end
|
307
322
|
|
323
|
+
it "should allow setting a namespace" do
|
324
|
+
Foo.namespace(namespace = "foo")
|
325
|
+
Foo.namespace.should == namespace
|
326
|
+
end
|
327
|
+
|
308
328
|
it "should provide #parse" do
|
309
329
|
Foo.should respond_to(:parse)
|
310
330
|
end
|
@@ -320,7 +340,7 @@ describe HappyMapper do
|
|
320
340
|
describe "#elements" do
|
321
341
|
it "should only return elements for the current class" do
|
322
342
|
Post.elements.size.should == 0
|
323
|
-
Status.elements.size.should ==
|
343
|
+
Status.elements.size.should == 10
|
324
344
|
end
|
325
345
|
end
|
326
346
|
|
@@ -376,8 +396,11 @@ describe HappyMapper do
|
|
376
396
|
first = items.items[0]
|
377
397
|
second = items.items[1]
|
378
398
|
first.asin.should == '0321480791'
|
379
|
-
first.
|
399
|
+
first.point.should == '38.5351715088 -121.7948684692'
|
400
|
+
first.detail_page_url.should be_a_kind_of(URI)
|
401
|
+
first.detail_page_url.to_s.should == 'http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh'
|
380
402
|
first.manufacturer.should == 'Addison-Wesley Professional'
|
403
|
+
first.product_group.should == '<ProductGroup>Book</ProductGroup>'
|
381
404
|
second.asin.should == '047022388X'
|
382
405
|
second.manufacturer.should == 'Wrox'
|
383
406
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jnunemaker-happymapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
@@ -17,9 +17,9 @@ dependencies:
|
|
17
17
|
version_requirement:
|
18
18
|
version_requirements: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
|
-
- - "
|
20
|
+
- - "="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.9.
|
22
|
+
version: 0.9.8
|
23
23
|
version:
|
24
24
|
- !ruby/object:Gem::Dependency
|
25
25
|
name: echoe
|