sax-machine 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/HISTORY.md +4 -0
- data/README.md +10 -12
- data/lib/sax-machine.rb +5 -1
- data/lib/sax-machine/config/sax_attribute.rb +1 -4
- data/lib/sax-machine/config/sax_element.rb +2 -1
- data/lib/sax-machine/handlers/sax_abstract_handler.rb +4 -5
- data/lib/sax-machine/sax_config.rb +1 -1
- data/lib/sax-machine/sax_document.rb +12 -3
- data/lib/sax-machine/version.rb +1 -1
- data/sax-machine.gemspec +0 -1
- data/spec/sax-machine/sax_activerecord_spec.rb +12 -14
- data/spec/sax-machine/sax_configure_spec.rb +26 -23
- data/spec/sax-machine/sax_document_spec.rb +74 -19
- data/spec/sax-machine/sax_include_spec.rb +18 -12
- data/spec/spec_helper.rb +1 -3
- metadata +2 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0fb49e4c86a5d258cd1266f3b39f0279ff6ecc4
|
4
|
+
data.tar.gz: 9221aa8a9a9a552f1ac9ce7e5b962db2e5987515
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6da8e076f9d871aebfee80887b007712768c60643d5690b514cd886758583e132971417533afb560de2a4c8aee988131a46a62da28b32be9ed2837c7f4cf9e86
|
7
|
+
data.tar.gz: 8f28dd9652e24a9d853ded552df3ca25e80cba2eead23bb1ead8d331bc50768778938d7709ab3ebaa7cc11c3ebb411c74de119b8c64897f3d1ae7a41c3457911
|
data/Gemfile
CHANGED
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -28,30 +28,26 @@ $ bundle
|
|
28
28
|
|
29
29
|
## Usage
|
30
30
|
|
31
|
-
|
31
|
+
SAX Machine can use either `nokogiri` or `ox` as XML SAX handler.
|
32
|
+
|
33
|
+
To use **Nokogiri** add this line to your Gemfile:
|
32
34
|
|
33
35
|
```ruby
|
34
|
-
|
36
|
+
gem 'nokogiri', '~> 1.6'
|
35
37
|
```
|
36
38
|
|
37
|
-
To use **Ox**
|
38
|
-
|
39
|
-
Add this line to your application's Gemfile:
|
39
|
+
To use **Ox** add this line to your Gemfile:
|
40
40
|
|
41
41
|
```ruby
|
42
42
|
gem 'ox', '>= 2.1.2'
|
43
43
|
```
|
44
44
|
|
45
|
-
|
45
|
+
You can also specify which handler to use manually, like this:
|
46
46
|
|
47
47
|
```ruby
|
48
|
-
|
49
|
-
SAXMachine.handler = :ox
|
48
|
+
SAXMachine.handler = :nokogiri
|
50
49
|
```
|
51
50
|
|
52
|
-
Please note that this operation is not thread-safe, so it's better to define
|
53
|
-
handler at initialization stage and do not change it during runtime.
|
54
|
-
|
55
51
|
## Examples
|
56
52
|
|
57
53
|
Include `SAXMachine` in any class and define properties to parse:
|
@@ -69,7 +65,8 @@ class AtomEntry
|
|
69
65
|
# The :as argument makes this available through entry.author instead of .name
|
70
66
|
element :name, as: :author
|
71
67
|
element "feedburner:origLink", as: :url
|
72
|
-
element
|
68
|
+
# The :default argument specifies default value for element when it's missing
|
69
|
+
element :summary, class: String, default: "No summary available"
|
73
70
|
element :content, class: AtomContent
|
74
71
|
element :published
|
75
72
|
ancestor :ancestor
|
@@ -104,6 +101,7 @@ feed.feed_url # The URL of the blog feed
|
|
104
101
|
feed.entries.first.title # Title of the first entry
|
105
102
|
feed.entries.first.author # The author of the first entry
|
106
103
|
feed.entries.first.url # Permalink on the blog for this entry
|
104
|
+
feed.entries.first.summary # Returns "No summary available" if summary is missing
|
107
105
|
feed.entries.first.ancestor # The Atom ancestor
|
108
106
|
feed.entries.first.content # Instance of AtomContent
|
109
107
|
feed.entries.first.content.text # Entry content text
|
data/lib/sax-machine.rb
CHANGED
@@ -25,10 +25,7 @@ module SAXMachine
|
|
25
25
|
def attrs_match?(attrs)
|
26
26
|
attrs.key?(@name) || attrs.value?(@name)
|
27
27
|
end
|
28
|
-
|
29
|
-
def has_value_and_attrs_match?(attrs)
|
30
|
-
attrs_match?(attrs)
|
31
|
-
end
|
28
|
+
alias_method :has_value_and_attrs_match?, :attrs_match?
|
32
29
|
|
33
30
|
def collection?
|
34
31
|
false
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module SAXMachine
|
2
2
|
class SAXConfig
|
3
3
|
class ElementConfig
|
4
|
-
attr_reader :name, :setter, :data_class, :collection
|
4
|
+
attr_reader :name, :as, :setter, :data_class, :collection, :default
|
5
5
|
|
6
6
|
def initialize(name, options)
|
7
7
|
@name = name.to_s
|
@@ -15,6 +15,7 @@ module SAXMachine
|
|
15
15
|
|
16
16
|
@as = options[:as]
|
17
17
|
@collection = options[:collection]
|
18
|
+
@default = options[:default]
|
18
19
|
|
19
20
|
@setter = if @collection
|
20
21
|
"add_#{options[:as]}"
|
@@ -104,19 +104,18 @@ module SAXMachine
|
|
104
104
|
else
|
105
105
|
value =
|
106
106
|
case config.data_class.to_s
|
107
|
-
when "String" then value.to_s
|
108
|
-
when "Integer" then value.to_i
|
109
|
-
when "Float" then value.to_f
|
107
|
+
when "String" then value != NO_BUFFER ? value.to_s : value
|
108
|
+
when "Integer" then value != NO_BUFFER ? value.to_i : value
|
109
|
+
when "Float" then value != NO_BUFFER ? value.to_f : value
|
110
110
|
# Assumes that time elements will be string-based and are not
|
111
111
|
# something else, e.g. seconds since epoch
|
112
|
-
when "Time" then Time.parse(value.to_s)
|
112
|
+
when "Time" then value != NO_BUFFER ? Time.parse(value.to_s) : value
|
113
113
|
when "" then value
|
114
114
|
else
|
115
115
|
element
|
116
116
|
end
|
117
117
|
|
118
118
|
object.send(config.setter, value) unless value == NO_BUFFER
|
119
|
-
|
120
119
|
mark_as_parsed(object, config)
|
121
120
|
end
|
122
121
|
|
@@ -33,6 +33,15 @@ module SAXMachine
|
|
33
33
|
attributes.each do |name, value|
|
34
34
|
send("#{name}=", value)
|
35
35
|
end
|
36
|
+
|
37
|
+
self.class.sax_config.top_level_elements.each do |_, configs|
|
38
|
+
configs.each do |config|
|
39
|
+
next unless config.default
|
40
|
+
next unless send(config.as).nil?
|
41
|
+
|
42
|
+
send(config.setter, config.default)
|
43
|
+
end
|
44
|
+
end
|
36
45
|
end
|
37
46
|
end
|
38
47
|
|
@@ -48,19 +57,19 @@ module SAXMachine
|
|
48
57
|
def element(name, options = {})
|
49
58
|
real_name = (options[:as] ||= name).to_s
|
50
59
|
sax_config.add_top_level_element(name, options)
|
51
|
-
create_attr
|
60
|
+
create_attr(real_name)
|
52
61
|
end
|
53
62
|
|
54
63
|
def attribute(name, options = {})
|
55
64
|
real_name = (options[:as] ||= name).to_s
|
56
65
|
sax_config.add_top_level_attribute(self.class.to_s, options.merge(name: name))
|
57
|
-
create_attr
|
66
|
+
create_attr(real_name)
|
58
67
|
end
|
59
68
|
|
60
69
|
def value(name, options = {})
|
61
70
|
real_name = (options[:as] ||= name).to_s
|
62
71
|
sax_config.add_top_level_element_value(self.class.to_s, options.merge(name: name))
|
63
|
-
create_attr
|
72
|
+
create_attr(real_name)
|
64
73
|
end
|
65
74
|
|
66
75
|
def ancestor(name, options = {})
|
data/lib/sax-machine/version.rb
CHANGED
data/sax-machine.gemspec
CHANGED
@@ -1,23 +1,21 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
require 'active_record'
|
3
3
|
|
4
|
-
describe "ActiveRecord" do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
c.element :title
|
10
|
-
end
|
4
|
+
describe "SAXMachine ActiveRecord integration" do
|
5
|
+
before do
|
6
|
+
class MySaxModel < ActiveRecord::Base
|
7
|
+
SAXMachine.configure(MySaxModel) do |c|
|
8
|
+
c.element :title
|
11
9
|
end
|
12
10
|
end
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
after do
|
14
|
+
Object.send(:remove_const, :MySaxModel)
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
17
|
+
it "parses document" do
|
18
|
+
document = MySaxModel.parse("<xml><title>My Title</title></xml>")
|
19
|
+
expect(document.title).to eq("My Title")
|
22
20
|
end
|
23
21
|
end
|
@@ -1,32 +1,35 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
describe "SAXMachine configure" do
|
4
|
+
before do
|
5
|
+
class A
|
6
|
+
SAXMachine.configure(A) do |c|
|
7
|
+
c.element :title
|
8
|
+
end
|
9
|
+
end
|
8
10
|
|
9
|
-
class B < A
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
11
|
+
class B < A
|
12
|
+
SAXMachine.configure(B) do |c|
|
13
|
+
c.element :b
|
14
|
+
end
|
15
|
+
end
|
14
16
|
|
15
|
-
class C < B
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
17
|
+
class C < B
|
18
|
+
SAXMachine.configure(C) do |c|
|
19
|
+
c.element :c
|
20
|
+
end
|
21
|
+
end
|
20
22
|
|
21
|
-
describe "SAXMachine configure" do
|
22
|
-
before do
|
23
23
|
xml = "<top><title>Test</title><b>Matched!</b><c>And Again</c></top>"
|
24
|
-
@a = A.
|
25
|
-
@
|
26
|
-
@
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
@a = A.parse xml
|
25
|
+
@b = B.parse xml
|
26
|
+
@c = C.parse xml
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
Object.send(:remove_const, :A)
|
31
|
+
Object.send(:remove_const, :B)
|
32
|
+
Object.send(:remove_const, :C)
|
30
33
|
end
|
31
34
|
|
32
35
|
it { expect(@a).to be_a(A) }
|
@@ -3,10 +3,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
describe "SAXMachine" do
|
4
4
|
describe "element" do
|
5
5
|
describe "when parsing a single element" do
|
6
|
-
before
|
6
|
+
before do
|
7
7
|
@klass = Class.new do
|
8
8
|
include SAXMachine
|
9
9
|
element :title
|
10
|
+
ancestor :body
|
11
|
+
value :something, required: false
|
12
|
+
attribute :anything, required: true
|
10
13
|
end
|
11
14
|
end
|
12
15
|
|
@@ -21,10 +24,6 @@ describe "SAXMachine" do
|
|
21
24
|
expect(document.title).to eq("Title")
|
22
25
|
end
|
23
26
|
|
24
|
-
it "allows introspection of the elements" do
|
25
|
-
expect(@klass.column_names).to match_array([:title])
|
26
|
-
end
|
27
|
-
|
28
27
|
it "does not overwrites the getter is there is already one present" do
|
29
28
|
@klass = Class.new do
|
30
29
|
def title
|
@@ -97,6 +96,38 @@ describe "SAXMachine" do
|
|
97
96
|
expect(document.title).to eq("My Title")
|
98
97
|
end
|
99
98
|
|
99
|
+
describe "the introspection" do
|
100
|
+
it "allows to get column names" do
|
101
|
+
expect(@klass.column_names).to match_array([:title])
|
102
|
+
end
|
103
|
+
|
104
|
+
it "allows to get elements" do
|
105
|
+
expect(@klass.sax_config.top_level_elements.values.flatten.map(&:to_s)).to \
|
106
|
+
match_array(["name: title dataclass: setter: title= required: value: as:title collection: with: {}"])
|
107
|
+
end
|
108
|
+
|
109
|
+
it "allows to get ancestors" do
|
110
|
+
expect(@klass.sax_config.ancestors.map(&:column)).to \
|
111
|
+
match_array([:body])
|
112
|
+
end
|
113
|
+
|
114
|
+
it "allows to get values" do
|
115
|
+
expect(@klass.sax_config.top_level_element_value.map(&:column)).to \
|
116
|
+
match_array([:something])
|
117
|
+
expect(@klass.sax_config.top_level_element_value.map(&:required?)).to \
|
118
|
+
match_array([false])
|
119
|
+
end
|
120
|
+
|
121
|
+
it "allows to get attributes" do
|
122
|
+
expect(@klass.sax_config.top_level_attributes.map(&:column)).to \
|
123
|
+
match_array([:anything])
|
124
|
+
expect(@klass.sax_config.top_level_attributes.map(&:required?)).to \
|
125
|
+
match_array([true])
|
126
|
+
expect(@klass.sax_config.top_level_attributes.map(&:collection?)).to \
|
127
|
+
match_array([false])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
100
131
|
describe "the class attribute" do
|
101
132
|
before(:each) do
|
102
133
|
@klass = Class.new do
|
@@ -153,6 +184,21 @@ describe "SAXMachine" do
|
|
153
184
|
end
|
154
185
|
end
|
155
186
|
|
187
|
+
describe "the default attribute" do
|
188
|
+
it "is available" do
|
189
|
+
@klass = Class.new do
|
190
|
+
include SAXMachine
|
191
|
+
element :number, class: Integer, default: 0
|
192
|
+
end
|
193
|
+
|
194
|
+
document = @klass.parse("<no>number</no>")
|
195
|
+
expect(document.number).to eq(0)
|
196
|
+
|
197
|
+
document = @klass.parse("<number></number>")
|
198
|
+
expect(document.number).to eq(0)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
156
202
|
describe "the required attribute" do
|
157
203
|
it "is available" do
|
158
204
|
@klass = Class.new do
|
@@ -165,7 +211,7 @@ describe "SAXMachine" do
|
|
165
211
|
end
|
166
212
|
|
167
213
|
describe "when parsing multiple elements" do
|
168
|
-
before
|
214
|
+
before do
|
169
215
|
@klass = Class.new do
|
170
216
|
include SAXMachine
|
171
217
|
element :title
|
@@ -212,7 +258,7 @@ describe "SAXMachine" do
|
|
212
258
|
|
213
259
|
describe "when using options for parsing elements" do
|
214
260
|
describe "using the 'as' option" do
|
215
|
-
before
|
261
|
+
before do
|
216
262
|
@klass = Class.new do
|
217
263
|
include SAXMachine
|
218
264
|
element :description, as: :summary
|
@@ -233,7 +279,7 @@ describe "SAXMachine" do
|
|
233
279
|
|
234
280
|
describe "using the :with option" do
|
235
281
|
describe "and the :value option" do
|
236
|
-
before
|
282
|
+
before do
|
237
283
|
@klass = Class.new do
|
238
284
|
include SAXMachine
|
239
285
|
element :link, value: :href, with: { foo: "bar" }
|
@@ -251,7 +297,7 @@ describe "SAXMachine" do
|
|
251
297
|
end
|
252
298
|
|
253
299
|
describe "and the :as option" do
|
254
|
-
before
|
300
|
+
before do
|
255
301
|
@klass = Class.new do
|
256
302
|
include SAXMachine
|
257
303
|
element :link, value: :href, as: :url, with: { foo: "bar" }
|
@@ -268,7 +314,7 @@ describe "SAXMachine" do
|
|
268
314
|
end
|
269
315
|
|
270
316
|
describe "with only one element" do
|
271
|
-
before
|
317
|
+
before do
|
272
318
|
@klass = Class.new do
|
273
319
|
include SAXMachine
|
274
320
|
element :link, with: { foo: "bar" }
|
@@ -297,7 +343,7 @@ describe "SAXMachine" do
|
|
297
343
|
end
|
298
344
|
|
299
345
|
describe "with multiple elements of same tag" do
|
300
|
-
before
|
346
|
+
before do
|
301
347
|
@klass = Class.new do
|
302
348
|
include SAXMachine
|
303
349
|
element :link, as: :first, with: { foo: "bar" }
|
@@ -317,7 +363,7 @@ describe "SAXMachine" do
|
|
317
363
|
end
|
318
364
|
|
319
365
|
describe "with only one element as a regular expression" do
|
320
|
-
before
|
366
|
+
before do
|
321
367
|
@klass = Class.new do
|
322
368
|
include SAXMachine
|
323
369
|
element :link, with: { foo: /ar$/ }
|
@@ -347,7 +393,7 @@ describe "SAXMachine" do
|
|
347
393
|
end
|
348
394
|
|
349
395
|
describe "using the 'value' option" do
|
350
|
-
before
|
396
|
+
before do
|
351
397
|
@klass = Class.new do
|
352
398
|
include SAXMachine
|
353
399
|
element :link, value: :foo
|
@@ -395,7 +441,7 @@ describe "SAXMachine" do
|
|
395
441
|
end
|
396
442
|
|
397
443
|
describe "when desiring both the content and attributes of an element" do
|
398
|
-
before
|
444
|
+
before do
|
399
445
|
@klass = Class.new do
|
400
446
|
include SAXMachine
|
401
447
|
element :link
|
@@ -416,7 +462,7 @@ describe "SAXMachine" do
|
|
416
462
|
|
417
463
|
describe "elements" do
|
418
464
|
describe "when parsing multiple elements" do
|
419
|
-
before
|
465
|
+
before do
|
420
466
|
@klass = Class.new do
|
421
467
|
include SAXMachine
|
422
468
|
elements :entry, as: :entries
|
@@ -451,7 +497,7 @@ describe "SAXMachine" do
|
|
451
497
|
end
|
452
498
|
|
453
499
|
describe "when using the with and class options" do
|
454
|
-
before
|
500
|
+
before do
|
455
501
|
class Bar
|
456
502
|
include SAXMachine
|
457
503
|
element :title
|
@@ -484,7 +530,7 @@ describe "SAXMachine" do
|
|
484
530
|
end
|
485
531
|
|
486
532
|
describe "when using the class option" do
|
487
|
-
before
|
533
|
+
before do
|
488
534
|
class Foo
|
489
535
|
include SAXMachine
|
490
536
|
element :title
|
@@ -559,7 +605,7 @@ describe "SAXMachine" do
|
|
559
605
|
end
|
560
606
|
|
561
607
|
describe "full example" do
|
562
|
-
before
|
608
|
+
before do
|
563
609
|
@xml = File.read("spec/fixtures/atom.xml")
|
564
610
|
|
565
611
|
class AtomEntry
|
@@ -881,11 +927,20 @@ describe "SAXMachine" do
|
|
881
927
|
end
|
882
928
|
|
883
929
|
@errors = []
|
884
|
-
@
|
930
|
+
@warnings = []
|
931
|
+
@item = ItemElement5.parse(
|
932
|
+
@xml,
|
933
|
+
->(x) { @errors << x },
|
934
|
+
->(x) { @warnings << x },
|
935
|
+
)
|
885
936
|
end
|
886
937
|
|
887
938
|
it "has error" do
|
888
939
|
expect(@errors.uniq.size).to eq(1)
|
889
940
|
end
|
941
|
+
|
942
|
+
it "has no warning" do
|
943
|
+
expect(@warnings.uniq.size).to eq(0)
|
944
|
+
end
|
890
945
|
end
|
891
946
|
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
describe "SAXMachine inheritance" do
|
4
|
+
before do
|
5
|
+
class A
|
6
|
+
include SAXMachine
|
7
|
+
element :title
|
8
|
+
end
|
7
9
|
|
8
|
-
class B < A
|
9
|
-
|
10
|
-
end
|
10
|
+
class B < A
|
11
|
+
element :b
|
12
|
+
end
|
11
13
|
|
12
|
-
class C < B
|
13
|
-
|
14
|
-
end
|
14
|
+
class C < B
|
15
|
+
element :c
|
16
|
+
end
|
15
17
|
|
16
|
-
describe "SAXMachine inheritance" do
|
17
|
-
before do
|
18
18
|
xml = "<top><title>Test</title><b>Matched!</b><c>And Again</c></top>"
|
19
19
|
@a = A.new
|
20
20
|
@a.parse xml
|
@@ -24,6 +24,12 @@ describe "SAXMachine inheritance" do
|
|
24
24
|
@c.parse xml
|
25
25
|
end
|
26
26
|
|
27
|
+
after do
|
28
|
+
Object.send(:remove_const, :A)
|
29
|
+
Object.send(:remove_const, :B)
|
30
|
+
Object.send(:remove_const, :C)
|
31
|
+
end
|
32
|
+
|
27
33
|
it { expect(@a).to be_a(A) }
|
28
34
|
it { expect(@a).not_to be_a(B) }
|
29
35
|
it { expect(@a).to be_a(SAXMachine) }
|
data/spec/spec_helper.rb
CHANGED
@@ -14,9 +14,7 @@ rescue LoadError
|
|
14
14
|
end
|
15
15
|
|
16
16
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/sax-machine')
|
17
|
-
|
18
|
-
SAXMachine.handler = :ox
|
19
|
-
end
|
17
|
+
SAXMachine.handler = ENV['HANDLER'].to_sym
|
20
18
|
|
21
19
|
RSpec.configure do |config|
|
22
20
|
config.run_all_when_everything_filtered = true
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sax-machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Dix
|
@@ -11,22 +11,8 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-
|
14
|
+
date: 2014-08-05 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
17
|
-
name: nokogiri
|
18
|
-
requirement: !ruby/object:Gem::Requirement
|
19
|
-
requirements:
|
20
|
-
- - "~>"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 1.6.0
|
23
|
-
type: :runtime
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - "~>"
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 1.6.0
|
30
16
|
- !ruby/object:Gem::Dependency
|
31
17
|
name: rspec
|
32
18
|
requirement: !ruby/object:Gem::Requirement
|