useless-doc 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/useless/doc/core/body.rb +1 -1
- data/lib/useless/doc/core/domain.rb +6 -2
- data/lib/useless/doc/core/request.rb +3 -3
- data/lib/useless/doc/core/resource.rb +1 -1
- data/lib/useless/doc/core/response.rb +1 -1
- data/lib/useless/doc/dsl.rb +8 -0
- data/lib/useless/doc/router.rb +12 -2
- data/lib/useless/doc/serialization/dump.rb +2 -0
- data/lib/useless/doc/serialization/load.rb +14 -0
- data/lib/useless/doc/ui/godel/api.mustache +2 -2
- data/lib/useless/doc/ui/godel/domain.mustache +3 -3
- data/lib/useless/doc/ui/godel/stylesheet.css +13 -4
- data/lib/useless/doc/ui/godel.rb +19 -2
- data/lib/useless/doc/version.rb +1 -1
- data/spec/documents/api.json +1 -0
- data/spec/documents/domain.json +1 -0
- data/spec/useless/doc/dsl_spec.rb +2 -0
- data/spec/useless/doc/router_spec.rb +10 -0
- data/spec/useless/doc/serialization/dump_spec.rb +4 -0
- data/spec/useless/doc/serialization/load_spec.rb +2 -0
- data/spec/useless/doc/ui/godel_spec.rb +34 -4
- data/useless-doc.gemspec +1 -0
- metadata +18 -2
@@ -13,12 +13,15 @@ module Useless
|
|
13
13
|
# @!attribute [r] description
|
14
14
|
# @return [String] a description of the domain.
|
15
15
|
#
|
16
|
+
# @!attribute [r] timestamp
|
17
|
+
# @return [Time] the time that this domain doc was last updated.
|
18
|
+
#
|
16
19
|
# @!attribute [r] apis
|
17
20
|
# @return [Array<API>] the APIs included in this domain.
|
18
21
|
#
|
19
22
|
class Domain
|
20
23
|
|
21
|
-
attr_accessor :name, :url, :description, :apis
|
24
|
+
attr_accessor :name, :url, :description, :timestamp, :apis
|
22
25
|
|
23
26
|
# @param [Hash] attrs corresponds to the class's instance attributes.
|
24
27
|
#
|
@@ -26,7 +29,8 @@ module Useless
|
|
26
29
|
@name = attrs[:name]
|
27
30
|
@url = attrs[:url]
|
28
31
|
@description = attrs[:description]
|
29
|
-
@
|
32
|
+
@timestamp = attrs[:timestamp]
|
33
|
+
@apis = attrs[:apis] || []
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -54,10 +54,10 @@ module Useless
|
|
54
54
|
@method = attrs[:method]
|
55
55
|
@description = attrs[:description]
|
56
56
|
@authentication_required = attrs[:authentication_required]
|
57
|
-
@parameters = attrs[:parameters]
|
58
|
-
@headers = attrs[:headers]
|
57
|
+
@parameters = attrs[:parameters] || []
|
58
|
+
@headers = attrs[:headers] || []
|
59
59
|
@body = attrs[:body]
|
60
|
-
@responses = attrs[:responses]
|
60
|
+
@responses = attrs[:responses] || []
|
61
61
|
end
|
62
62
|
|
63
63
|
# Documentation for a request parameter for an API action.
|
data/lib/useless/doc/dsl.rb
CHANGED
@@ -84,6 +84,14 @@ module Useless
|
|
84
84
|
@attributes[:description] = description
|
85
85
|
end
|
86
86
|
|
87
|
+
def timestamp(timestamp)
|
88
|
+
if timestamp.is_a?(String)
|
89
|
+
timestamp = Time.parse(timestamp)
|
90
|
+
end
|
91
|
+
|
92
|
+
@attributes[:timestamp] = timestamp
|
93
|
+
end
|
94
|
+
|
87
95
|
def api(name, &block)
|
88
96
|
api = API.build name: name, &block
|
89
97
|
@attributes[:apis] << api
|
data/lib/useless/doc/router.rb
CHANGED
@@ -11,6 +11,16 @@ module Useless
|
|
11
11
|
@default ||= Doc::Router::Default.new
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.uri_for(raw_url)
|
15
|
+
url = raw_url.dup
|
16
|
+
|
17
|
+
unless url =~ /^https?:\/\//
|
18
|
+
url = 'http://' + url
|
19
|
+
end
|
20
|
+
|
21
|
+
URI(url)
|
22
|
+
end
|
23
|
+
|
14
24
|
def doc_for_api(url)
|
15
25
|
end
|
16
26
|
|
@@ -26,7 +36,7 @@ module Useless
|
|
26
36
|
|
27
37
|
def doc_for_api(url)
|
28
38
|
return nil unless supported_url?(url)
|
29
|
-
uri =
|
39
|
+
uri = Router.uri_for(url)
|
30
40
|
host = uri.host
|
31
41
|
new_host = host.
|
32
42
|
split('.').
|
@@ -36,7 +46,7 @@ module Useless
|
|
36
46
|
end
|
37
47
|
|
38
48
|
def api_for_doc(url)
|
39
|
-
uri =
|
49
|
+
uri = Router.uri_for(url)
|
40
50
|
host = uri.host
|
41
51
|
parts = host.split('.')
|
42
52
|
parts.slice!(-3) if parts[-3] == 'doc'
|
@@ -30,6 +30,7 @@ module Useless
|
|
30
30
|
'name' => domain.name,
|
31
31
|
'url' => domain.url,
|
32
32
|
'description' => domain.description,
|
33
|
+
'timestamp' => domain.timestamp ? domain.timestamp.iso8601 : nil,
|
33
34
|
'apis' => domain.apis.map { |api| api(api) }
|
34
35
|
end
|
35
36
|
end
|
@@ -46,6 +47,7 @@ module Useless
|
|
46
47
|
'name' => api.name,
|
47
48
|
'url' => api.url,
|
48
49
|
'description' => api.description,
|
50
|
+
'timestamp' => api.timestamp ? api.timestamp.iso8601 : nil,
|
49
51
|
'resources' => api.resources.map { |resource| resource(resource) }
|
50
52
|
end
|
51
53
|
end
|
@@ -51,9 +51,16 @@ module Useless
|
|
51
51
|
api json
|
52
52
|
end
|
53
53
|
|
54
|
+
timestamp = begin
|
55
|
+
Time.parse(hash['timestamp'])
|
56
|
+
rescue TypeError, ArgumentError
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
54
60
|
Useless::Doc::Core::Domain.new \
|
55
61
|
name: hash['name'],
|
56
62
|
url: hash['url'],
|
63
|
+
timestamp: timestamp,
|
57
64
|
description: hash['description'],
|
58
65
|
apis: apis
|
59
66
|
end
|
@@ -73,9 +80,16 @@ module Useless
|
|
73
80
|
resource json
|
74
81
|
end
|
75
82
|
|
83
|
+
timestamp = begin
|
84
|
+
Time.parse(hash['timestamp'])
|
85
|
+
rescue TypeError, ArgumentError
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
76
89
|
Useless::Doc::Core::API.new \
|
77
90
|
name: hash['name'],
|
78
91
|
url: hash['url'],
|
92
|
+
timestamp: timestamp,
|
79
93
|
description: hash['description'],
|
80
94
|
resources: resources
|
81
95
|
end
|
@@ -13,14 +13,14 @@
|
|
13
13
|
<section class="api header-section">
|
14
14
|
<a class="name header-section-title" href="{{doc_url}}">{{name}}</a>
|
15
15
|
|
16
|
-
|
16
|
+
{{{description}}}
|
17
17
|
</section>
|
18
18
|
{{/apis}}
|
19
19
|
</header>
|
20
20
|
|
21
21
|
<section class="main description">
|
22
|
-
<article>
|
23
|
-
|
22
|
+
<article class="description domain">
|
23
|
+
{{{description}}}
|
24
24
|
</article>
|
25
25
|
</section>
|
26
26
|
</body>
|
@@ -10,6 +10,7 @@ h1, h2, h3, h4 {
|
|
10
10
|
font-family: 'Open Sans Condensed', sans-serif;
|
11
11
|
margin-top: 0;
|
12
12
|
margin-bottom: 0.25em;
|
13
|
+
color: #333;
|
13
14
|
}
|
14
15
|
|
15
16
|
h2 {
|
@@ -35,11 +36,19 @@ h4 span.content-type {
|
|
35
36
|
font-size: 0.75em;
|
36
37
|
}
|
37
38
|
|
38
|
-
|
39
|
-
color: #
|
39
|
+
article.description {
|
40
|
+
color: #555;
|
40
41
|
margin-top: 0;
|
41
42
|
}
|
42
43
|
|
44
|
+
article.description code {
|
45
|
+
font-family: 'Courier New', 'Courier', monospace;
|
46
|
+
}
|
47
|
+
|
48
|
+
article.description h2 {
|
49
|
+
margin-bottom: 0.25em;
|
50
|
+
}
|
51
|
+
|
43
52
|
p.description.request {
|
44
53
|
margin-bottom: 0.5em;
|
45
54
|
}
|
@@ -71,7 +80,7 @@ header section.header-section.api {
|
|
71
80
|
}
|
72
81
|
|
73
82
|
header section.header-section p {
|
74
|
-
margin
|
83
|
+
margin: 0.25em 0 0.75em;
|
75
84
|
}
|
76
85
|
|
77
86
|
header section.header-section a.header-section-title {
|
@@ -85,7 +94,7 @@ header section.header-section td.description {
|
|
85
94
|
|
86
95
|
section.main {
|
87
96
|
position: relative;
|
88
|
-
margin-left:
|
97
|
+
margin-left: 303px;
|
89
98
|
min-width: 520px;
|
90
99
|
}
|
91
100
|
|
data/lib/useless/doc/ui/godel.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'mustache'
|
2
2
|
require 'forwardable'
|
3
3
|
require 'rack/utils'
|
4
|
+
require 'redcarpet'
|
4
5
|
|
5
6
|
require 'useless/doc/core/domain'
|
6
7
|
require 'useless/doc/core/api'
|
@@ -16,6 +17,14 @@ module Useless
|
|
16
17
|
File.dirname(__FILE__) + '/godel'
|
17
18
|
end
|
18
19
|
|
20
|
+
def self.markdown
|
21
|
+
@markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML,
|
22
|
+
:no_intra_emphasis => true, :tables => true,
|
23
|
+
:fenced_code_blocks => true,:autolink => true,
|
24
|
+
:space_after_headers => true, :lax_spacing => true,
|
25
|
+
:superscript => true)
|
26
|
+
end
|
27
|
+
|
19
28
|
def initialize(router)
|
20
29
|
@router = router
|
21
30
|
end
|
@@ -39,7 +48,7 @@ module Useless
|
|
39
48
|
class Domain < Mustache
|
40
49
|
extend Forwardable
|
41
50
|
|
42
|
-
def_delegators :@domain, :name
|
51
|
+
def_delegators :@domain, :name
|
43
52
|
|
44
53
|
self.template_file = Godel.asset_path + '/domain.mustache'
|
45
54
|
|
@@ -48,6 +57,10 @@ module Useless
|
|
48
57
|
@router = router
|
49
58
|
end
|
50
59
|
|
60
|
+
def description
|
61
|
+
Godel.markdown.render(@domain.description)
|
62
|
+
end
|
63
|
+
|
51
64
|
def apis
|
52
65
|
@domain.apis.map{ |api| Godel::API.new(api, @router) }
|
53
66
|
end
|
@@ -56,7 +69,7 @@ module Useless
|
|
56
69
|
class API < Mustache
|
57
70
|
extend Forwardable
|
58
71
|
|
59
|
-
def_delegators :@api, :name, :url
|
72
|
+
def_delegators :@api, :name, :url
|
60
73
|
|
61
74
|
self.template_file = Godel.asset_path + '/api.mustache'
|
62
75
|
|
@@ -69,6 +82,10 @@ module Useless
|
|
69
82
|
@router.doc_for_api(@api.url)
|
70
83
|
end
|
71
84
|
|
85
|
+
def description
|
86
|
+
Godel.markdown.render(@api.description)
|
87
|
+
end
|
88
|
+
|
72
89
|
def resources
|
73
90
|
@api.resources.map{ |resource| Godel::Resource.new(resource, @router) }
|
74
91
|
end
|
data/lib/useless/doc/version.rb
CHANGED
data/spec/documents/api.json
CHANGED
data/spec/documents/domain.json
CHANGED
@@ -11,6 +11,7 @@ describe Useless::Doc::DSL::Domain do
|
|
11
11
|
domain = Useless::Doc.domain 'useless.io' do
|
12
12
|
url 'http://useless.io'
|
13
13
|
description 'A collection of useless APIs.'
|
14
|
+
timestamp '2013-03-06 11:13 PM'
|
14
15
|
|
15
16
|
api 'Twonk' do
|
16
17
|
url 'twonk.useless.io'
|
@@ -21,6 +22,7 @@ describe Useless::Doc::DSL::Domain do
|
|
21
22
|
domain.name.should == 'useless.io'
|
22
23
|
domain.url.should == 'http://useless.io'
|
23
24
|
domain.description.should == 'A collection of useless APIs.'
|
25
|
+
domain.timestamp.should == Time.parse('2013-03-06 11:13 PM')
|
24
26
|
|
25
27
|
domain.apis.first.name.should == 'Twonk'
|
26
28
|
domain.apis.first.url.should == 'twonk.useless.io'
|
@@ -18,6 +18,11 @@ describe Useless::Doc::Router::Default do
|
|
18
18
|
it 'should return nil if the specified URL is not part of the domain' do
|
19
19
|
@router.doc_for_api('http://nonexistant.domain.com/some/path').should be_nil
|
20
20
|
end
|
21
|
+
|
22
|
+
it 'should handle URLs that are missing a scheme' do
|
23
|
+
@router.doc_for_api('api.domain.com/some/path').
|
24
|
+
should == 'http://api.doc.domain.com/some/path'
|
25
|
+
end
|
21
26
|
end
|
22
27
|
|
23
28
|
describe '.api_for_doc' do
|
@@ -34,5 +39,10 @@ describe Useless::Doc::Router::Default do
|
|
34
39
|
it 'should return nil if the resulting URL is not part of the domain' do
|
35
40
|
@router.api_for_doc('http://nonexistant.doc.domain.com/some/path').should be_nil
|
36
41
|
end
|
42
|
+
|
43
|
+
it 'should handle URLs that are missing a scheme' do
|
44
|
+
@router.api_for_doc('api.doc.domain.com/some/path').
|
45
|
+
should == 'http://api.domain.com/some/path'
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
@@ -33,6 +33,7 @@ describe Useless::Doc::Serialization::Dump do
|
|
33
33
|
domain = Useless::Doc::Core::Domain.new \
|
34
34
|
name: 'Useless',
|
35
35
|
url: 'http://useless.io',
|
36
|
+
timestamp: Time.parse('2013-03-06 11:13 PM'),
|
36
37
|
description: 'A collection of useless APIs.',
|
37
38
|
apis: [api]
|
38
39
|
|
@@ -40,6 +41,7 @@ describe Useless::Doc::Serialization::Dump do
|
|
40
41
|
hash = Useless::Doc::Serialization::Load.json_to_hash(json)
|
41
42
|
hash['name'].should == 'Useless'
|
42
43
|
hash['url'].should == 'http://useless.io'
|
44
|
+
hash['timestamp'].should == Time.parse('2013-03-06 11:13 PM').iso8601
|
43
45
|
hash['description'].should == 'A collection of useless APIs.'
|
44
46
|
|
45
47
|
api_hash = Useless::Doc::Serialization::Load.json_to_hash(hash['apis'][0])
|
@@ -59,6 +61,7 @@ describe Useless::Doc::Serialization::Dump do
|
|
59
61
|
api = Useless::Doc::Core::API.new \
|
60
62
|
name: 'Twiddles API',
|
61
63
|
url: 'twiddles.useless.io',
|
64
|
+
timestamp: Time.parse('2013-03-06 11:13 PM'),
|
62
65
|
description: 'Pretty much, like, everything you\'re looking for',
|
63
66
|
resources: [resource]
|
64
67
|
|
@@ -66,6 +69,7 @@ describe Useless::Doc::Serialization::Dump do
|
|
66
69
|
hash = Useless::Doc::Serialization::Load.json_to_hash(json)
|
67
70
|
hash['name'].should == 'Twiddles API'
|
68
71
|
hash['url'].should == 'twiddles.useless.io'
|
72
|
+
hash['timestamp'].should == Time.parse('2013-03-06 11:13 PM').iso8601
|
69
73
|
hash['description'].should == 'Pretty much, like, everything you\'re looking for'
|
70
74
|
|
71
75
|
resource_hash = Useless::Doc::Serialization::Load.json_to_hash(hash['resources'][0])
|
@@ -45,6 +45,7 @@ describe Useless::Doc::Serialization::Load do
|
|
45
45
|
domain = Useless::Doc::Serialization::Load.domain document.read
|
46
46
|
domain.name.should == 'Useless'
|
47
47
|
domain.url.should == 'http://useless.io'
|
48
|
+
domain.timestamp.should == Time.parse('2013-03-06T23:13:00-05:00')
|
48
49
|
domain.description.should == 'A collection of useless APIs.'
|
49
50
|
domain.apis.first.name.should == 'The Jah API'
|
50
51
|
domain.apis.first.url.should == 'http://jah.useless.io'
|
@@ -58,6 +59,7 @@ describe Useless::Doc::Serialization::Load do
|
|
58
59
|
api = Useless::Doc::Serialization::Load.api document.read
|
59
60
|
api.name.should == 'Twonk API'
|
60
61
|
api.url.should == 'twonk.useless.io'
|
62
|
+
api.timestamp.should == Time.parse('2013-03-06T23:13:00-05:00')
|
61
63
|
api.description.should == 'Twonk information. Duh.'
|
62
64
|
api.resources.first.path.should == '/twonks/:id'
|
63
65
|
api.resources.first.description.should == 'The most critical aspect.'
|
@@ -2,6 +2,8 @@ require File.dirname(__FILE__) + '/../../../spec_helper'
|
|
2
2
|
|
3
3
|
require 'nokogiri'
|
4
4
|
require 'useless/doc'
|
5
|
+
require 'useless/doc/core/domain'
|
6
|
+
require 'useless/doc/core/api'
|
5
7
|
require 'useless/doc/router'
|
6
8
|
require 'useless/doc/ui/godel'
|
7
9
|
|
@@ -9,10 +11,10 @@ describe Useless::Doc::UI::Godel do
|
|
9
11
|
describe '.html' do
|
10
12
|
context 'for a Core::Domain instance' do
|
11
13
|
before(:all) do
|
12
|
-
router = Useless::Doc::Router.default
|
14
|
+
@router = Useless::Doc::Router.default
|
13
15
|
json = load_document('domain.json').read
|
14
16
|
domain = Useless::Doc.load.domain(json)
|
15
|
-
result = Useless::Doc::UI::Godel.new(router).html(domain)
|
17
|
+
result = Useless::Doc::UI::Godel.new(@router).html(domain)
|
16
18
|
@doc = Nokogiri::HTML(result)
|
17
19
|
end
|
18
20
|
|
@@ -23,11 +25,25 @@ describe Useless::Doc::UI::Godel do
|
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'should render the domain description in an article p' do
|
26
|
-
description = @doc.css('article
|
28
|
+
description = @doc.css('article.description.domain p')
|
27
29
|
description.length.should == 1
|
28
30
|
description.first.content.should == 'A collection of useless APIs.'
|
29
31
|
end
|
30
32
|
|
33
|
+
it 'should not escape the description text' do
|
34
|
+
domain = Useless::Doc::Core::Domain.new description: '<span>The description</span>'
|
35
|
+
result = Useless::Doc::UI::Godel.new(@router).html(domain)
|
36
|
+
description = Nokogiri::HTML(result).css('article.description.domain p')
|
37
|
+
description.first.inner_html.should == '<span>The description</span>'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should interpret Markdown for the description' do
|
41
|
+
domain = Useless::Doc::Core::Domain.new description: '*The description*'
|
42
|
+
result = Useless::Doc::UI::Godel.new(@router).html(domain)
|
43
|
+
description = Nokogiri::HTML(result).css('article.description.domain p')
|
44
|
+
description.first.inner_html.should == '<em>The description</em>'
|
45
|
+
end
|
46
|
+
|
31
47
|
it 'should render API names as a\'s, with correct doc URLs' do
|
32
48
|
as = @doc.css('a.name')
|
33
49
|
as.length.should == 2
|
@@ -58,11 +74,25 @@ describe Useless::Doc::UI::Godel do
|
|
58
74
|
end
|
59
75
|
|
60
76
|
it 'should render the API description in an article p' do
|
61
|
-
description = @doc.css('article
|
77
|
+
description = @doc.css('article.description.api p')
|
62
78
|
description.length.should == 1
|
63
79
|
description.first.content.should == 'Twonk information. Duh.'
|
64
80
|
end
|
65
81
|
|
82
|
+
it 'should not escape the description text' do
|
83
|
+
domain = Useless::Doc::Core::API.new description: '<span>The description</span>'
|
84
|
+
result = Useless::Doc::UI::Godel.new(@router).html(domain)
|
85
|
+
description = Nokogiri::HTML(result).css('article.description.api p')
|
86
|
+
description.first.inner_html.should == '<span>The description</span>'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should interpret Markdown for the description' do
|
90
|
+
domain = Useless::Doc::Core::API.new description: '*The description*'
|
91
|
+
result = Useless::Doc::UI::Godel.new(@router).html(domain)
|
92
|
+
description = Nokogiri::HTML(result).css('article.description.api p')
|
93
|
+
description.first.inner_html.should == '<em>The description</em>'
|
94
|
+
end
|
95
|
+
|
66
96
|
it 'should render resouce paths as a\'s' do
|
67
97
|
as = @doc.css('a.path')
|
68
98
|
as.length.should == 2
|
data/useless-doc.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_dependency 'low', '~> 0.0.17'
|
21
21
|
gem.add_dependency 'sinatra', '~> 1.3.4'
|
22
22
|
gem.add_dependency 'mustache', '~> 0.99.4'
|
23
|
+
gem.add_dependency 'redcarpet', '~> 2.2.2'
|
23
24
|
|
24
25
|
gem.add_development_dependency 'rspec', '~> 2.12.0'
|
25
26
|
gem.add_development_dependency 'rack-test', '~> 0.6.2'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: useless-doc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-03-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: oj
|
@@ -107,6 +107,22 @@ dependencies:
|
|
107
107
|
- - ~>
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: 0.99.4
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: redcarpet
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.2.2
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.2.2
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
127
|
name: rspec
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|