muffins 0.0.5 → 0.0.6
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/.rvmrc +1 -0
- data/README.md +15 -22
- data/Rakefile +1 -1
- data/lib/muffins.rb +13 -35
- data/lib/muffins/class_methods.rb +60 -0
- data/lib/muffins/document.rb +38 -0
- data/lib/muffins/mapping.rb +24 -59
- data/lib/muffins/mapping_parent.rb +27 -0
- data/lib/muffins/version.rb +1 -1
- data/muffins.gemspec +15 -17
- data/spec/fixtures/books.html +23 -0
- data/spec/fixtures/books.xml +11 -0
- data/spec/muffins/document_spec.rb +46 -0
- data/spec/muffins/mapping_parent_spec.rb +33 -0
- data/spec/muffins/mapping_spec.rb +29 -266
- data/spec/muffins_spec.rb +43 -103
- data/spec/spec_helper.rb +1 -4
- metadata +30 -59
- data/features/basic_parsing.feature +0 -33
- data/features/nested_parsing.feature +0 -37
- data/features/non_primitive_typing.feature +0 -36
- data/features/specified_pathnames.feature +0 -33
- data/features/specified_typing.feature +0 -33
- data/features/step_definitions/application_steps.rb +0 -52
- data/features/support/env.rb +0 -16
- data/lib/muffins/mapping_decorator.rb +0 -22
- data/spec/muffins/mapping_decorator_spec.rb +0 -46
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Muffins::Document do
|
4
|
+
|
5
|
+
subject { described_class.new(:body => body) }
|
6
|
+
|
7
|
+
context "HTML" do
|
8
|
+
|
9
|
+
let(:body) { fixture_file("books.html") }
|
10
|
+
let(:path) { ".book" }
|
11
|
+
|
12
|
+
describe "#all" do
|
13
|
+
it "returns 2 objects" do
|
14
|
+
subject.all(path).size.should eql(2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#first" do
|
19
|
+
it "returns a string" do
|
20
|
+
subject.first(path).should be_a(String)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context "XML" do
|
27
|
+
|
28
|
+
let(:body) { fixture_file("books.xml") }
|
29
|
+
let(:path) { "book" }
|
30
|
+
|
31
|
+
describe "#all" do
|
32
|
+
it "returns 2 objects" do
|
33
|
+
subject.all(path).size.should eql(2)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#first" do
|
38
|
+
it "returns an element" do
|
39
|
+
subject.first(path).should be_a(String)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Muffins::MappingParent do
|
4
|
+
|
5
|
+
class Book
|
6
|
+
include Muffins
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { Muffins::MappingParent.new(options) }
|
10
|
+
|
11
|
+
let(:options) do
|
12
|
+
{ :path => path,
|
13
|
+
:klass => klass }
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:klass) { Book }
|
17
|
+
let(:path) { "#book" }
|
18
|
+
|
19
|
+
let(:name) { :foo }
|
20
|
+
let(:type) { String }
|
21
|
+
let(:opts) { {} }
|
22
|
+
|
23
|
+
its(:klass) { should eql(klass) }
|
24
|
+
its(:path) { should eql(path) }
|
25
|
+
|
26
|
+
describe "#map" do
|
27
|
+
it "initializes a namespaced mapping" do
|
28
|
+
subject.map(name, type, opts)
|
29
|
+
klass.send(:mappings).first.within.should == path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -1,280 +1,43 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
class Book
|
5
|
-
include Muffins
|
6
|
-
end
|
7
|
-
|
8
|
-
describe Mapping do
|
9
|
-
|
10
|
-
let(:name) { :foo }
|
11
|
-
let(:options) { {} }
|
12
|
-
|
13
|
-
let(:document) { mock(Nokogiri::XML::Document) }
|
14
|
-
|
15
|
-
let(:node) { mock(Nokogiri::XML::Node) }
|
16
|
-
let(:nodes) { [node] }
|
17
|
-
|
18
|
-
let(:value) { 'bar' }
|
19
|
-
let(:values) { [value] }
|
20
|
-
|
21
|
-
subject { Mapping.new(name, options) }
|
22
|
-
|
23
|
-
describe "#map" do
|
24
|
-
before(:each) do
|
25
|
-
subject.stub(:typecast).with(node).and_return(value)
|
26
|
-
end
|
27
|
-
|
28
|
-
context "#collection? returns true" do
|
29
|
-
before(:each) do
|
30
|
-
subject.stub(:collection?).and_return(true)
|
31
|
-
|
32
|
-
document.stub(:css).and_return(nodes)
|
33
|
-
end
|
34
|
-
|
35
|
-
it "typecasts the parsed values" do
|
36
|
-
subject.should_receive(:typecast).with(node).and_return(value)
|
37
|
-
subject.map(document)
|
38
|
-
end
|
39
|
-
|
40
|
-
it "returns an array of typecasted values" do
|
41
|
-
subject.map(document).should == values
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context "#collection? returns false" do
|
46
|
-
before(:each) do
|
47
|
-
subject.stub(:collection?).and_return(false)
|
48
|
-
|
49
|
-
document.stub(:at_css).and_return(node)
|
50
|
-
end
|
51
|
-
|
52
|
-
it "typecasts the parsed value" do
|
53
|
-
subject.should_receive(:typecast).with(node).and_return(value)
|
54
|
-
subject.map(document)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "returns the typecasted value" do
|
58
|
-
subject.map(document).should == value
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe "#path" do
|
64
|
-
context "no options are passed" do
|
65
|
-
it "returns the mapping name" do
|
66
|
-
subject.path.should == name
|
67
|
-
end
|
68
|
-
end
|
3
|
+
describe Muffins::Mapping do
|
69
4
|
|
70
|
-
|
71
|
-
let(:to) { 'bar' }
|
72
|
-
let(:options) { {:to => to} }
|
5
|
+
subject { described_class.new(options) }
|
73
6
|
|
74
|
-
|
75
|
-
|
76
|
-
|
7
|
+
let(:name) { :foo }
|
8
|
+
let(:type) { String }
|
9
|
+
let(:to) { ".book > .title" }
|
10
|
+
let(:within) { "#books" }
|
11
|
+
let(:collection) { false }
|
77
12
|
|
78
|
-
|
79
|
-
subject.path.should == to
|
80
|
-
end
|
81
|
-
end
|
13
|
+
let(:document) { fixture_file("books.html") }
|
82
14
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
15
|
+
let(:options) do
|
16
|
+
{ :name => name,
|
17
|
+
:type => type,
|
18
|
+
:to => to,
|
19
|
+
:within => within,
|
20
|
+
:collection => collection }
|
21
|
+
end
|
88
22
|
|
89
|
-
|
90
|
-
|
91
|
-
end
|
23
|
+
its(:absolute_path) { should eql("#books > .book > .title") }
|
24
|
+
its(:relative_path) { should eql(".book > .title") }
|
92
25
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
26
|
+
describe "#coerce" do
|
27
|
+
it "coerces to the set type" do
|
28
|
+
subject.coerce(1).should be_a(String)
|
97
29
|
end
|
30
|
+
end
|
98
31
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
subject.collection?.should be_false
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context "options[:collection] is false" do
|
107
|
-
|
108
|
-
let(:options) { {:collection => false} }
|
109
|
-
|
110
|
-
before(:each) do
|
111
|
-
subject.stub(:options).and_return(options)
|
112
|
-
end
|
113
|
-
|
114
|
-
it "returns false" do
|
115
|
-
subject.collection?.should be_false
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context "options[:collection] is true" do
|
120
|
-
|
121
|
-
let(:options) { {:collection => true} }
|
122
|
-
|
123
|
-
before(:each) do
|
124
|
-
subject.stub(:options).and_return(options)
|
125
|
-
end
|
126
|
-
|
127
|
-
it "returns true" do
|
128
|
-
subject.collection?.should be_true
|
129
|
-
end
|
130
|
-
end
|
32
|
+
describe "#parse" do
|
33
|
+
context "when collection is false" do
|
34
|
+
it { subject.parse(document).should eql("War and Peace (Vintage Classics)") }
|
131
35
|
end
|
132
36
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
describe "#typecast" do
|
137
|
-
|
138
|
-
before(:each) do
|
139
|
-
node.stub(:try).with(:text).and_return(value)
|
140
|
-
|
141
|
-
subject.stub(:options).and_return(options)
|
142
|
-
end
|
143
|
-
|
144
|
-
context "#primitive? returns true" do
|
145
|
-
|
146
|
-
before(:each) do
|
147
|
-
subject.stub(:primitive?).and_return(true)
|
148
|
-
end
|
149
|
-
|
150
|
-
context "options[:type] = String" do
|
151
|
-
let(:options) { {:type => String} }
|
152
|
-
|
153
|
-
it "returns an instance of String" do
|
154
|
-
subject.typecast(node).should be_a(String)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
context "options[:type] = Float" do
|
159
|
-
let(:options) { {:type => Float} }
|
160
|
-
let(:value) { '10.01' }
|
161
|
-
|
162
|
-
it "returns an instance of Float" do
|
163
|
-
subject.typecast(node).should be_a(Float)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
context "options[:type] = Time" do
|
168
|
-
let(:options) { {:type => Time} }
|
169
|
-
|
170
|
-
let(:value) { '00:00:00 -0700' }
|
171
|
-
|
172
|
-
it "returns an instance of Time" do
|
173
|
-
subject.typecast(node).should be_a(Time)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
context "options[:type] = Date" do
|
178
|
-
let(:options) { {:type => Date} }
|
179
|
-
|
180
|
-
let(:value) { '2011-01-01' }
|
181
|
-
|
182
|
-
it "returns an instance of Date" do
|
183
|
-
subject.typecast(node).should be_a(Date)
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
context "options[:type] = DateTime" do
|
188
|
-
let(:options) { {:type => DateTime} }
|
189
|
-
|
190
|
-
let(:value) { '2011-01-01 00:00:00 -0700' }
|
191
|
-
|
192
|
-
it "returns an instance of DateTime" do
|
193
|
-
subject.typecast(node).should be_a(DateTime)
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
context "options[:type] = Integer" do
|
198
|
-
let(:options) { {:type => Integer} }
|
199
|
-
|
200
|
-
let(:value) { '12908234' }
|
201
|
-
|
202
|
-
it "returns an instance of Integer" do
|
203
|
-
subject.typecast(node).should be_a(Integer)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
context "options[:type] = Boolean" do
|
208
|
-
let(:options) { {:type => Boolean} }
|
209
|
-
|
210
|
-
context "value = 't'" do
|
211
|
-
|
212
|
-
let(:value) { 't' }
|
213
|
-
|
214
|
-
it "returns true" do
|
215
|
-
subject.typecast(node).should be_true
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
context "value = 'true'" do
|
220
|
-
|
221
|
-
let(:value) { 'true' }
|
222
|
-
|
223
|
-
it "returns true" do
|
224
|
-
subject.typecast(node).should be_true
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
context "value = '1'" do
|
229
|
-
|
230
|
-
let(:value) { '1' }
|
231
|
-
|
232
|
-
it "returns true" do
|
233
|
-
subject.typecast(node).should be_true
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
context "value = '0'" do
|
238
|
-
|
239
|
-
let(:value) { '0' }
|
240
|
-
|
241
|
-
it "returns false" do
|
242
|
-
subject.typecast(node).should be_false
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
context "value = 'false'" do
|
247
|
-
|
248
|
-
let(:value) { 'false' }
|
249
|
-
|
250
|
-
it "returns false" do
|
251
|
-
subject.typecast(node).should be_false
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
context "value = 'f'" do
|
256
|
-
|
257
|
-
let(:value) { 'f' }
|
258
|
-
|
259
|
-
it "returns false" do
|
260
|
-
subject.typecast(node).should be_false
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
context "#primitive? returns false" do
|
267
|
-
let(:options) { {:type => Book} }
|
268
|
-
|
269
|
-
before(:each) do
|
270
|
-
subject.stub(:primitive?).and_return(false)
|
271
|
-
end
|
272
|
-
|
273
|
-
it "attempts to parse the nested set" do
|
274
|
-
Book.should_receive(:parse).with(node)
|
275
|
-
subject.typecast(node)
|
276
|
-
end
|
277
|
-
end
|
37
|
+
context "when collection is true" do
|
38
|
+
let(:collection) { true }
|
39
|
+
it { subject.parse(document).should eql([ "War and Peace (Vintage Classics)", "Anna Karenina (Oprah's Book Club)" ])}
|
278
40
|
end
|
279
41
|
end
|
280
|
-
|
42
|
+
|
43
|
+
end
|
data/spec/muffins_spec.rb
CHANGED
@@ -2,127 +2,67 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Muffins do
|
4
4
|
|
5
|
-
|
5
|
+
class Book
|
6
|
+
include Muffins
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
include Muffins
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
let(:book) { mock(Book) }
|
14
|
-
|
15
|
-
let(:mapping) { mock(Muffins::Mapping) }
|
16
|
-
let(:mapping_name) { :foo }
|
17
|
-
let(:mapping_options) { {} }
|
18
|
-
|
19
|
-
|
20
|
-
describe ".mappings" do
|
21
|
-
it "returns an array" do
|
22
|
-
subject.mappings.should be_an(Array)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe ".base_path" do
|
27
|
-
context "value is passed" do
|
28
|
-
it "sets @base_path to the passed value" do
|
29
|
-
subject.base_path('book')
|
30
|
-
subject.instance_variable_get(:@base_path).should == 'book'
|
31
|
-
end
|
32
|
-
end
|
8
|
+
path ".book"
|
9
|
+
end
|
33
10
|
|
34
|
-
|
35
|
-
it "returns the value of @base_path" do
|
36
|
-
subject.instance_variable_set(:@base_path, 'book')
|
37
|
-
subject.base_path.should == 'book'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
11
|
+
subject { Book }
|
41
12
|
|
42
|
-
|
43
|
-
let(:document) { mock(Nokogiri::XML::Document) }
|
44
|
-
let(:node) { mock(Nokogiri::XML::Node) }
|
13
|
+
let(:book) { Book.new }
|
45
14
|
|
46
|
-
|
15
|
+
let(:name) { :foo }
|
16
|
+
let(:type) { String }
|
47
17
|
|
48
|
-
|
49
|
-
|
18
|
+
let(:mapping) { mock(Muffins::Mapping) }
|
19
|
+
let(:mappings) { [] }
|
50
20
|
|
51
|
-
|
21
|
+
describe ".parse" do
|
52
22
|
|
53
|
-
|
54
|
-
subject.stub(:base_path).and_return(base_path)
|
55
|
-
subject.stub(:new).and_return(book)
|
56
|
-
subject.stub(:mappings).and_return(mappings)
|
23
|
+
let(:document) { fixture_file("books.html") }
|
57
24
|
|
58
|
-
|
25
|
+
it "parses a document based on the path" do
|
26
|
+
subject.parse(document).count.should == 2
|
27
|
+
end
|
59
28
|
|
60
|
-
|
29
|
+
end
|
61
30
|
|
62
|
-
|
63
|
-
|
31
|
+
describe ".within" do
|
32
|
+
it "yields an instance of MappingParent" do
|
33
|
+
subject.within(name) do |parent|
|
34
|
+
parent.should be_a(Muffins::MappingParent)
|
64
35
|
end
|
36
|
+
end
|
37
|
+
end
|
65
38
|
|
66
|
-
|
67
|
-
book.should_receive("#{mapping_name}=").with(mapping_value)
|
68
|
-
subject.parse(document)
|
69
|
-
end
|
39
|
+
describe ".map" do
|
70
40
|
|
71
|
-
|
72
|
-
|
73
|
-
|
41
|
+
before do
|
42
|
+
Muffins::Mapping.stub(:new).and_return(mapping)
|
43
|
+
subject.stub(:mappings).and_return(mappings)
|
74
44
|
end
|
75
45
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
foo.should be_a(Muffins::MappingDecorator)
|
80
|
-
end
|
81
|
-
end
|
46
|
+
it "initializes a new Mapping" do
|
47
|
+
Muffins::Mapping.should_receive(:new).with(:name => name, :type => type)
|
48
|
+
subject.map(name, type)
|
82
49
|
end
|
83
50
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
before(:each) do
|
89
|
-
Muffins::Mapping.stub(:new)
|
90
|
-
.with(mapping_name, mapping_options)
|
91
|
-
.and_return(mapping)
|
92
|
-
|
93
|
-
|
94
|
-
book.stub(:respond_to?).with(mapping_name)
|
95
|
-
subject.stub(:new).and_return(book)
|
96
|
-
|
97
|
-
subject.stub(:mappings).and_return(mappings)
|
98
|
-
end
|
99
|
-
|
100
|
-
it "adds a new instance of Muffins::Mapping to .mappings" do
|
101
|
-
mappings.should_receive(:<<).with(mapping)
|
102
|
-
subject.map mapping_name, mapping_options
|
103
|
-
end
|
104
|
-
|
105
|
-
context "the mapping name is not a defined method" do
|
106
|
-
before(:each) do
|
107
|
-
book.stub(:respond_to?).with(mapping_name).and_return(false)
|
108
|
-
end
|
109
|
-
|
110
|
-
it "defines an attr_accessor for the mapping name" do
|
111
|
-
subject.should_receive(:attr_accessor).with(mapping_name)
|
112
|
-
subject.map mapping_name
|
113
|
-
end
|
114
|
-
end
|
51
|
+
it "adds a mapping to the list of mappings" do
|
52
|
+
mappings.should_receive(:<<).with(mapping)
|
53
|
+
subject.map(name, type)
|
54
|
+
end
|
115
55
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
56
|
+
it "creates a getter method for :foo" do
|
57
|
+
subject.map(name, type)
|
58
|
+
book.should respond_to(name)
|
59
|
+
end
|
120
60
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
end
|
61
|
+
it "creates a setter method for :foo" do
|
62
|
+
subject.map(name, type)
|
63
|
+
book.should respond_to("#{name}=")
|
126
64
|
end
|
65
|
+
|
127
66
|
end
|
128
|
-
|
67
|
+
|
68
|
+
end
|