useless-doc 0.2.1 → 0.2.2
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/lib/useless/doc/core/api.rb +8 -1
- data/lib/useless/doc/dsl.rb +93 -8
- data/lib/useless/doc/rack/retriever.rb +26 -2
- data/lib/useless/doc/sinatra.rb +66 -32
- data/lib/useless/doc/version.rb +1 -1
- data/lib/useless/doc.rb +5 -0
- data/spec/useless/doc/dsl_spec.rb +45 -15
- data/spec/useless/doc/rack/retriever_spec.rb +29 -2
- data/spec/useless/doc/sinatra_spec.rb +65 -14
- metadata +2 -2
data/lib/useless/doc/core/api.rb
CHANGED
@@ -4,21 +4,28 @@ module Useless
|
|
4
4
|
|
5
5
|
# Documentation for an entire API.
|
6
6
|
#
|
7
|
+
# @!attribute [r] url
|
8
|
+
# @return [String] a the URL of the API.
|
9
|
+
#
|
7
10
|
# @!attribute [r] description
|
8
11
|
# @return [String] a description of the API.
|
9
12
|
#
|
13
|
+
# @!attribute [r] timestamp
|
14
|
+
# @return [Time] the time that this API doc was last updated.
|
15
|
+
#
|
10
16
|
# @!attribute [r] resources
|
11
17
|
# @return [Array<Resource>] the resources included in the API.
|
12
18
|
#
|
13
19
|
class API
|
14
20
|
|
15
|
-
attr_accessor :url, :description, :resources
|
21
|
+
attr_accessor :url, :description, :timestamp, :resources
|
16
22
|
|
17
23
|
# @param [Hash] attrs corresponds to the class's instance attributes.
|
18
24
|
#
|
19
25
|
def initialize(attrs = {})
|
20
26
|
@url = attrs[:url]
|
21
27
|
@description = attrs[:description]
|
28
|
+
@timestamp = attrs[:timestamp]
|
22
29
|
@resources = attrs[:resources]
|
23
30
|
end
|
24
31
|
end
|
data/lib/useless/doc/dsl.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'useless/doc/core/api'
|
1
2
|
require 'useless/doc/core/body'
|
2
3
|
require 'useless/doc/core/header'
|
3
4
|
require 'useless/doc/core/request'
|
@@ -7,14 +8,14 @@ require 'useless/doc/core/response'
|
|
7
8
|
module Useless
|
8
9
|
module Doc
|
9
10
|
|
10
|
-
# A simple DSL for building
|
11
|
+
# A simple DSL for building API documentation.
|
11
12
|
#
|
12
13
|
# @example
|
13
|
-
# Useless::Doc
|
14
|
-
#
|
15
|
-
# description 'The entire collection of resources.'
|
14
|
+
# Useless::Doc.api 'resource.useless.io' do
|
15
|
+
# description 'A source of resources'
|
16
16
|
#
|
17
|
-
# get '
|
17
|
+
# get '/resources' do
|
18
|
+
# description 'Returns a full listing of the resources.'
|
18
19
|
# authentication_required false
|
19
20
|
# parameter 'page', 'The page of resources to be returned.'
|
20
21
|
# header 'X-Twiddle', 'The twiddle threshold.'
|
@@ -42,7 +43,10 @@ module Useless
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def self.included(base)
|
45
|
-
base.
|
46
|
+
base.instance_eval do
|
47
|
+
extend ClassMethods
|
48
|
+
attr_reader :attributes
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
52
|
def initialize(attributes = {})
|
@@ -60,6 +64,89 @@ module Useless
|
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
67
|
+
class API
|
68
|
+
include DSL::Member
|
69
|
+
|
70
|
+
def initialize(attributes = {})
|
71
|
+
@resource_dsls = []
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_attributes
|
76
|
+
{ resources: [] }
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate
|
80
|
+
@attributes[:resources] = @resource_dsls.map do |resource_dsl|
|
81
|
+
resource_dsl.generate
|
82
|
+
end
|
83
|
+
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def url(url)
|
88
|
+
@attributes[:url] = url
|
89
|
+
end
|
90
|
+
|
91
|
+
def description(description)
|
92
|
+
@attributes[:description] = description
|
93
|
+
end
|
94
|
+
|
95
|
+
def timestamp(timestamp)
|
96
|
+
if timestamp.is_a?(String)
|
97
|
+
timestamp = Time.parse(timestamp)
|
98
|
+
end
|
99
|
+
|
100
|
+
@attributes[:timestamp] = timestamp
|
101
|
+
end
|
102
|
+
|
103
|
+
def get(path, &block)
|
104
|
+
resource(path).get(&block)
|
105
|
+
end
|
106
|
+
|
107
|
+
def head(path, &block)
|
108
|
+
resource(path).head(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
def post(path, &block)
|
112
|
+
resource(path).post(&block)
|
113
|
+
end
|
114
|
+
|
115
|
+
def put(path, &block)
|
116
|
+
resource(path).put(&block)
|
117
|
+
end
|
118
|
+
|
119
|
+
def patch(path, &block)
|
120
|
+
resource(path).patch(&block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def delete(path, &block)
|
124
|
+
resource(path).delete(&block)
|
125
|
+
end
|
126
|
+
|
127
|
+
def trace(path, &block)
|
128
|
+
resource(path).trace(&block)
|
129
|
+
end
|
130
|
+
|
131
|
+
def connect(path, &block)
|
132
|
+
resource(path).connect(&block)
|
133
|
+
end
|
134
|
+
|
135
|
+
def resource(path, &block)
|
136
|
+
resource_dsl = @resource_dsls.find do |resource|
|
137
|
+
resource.attributes[:path] == path
|
138
|
+
end
|
139
|
+
|
140
|
+
unless resource_dsl
|
141
|
+
resource_dsl = Resource.new(path: path)
|
142
|
+
@resource_dsls << resource_dsl
|
143
|
+
end
|
144
|
+
|
145
|
+
resource_dsl.instance_eval(&block) if block_given?
|
146
|
+
resource_dsl
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
63
150
|
class Resource
|
64
151
|
include DSL::Member
|
65
152
|
|
@@ -107,8 +194,6 @@ module Useless
|
|
107
194
|
method(Doc::Core::Request::Method::CONNECT, description, &block)
|
108
195
|
end
|
109
196
|
|
110
|
-
private
|
111
|
-
|
112
197
|
def method(type, description, &block)
|
113
198
|
attributes = { method: type, description: description }
|
114
199
|
@attributes[:requests] << Request.build(attributes, &block)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'time'
|
1
2
|
require 'typhoeus'
|
2
3
|
|
3
4
|
module Useless
|
@@ -25,9 +26,32 @@ module Useless
|
|
25
26
|
end
|
26
27
|
|
27
28
|
module Standard
|
29
|
+
NotModified = 304
|
30
|
+
|
31
|
+
@cache = {}
|
32
|
+
|
28
33
|
def self.retrieve(url)
|
29
|
-
|
30
|
-
|
34
|
+
headers = { 'Accept' => 'application/json' }
|
35
|
+
|
36
|
+
if @cache[url]
|
37
|
+
headers['If-Modified-Since'] = @cache[url].timestamp.httpdate()
|
38
|
+
end
|
39
|
+
|
40
|
+
response = Typhoeus.options url, headers: headers
|
41
|
+
|
42
|
+
unless response.response_code == NotModified
|
43
|
+
@cache[url] = CacheItem.new(response.response_body, Time.now)
|
44
|
+
end
|
45
|
+
|
46
|
+
@cache[url].response_body
|
47
|
+
end
|
48
|
+
|
49
|
+
class CacheItem
|
50
|
+
attr_accessor :response_body, :timestamp
|
51
|
+
|
52
|
+
def initialize(response_body, timestamp)
|
53
|
+
@response_body, @timestamp = response_body, timestamp
|
54
|
+
end
|
31
55
|
end
|
32
56
|
end
|
33
57
|
|
data/lib/useless/doc/sinatra.rb
CHANGED
@@ -5,40 +5,74 @@ require 'useless/doc/serialization/dump'
|
|
5
5
|
|
6
6
|
module Useless
|
7
7
|
module Doc
|
8
|
+
|
9
|
+
# Provides access to the +Doc::DSL+ via the +.doc+ method. The JSON of the
|
10
|
+
# API doc that is built will be served via an OPTIONS request to the root.
|
11
|
+
# Resource documentation is similarly served via an OPTIONS request to the
|
12
|
+
# corresponding path.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# class ResourceApp < Sinatra::Base
|
16
|
+
# register Useless::Doc::Sinatra
|
17
|
+
#
|
18
|
+
# doc 'resources.useless.io' do
|
19
|
+
# description 'A place with resources'
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# doc.get '/some-resources' do
|
23
|
+
# description 'Get all of these resources'
|
24
|
+
#
|
25
|
+
# request do
|
26
|
+
# parameter 'page', 'The page of resources to return.'
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# response do
|
30
|
+
# body do
|
31
|
+
# attribute 'name', 'The name of the resource.'
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# ...
|
37
|
+
#
|
38
|
+
# end
|
39
|
+
#
|
8
40
|
module Sinatra
|
41
|
+
def doc=(doc)
|
42
|
+
@doc = doc
|
43
|
+
end
|
44
|
+
|
45
|
+
def doc(url = nil, &block)
|
46
|
+
@dsl ||= Useless::Doc::DSL::API.new(url: url)
|
47
|
+
@dsl.instance_eval(&block) if block_given?
|
48
|
+
@dsl
|
49
|
+
end
|
50
|
+
|
51
|
+
def generated_doc
|
52
|
+
@doc ||= @dsl.generate if @dsl
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.registered(app)
|
56
|
+
app.options '/' do
|
57
|
+
if api = self.class.generated_doc
|
58
|
+
last_modified api.timestamp
|
59
|
+
Useless::Doc::Serialization::Dump.api(api)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
app.options '/*' do
|
64
|
+
if api = self.class.generated_doc
|
65
|
+
resource = api.resources.find do |resource|
|
66
|
+
resource.path == "/#{params[:splat].first}"
|
67
|
+
end
|
68
|
+
|
69
|
+
if resource
|
70
|
+
last_modified api.timestamp
|
71
|
+
return Useless::Doc::Serialization::Dump.resource(resource)
|
72
|
+
end
|
73
|
+
end
|
9
74
|
|
10
|
-
|
11
|
-
# created will then be served via an OPTIONS request to the specified
|
12
|
-
# +path+.
|
13
|
-
#
|
14
|
-
# @param [String] path the path of the resource to be documented.
|
15
|
-
#
|
16
|
-
# @param block is passed to the DSL to build the resource.
|
17
|
-
#
|
18
|
-
# @example
|
19
|
-
# class ResourceApp < Sinatra::Base
|
20
|
-
# register Useless::Doc::Sinatra
|
21
|
-
#
|
22
|
-
# doc '/some-resources' do
|
23
|
-
# get 'Get all of these resources' do
|
24
|
-
# request do
|
25
|
-
# parameter 'page', 'The page of resources to return.'
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# response do
|
29
|
-
# body do
|
30
|
-
# attribute 'name', 'The name of the resource.'
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
def doc(path, &block)
|
38
|
-
resource = Useless::Doc::DSL::Resource.build path: path, &block
|
39
|
-
|
40
|
-
options(path) do
|
41
|
-
Doc::Serialization::Dump.resource(resource)
|
75
|
+
pass
|
42
76
|
end
|
43
77
|
end
|
44
78
|
end
|
data/lib/useless/doc/version.rb
CHANGED
data/lib/useless/doc.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
|
+
require 'time'
|
3
4
|
require 'rack/test'
|
5
|
+
require 'useless/doc'
|
4
6
|
require 'useless/doc/dsl'
|
5
7
|
|
6
8
|
describe Useless::Doc::DSL do
|
7
9
|
describe '.build' do
|
8
|
-
it 'should provide a terse DSL for building
|
9
|
-
|
10
|
-
|
11
|
-
description 'The entire collection of widgets'
|
10
|
+
it 'should provide a terse DSL for building API documentation (through Useless::Doc.api)' do
|
11
|
+
api = Useless::Doc.api 'widget.useless.io' do
|
12
|
+
description 'The canonical source of worldwide widgets.'
|
12
13
|
|
13
|
-
|
14
|
+
resource('/widgets').description 'The whole kit and kaboodle.'
|
15
|
+
|
16
|
+
get '/widgets' do
|
17
|
+
description 'List the entire collection of widgets.'
|
14
18
|
authentication_required false
|
19
|
+
|
15
20
|
parameter 'page', 'The page of widgets that you\'d like',
|
16
21
|
type: :query, required: false, default: 1
|
17
22
|
header 'X-Twiddle', 'The twiddle threshold.'
|
@@ -30,7 +35,7 @@ describe Useless::Doc::DSL do
|
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
33
|
-
post do
|
38
|
+
post '/widgets' do
|
34
39
|
authentication_required true
|
35
40
|
description 'Creates a new widget'
|
36
41
|
|
@@ -44,17 +49,42 @@ describe Useless::Doc::DSL do
|
|
44
49
|
response 201, 'The widget was successfully created'
|
45
50
|
response 422, 'The widget couldn\'t be created because name was missing'
|
46
51
|
end
|
52
|
+
|
53
|
+
delete '/widgets/:id' do
|
54
|
+
response 404, 'The widget could not be found.'
|
55
|
+
response 200, 'The widgets was deleted successfully'
|
56
|
+
end
|
57
|
+
|
58
|
+
resource '/widgets/:id' do
|
59
|
+
put do
|
60
|
+
description 'Resource-oriented specification.'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
timestamp '2013-01-12 12:44 AM'
|
47
65
|
end
|
48
66
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
67
|
+
api.url.should == 'widget.useless.io'
|
68
|
+
api.description.should == 'The canonical source of worldwide widgets.'
|
69
|
+
api.timestamp.should == Time.parse('2013-01-12 12:44 AM')
|
70
|
+
|
71
|
+
collection = api.resources.find { |resource| resource.path == '/widgets' }
|
72
|
+
collection.description.should == 'The whole kit and kaboodle.'
|
73
|
+
collection.requests[0].method.should == 'GET'
|
74
|
+
collection.requests[0].headers[0].description.should == 'The twiddle threshold.'
|
75
|
+
collection.requests[0].responses[1].code.should == 200
|
76
|
+
collection.requests[1].method.should == 'POST'
|
77
|
+
collection.requests[1].description.should == 'Creates a new widget'
|
78
|
+
collection.requests[1].body.content_type.should == 'application/json'
|
79
|
+
collection.requests[1].body.attributes[0].key.should == 'name'
|
80
|
+
collection.requests[1].responses[1].description.should == 'The widget couldn\'t be created because name was missing'
|
81
|
+
|
82
|
+
instance = api.resources.find { |resource| resource.path == '/widgets/:id' }
|
83
|
+
instance.requests[0].method.should == 'DELETE'
|
84
|
+
instance.requests[0].responses[0].code.should == 404
|
85
|
+
instance.requests[0].responses[1].code.should == 200
|
86
|
+
instance.requests[1].method.should == 'PUT'
|
87
|
+
instance.requests[1].description.should == 'Resource-oriented specification.'
|
58
88
|
end
|
59
89
|
end
|
60
90
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
2
|
|
3
|
+
require 'time'
|
3
4
|
require 'rack/test'
|
4
5
|
require 'useless/doc/rack/retriever'
|
5
6
|
|
@@ -42,8 +43,34 @@ describe Useless::Doc::Rack::Retriever do
|
|
42
43
|
end
|
43
44
|
|
44
45
|
describe Useless::Doc::Rack::Retriever::Standard do
|
45
|
-
|
46
|
-
Useless::Doc::Rack::Retriever::Standard.
|
46
|
+
before(:each) do
|
47
|
+
Useless::Doc::Rack::Retriever::Standard.instance_variable_set(:@cache, {})
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should make a normal request if the cache is empty.' do
|
51
|
+
Typhoeus.should_receive(:options).
|
52
|
+
with('http://some-api.granmal.com/some/resource', headers: { 'Accept' => 'application/json' }).
|
53
|
+
and_return(mock(:response, response_code: 200, response_body: '{ "some": "json" }'))
|
54
|
+
|
55
|
+
response = Useless::Doc::Rack::Retriever::Standard.retrieve('http://some-api.granmal.com/some/resource')
|
56
|
+
response.should == '{ "some": "json" }'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should make a request with a cache control header if there is a cache hit.' do
|
60
|
+
now = Time.now
|
61
|
+
Time.should_receive(:now).once.and_return(now)
|
62
|
+
|
63
|
+
Typhoeus.should_receive(:options).once.
|
64
|
+
with('http://some-api.granmal.com/some/resource', headers: { 'Accept' => 'application/json' }).
|
65
|
+
and_return(mock(:response, response_code: 200, response_body: '{ "some": "json" }'))
|
66
|
+
|
67
|
+
Typhoeus.should_receive(:options).once.
|
68
|
+
with('http://some-api.granmal.com/some/resource', headers: { 'Accept' => 'application/json', 'If-Modified-Since' => now.httpdate}).
|
69
|
+
and_return(mock(:response, response_code: 304, response_body: ''))
|
70
|
+
|
71
|
+
Useless::Doc::Rack::Retriever::Standard.retrieve('http://some-api.granmal.com/some/resource')
|
72
|
+
response = Useless::Doc::Rack::Retriever::Standard.retrieve('http://some-api.granmal.com/some/resource')
|
73
|
+
response.should == '{ "some": "json" }'
|
47
74
|
end
|
48
75
|
end
|
49
76
|
|
@@ -9,23 +9,20 @@ describe Useless::Doc::Sinatra do
|
|
9
9
|
class DocApp < Sinatra::Base
|
10
10
|
register Useless::Doc::Sinatra
|
11
11
|
|
12
|
-
doc '
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
12
|
+
doc 'resource.useless.io' do
|
13
|
+
description 'A resource repository'
|
14
|
+
timestamp '2013-01-01 12:00 PM'
|
15
|
+
end
|
16
|
+
|
17
|
+
doc.get '/some-resources' do
|
18
|
+
description 'Get all of these resources'
|
22
19
|
|
23
|
-
|
20
|
+
parameter 'since', 'Only resources after this date.'
|
21
|
+
|
22
|
+
response 200, 'The responses were fetched correctly.' do
|
24
23
|
body do
|
25
24
|
attribute 'name', 'The name of the resource.'
|
26
25
|
end
|
27
|
-
|
28
|
-
response 201, 'The resource was successfully created.'
|
29
26
|
end
|
30
27
|
end
|
31
28
|
|
@@ -33,9 +30,33 @@ describe Useless::Doc::Sinatra do
|
|
33
30
|
[]
|
34
31
|
end
|
35
32
|
|
33
|
+
doc.post '/some-resources' do
|
34
|
+
description 'Make a new resource'
|
35
|
+
|
36
|
+
body do
|
37
|
+
attribute 'name', 'The name of the resource.'
|
38
|
+
end
|
39
|
+
|
40
|
+
response 201, 'The resource was successfully created.'
|
41
|
+
end
|
42
|
+
|
36
43
|
post '/some-resources' do
|
37
44
|
201
|
38
45
|
end
|
46
|
+
|
47
|
+
doc.put '/some-resources/:id' do
|
48
|
+
description 'Update a resource'
|
49
|
+
|
50
|
+
body do
|
51
|
+
attribute 'name', 'The name of the resource.'
|
52
|
+
end
|
53
|
+
|
54
|
+
response 200, 'The resource was successfully updated.'
|
55
|
+
end
|
56
|
+
|
57
|
+
put '/some-resources/:id' do
|
58
|
+
200
|
59
|
+
end
|
39
60
|
end
|
40
61
|
|
41
62
|
include Rack::Test::Methods
|
@@ -44,7 +65,17 @@ describe Useless::Doc::Sinatra do
|
|
44
65
|
DocApp
|
45
66
|
end
|
46
67
|
|
47
|
-
it 'should serve documentation for the
|
68
|
+
it 'should serve documentation for the whole API from root.' do
|
69
|
+
options 'http://some-api.granmal.com/'
|
70
|
+
api = Useless::Doc::Serialization::Load.api(last_response.body)
|
71
|
+
|
72
|
+
paths = api.resources.map { |resource| resource.path }
|
73
|
+
paths.length.should == 2
|
74
|
+
paths.should include '/some-resources'
|
75
|
+
paths.should include '/some-resources/:id'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should serve documentation for the specified resource.' do
|
48
79
|
options 'http://some-api.granmal.com/some-resources'
|
49
80
|
resource = Useless::Doc::Serialization::Load.resource(last_response.body)
|
50
81
|
|
@@ -57,4 +88,24 @@ describe Useless::Doc::Sinatra do
|
|
57
88
|
post.responses[0].code.should == 201
|
58
89
|
end
|
59
90
|
|
91
|
+
it 'should return a 404 if the specified resource doesn\'t exist' do
|
92
|
+
options 'http://some-api.granmal.com/some-nonexistant-resources'
|
93
|
+
last_response.should be_not_found
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should return a 304 if the doc has not been modified since specified time' do
|
97
|
+
header 'If-Modified-Since', Time.parse('2013-01-02 12:00 PM').httpdate
|
98
|
+
options 'http://some-api.granmal.com/'
|
99
|
+
last_response.status.should == 304
|
100
|
+
options 'http://some-api.granmal.com/some-resources'
|
101
|
+
last_response.status.should == 304
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should not return a 304 if the doc has been modified since the specified time' do
|
105
|
+
header 'If-Modified-Since', Time.parse('2012-12-31 12:00 PM').httpdate
|
106
|
+
options 'http://some-api.granmal.com/'
|
107
|
+
last_response.status.should_not == 304
|
108
|
+
options 'http://some-api.granmal.com/some-resources'
|
109
|
+
last_response.status.should_not == 304
|
110
|
+
end
|
60
111
|
end
|
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.2.
|
4
|
+
version: 0.2.2
|
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-01-
|
12
|
+
date: 2013-01-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: oj
|