acfs 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +7 -21
- data/acfs.gemspec +4 -8
- data/lib/acfs/adapter/typhoeus.rb +5 -5
- data/lib/acfs/collections/paginatable.rb +2 -2
- data/lib/acfs/errors.rb +32 -12
- data/lib/acfs/location.rb +3 -3
- data/lib/acfs/middleware/json.rb +3 -3
- data/lib/acfs/operation.rb +15 -0
- data/lib/acfs/request.rb +2 -1
- data/lib/acfs/request/callbacks.rb +3 -3
- data/lib/acfs/resource/attributes.rb +2 -0
- data/lib/acfs/resource/persistence.rb +0 -2
- data/lib/acfs/resource/query_methods.rb +1 -1
- data/lib/acfs/service.rb +19 -14
- data/lib/acfs/stub.rb +3 -4
- data/lib/acfs/version.rb +1 -1
- data/spec/acfs/global_spec.rb +2 -2
- data/spec/acfs/middleware/json_spec.rb +1 -1
- data/spec/acfs/resource/attributes/float_spec.rb +1 -1
- data/spec/acfs/resource/attributes_spec.rb +3 -5
- metadata +9 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53acc88297d3217a5d01db2ce713f935835b05438471fd2b69918029b5480954
|
4
|
+
data.tar.gz: d61a2f4e5014dab37d4e1a857d548df8dfb42afcaba219005556f203ea68da65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35f66d3569256b183cb7c78fed7c41f0ce916f41ec081e60696693af8b869d47e797a53474942c4847cefb621e58d3d52ddf629168c63b7b2d558d1db23cf74a
|
7
|
+
data.tar.gz: 5e03be0b16cb469061b55a3c4467a4fa71496925caa086ed5d024ed79aaefdcc568afd970d526c284b08b8db06593f51886d8642f035f78f2ed7f89bd1b8f311
|
data/CHANGELOG.md
CHANGED
@@ -14,6 +14,16 @@
|
|
14
14
|
### Breaks
|
15
15
|
|
16
16
|
|
17
|
+
## 1.5.0 - (2020-06-19)
|
18
|
+
---
|
19
|
+
|
20
|
+
### New
|
21
|
+
* Error classes for more HTTP error responses: `400`, `401`, `403`, `500`, `502`, `503`, `504`.
|
22
|
+
|
23
|
+
### Changes
|
24
|
+
* Replace deprecated MultiJson with core JSON module
|
25
|
+
|
26
|
+
|
17
27
|
## 1.4.0 - (2020-06-12)
|
18
28
|
---
|
19
29
|
|
data/README.md
CHANGED
@@ -10,6 +10,7 @@ Acfs is a library to develop API client libraries for single services within a l
|
|
10
10
|
|
11
11
|
Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses on a per service level and automatic request queuing and parallel processing. See Usage for more.
|
12
12
|
|
13
|
+
|
13
14
|
## Installation
|
14
15
|
|
15
16
|
Add this line to your application's Gemfile:
|
@@ -24,6 +25,7 @@ Or install it yourself as:
|
|
24
25
|
|
25
26
|
> gem install acfs
|
26
27
|
|
28
|
+
|
27
29
|
## Usage
|
28
30
|
|
29
31
|
First you need to define your service(s):
|
@@ -145,6 +147,7 @@ Acfs has basic update support using `PUT` requests:
|
|
145
147
|
@user.persisted? # => true
|
146
148
|
```
|
147
149
|
|
150
|
+
|
148
151
|
## Singleton resources
|
149
152
|
|
150
153
|
Singletons can be used in Acfs by creating a new resource which inherits from `SingletonResource`:
|
@@ -183,6 +186,7 @@ my_single = Single.find name: 'Max'
|
|
183
186
|
Acfs.run # sends GET request with param to /single?name=Max
|
184
187
|
```
|
185
188
|
|
189
|
+
|
186
190
|
## Resource Inheritance
|
187
191
|
|
188
192
|
Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
|
@@ -219,6 +223,7 @@ Acfs.run
|
|
219
223
|
@computer[2].class # => Pc
|
220
224
|
```
|
221
225
|
|
226
|
+
|
222
227
|
## Stubbing
|
223
228
|
|
224
229
|
You can stub resources in applications using an Acfs service client:
|
@@ -279,6 +284,7 @@ it 'should find user number one' do
|
|
279
284
|
end
|
280
285
|
```
|
281
286
|
|
287
|
+
|
282
288
|
## Instrumentation
|
283
289
|
|
284
290
|
Acfs supports [instrumentation via active support][1].
|
@@ -296,21 +302,6 @@ Read [official guide][2] to see to to subscribe.
|
|
296
302
|
[1]: http://guides.rubyonrails.org/active_support_instrumentation.html
|
297
303
|
[2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
|
298
304
|
|
299
|
-
## Roadmap
|
300
|
-
|
301
|
-
* Update
|
302
|
-
* Better new? detection eg. storing ETag from request resources.
|
303
|
-
* Use PATCH for with only changed attributes and `If-Unmodifed-Since`
|
304
|
-
and `If-Match` header fields if resource was surly loaded from service
|
305
|
-
and not created with an id (e.g `User.new id: 5, name: "john"`).
|
306
|
-
* Conflict detection (ETag / If-Unmodified-Since)
|
307
|
-
* High level features
|
308
|
-
* Support for custom mime types on client and server side. (`application/vnd.myservice.user.v2+msgpack`)
|
309
|
-
* Server side components
|
310
|
-
* Reusing model definitions for generating responses?
|
311
|
-
* Rails responders providing REST operations with integrated ETag,
|
312
|
-
Modified Headers, conflict detection, ...
|
313
|
-
* Documentation
|
314
305
|
|
315
306
|
## Contributing
|
316
307
|
|
@@ -322,14 +313,9 @@ Read [official guide][2] to see to to subscribe.
|
|
322
313
|
7. Push to the branch (`git push origin my-new-feature`)
|
323
314
|
8. Create new Pull Request
|
324
315
|
|
325
|
-
## Contributors
|
326
|
-
|
327
|
-
* [Nicolas Fricke](https://github.com/nicolas-fricke)
|
328
|
-
* [Tino Junge](https://github.com/tino-junge)
|
329
|
-
* [Malte Swart](https://github.com/mswart)
|
330
316
|
|
331
317
|
## License
|
332
318
|
|
333
319
|
MIT License
|
334
320
|
|
335
|
-
Copyright (c) 2013 Jan Graichen. MIT license, see LICENSE for more details.
|
321
|
+
Copyright (c) 2013-2020 Jan Graichen. MIT license, see LICENSE for more details.
|
data/acfs.gemspec
CHANGED
@@ -25,15 +25,11 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
26
|
spec.require_paths = %w[lib]
|
27
27
|
|
28
|
-
spec.add_runtime_dependency 'actionpack', '>=
|
29
|
-
spec.add_runtime_dependency 'activemodel', '>=
|
30
|
-
spec.add_runtime_dependency 'activesupport', '>=
|
31
|
-
spec.add_runtime_dependency 'multi_json'
|
32
|
-
|
33
|
-
# Bundle update w/o version resolves to 0.3.3 ...
|
34
|
-
spec.add_runtime_dependency 'typhoeus', '~> 1.0'
|
35
|
-
|
28
|
+
spec.add_runtime_dependency 'actionpack', '>= 5.2'
|
29
|
+
spec.add_runtime_dependency 'activemodel', '>= 5.2'
|
30
|
+
spec.add_runtime_dependency 'activesupport', '>= 5.2'
|
36
31
|
spec.add_runtime_dependency 'rack'
|
32
|
+
spec.add_runtime_dependency 'typhoeus', '~> 1.0'
|
37
33
|
|
38
34
|
spec.add_development_dependency 'bundler'
|
39
35
|
|
@@ -55,14 +55,14 @@ module Acfs
|
|
55
55
|
request = ::Typhoeus::Request.new(req.url, **@opts.merge(opts))
|
56
56
|
|
57
57
|
request.on_complete do |response|
|
58
|
-
if response.timed_out?
|
59
|
-
|
60
|
-
|
58
|
+
raise ::Acfs::TimeoutError.new(req) if response.timed_out?
|
59
|
+
|
60
|
+
if response.code.zero?
|
61
61
|
# Failed to get HTTP response
|
62
62
|
raise ::Acfs::RequestError.new(req, response.return_message)
|
63
|
-
else
|
64
|
-
req.complete! convert_response(req, response)
|
65
63
|
end
|
64
|
+
|
65
|
+
req.complete! convert_response(req, response)
|
66
66
|
end
|
67
67
|
|
68
68
|
request
|
data/lib/acfs/errors.rb
CHANGED
@@ -26,7 +26,7 @@ module Acfs
|
|
26
26
|
|
27
27
|
class TimeoutError < RequestError
|
28
28
|
def initialize(request)
|
29
|
-
super(request,
|
29
|
+
super(request, 'Timeout reached')
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -38,13 +38,13 @@ module Acfs
|
|
38
38
|
def initialize(opts = {})
|
39
39
|
@response = opts[:response]
|
40
40
|
|
41
|
-
if response
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
message = if response
|
42
|
+
(opts[:message] ? opts[:message] + ':' : 'Received') +
|
43
|
+
" #{response.code} for #{response.request.method.upcase}" \
|
44
|
+
" #{response.request.url} #{response.request.format}"
|
45
|
+
else
|
46
|
+
opts[:message] || 'Received erroneous response'
|
47
|
+
end
|
48
48
|
|
49
49
|
super opts, message
|
50
50
|
end
|
@@ -67,11 +67,19 @@ module Acfs
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
# 400
|
71
|
+
class BadRequest < ErroneousResponse; end
|
72
|
+
|
73
|
+
# 401
|
74
|
+
class Unauthorized < ErroneousResponse; end
|
75
|
+
|
76
|
+
# 403
|
77
|
+
class Forbidden < ErroneousResponse; end
|
74
78
|
|
79
|
+
# 404
|
80
|
+
class ResourceNotFound < ErroneousResponse; end
|
81
|
+
|
82
|
+
# 422
|
75
83
|
class InvalidResource < ErroneousResponse
|
76
84
|
attr_reader :errors, :resource
|
77
85
|
|
@@ -91,6 +99,18 @@ module Acfs
|
|
91
99
|
end
|
92
100
|
end
|
93
101
|
|
102
|
+
# 500
|
103
|
+
class ServerError < ErroneousResponse; end
|
104
|
+
|
105
|
+
# 502
|
106
|
+
class BadGateway < ErroneousResponse; end
|
107
|
+
|
108
|
+
# 503
|
109
|
+
class ServiceUnavailable < ErroneousResponse; end
|
110
|
+
|
111
|
+
# 504
|
112
|
+
class GatewayTimeout < ErroneousResponse; end
|
113
|
+
|
94
114
|
# A ResourceNotLoaded error will be thrown when calling some
|
95
115
|
# modifing methods on not loaded resources as it is usally
|
96
116
|
# unwanted to call e.g. `update_attributes` on a not loaded
|
data/lib/acfs/location.rb
CHANGED
@@ -49,7 +49,7 @@ module Acfs
|
|
49
49
|
def extract_arg(key, hashes)
|
50
50
|
hashes.each_with_index do |hash, index|
|
51
51
|
if hash.key?(key)
|
52
|
-
return (index
|
52
|
+
return (index.zero? ? hash.delete(key) : hash.fetch(key))
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
@@ -72,9 +72,9 @@ module Acfs
|
|
72
72
|
args.fetch(sym) do
|
73
73
|
if args[:raise].nil? || args[:raise]
|
74
74
|
raise ArgumentError.new "URI path argument `#{sym}' missing on `#{self}'. Given: `#{args}.inspect'"
|
75
|
-
else
|
76
|
-
":#{sym}"
|
77
75
|
end
|
76
|
+
|
77
|
+
":#{sym}"
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
data/lib/acfs/middleware/json.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'json'
|
4
4
|
|
5
5
|
module Acfs
|
6
6
|
module Middleware
|
@@ -12,11 +12,11 @@ module Acfs
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def encode(data)
|
15
|
-
::
|
15
|
+
::JSON.dump data
|
16
16
|
end
|
17
17
|
|
18
18
|
def decode(body)
|
19
|
-
::
|
19
|
+
::JSON.load body
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
data/lib/acfs/operation.rb
CHANGED
@@ -8,6 +8,7 @@ module Acfs
|
|
8
8
|
#
|
9
9
|
class Operation
|
10
10
|
attr_reader :action, :params, :resource, :data, :callback, :location, :url
|
11
|
+
|
11
12
|
delegate :service, to: :resource
|
12
13
|
delegate :call, to: :callback
|
13
14
|
|
@@ -71,10 +72,24 @@ module Acfs
|
|
71
72
|
|
72
73
|
def handle_failure(response)
|
73
74
|
case response.code
|
75
|
+
when 400
|
76
|
+
raise ::Acfs::BadRequest.new response: response
|
77
|
+
when 401
|
78
|
+
raise ::Acfs::Unauthorized.new response: response
|
79
|
+
when 403
|
80
|
+
raise ::Acfs::Forbidden.new response: response
|
74
81
|
when 404
|
75
82
|
raise ::Acfs::ResourceNotFound.new response: response
|
76
83
|
when 422
|
77
84
|
raise ::Acfs::InvalidResource.new response: response, errors: response.data.try(:[], 'errors')
|
85
|
+
when 500
|
86
|
+
raise ::Acfs::ServerError.new response: response
|
87
|
+
when 502
|
88
|
+
raise ::Acfs::BadGateway.new response: response
|
89
|
+
when 503
|
90
|
+
raise ::Acfs::ServiceUnavailable.new response: response
|
91
|
+
when 504
|
92
|
+
raise ::Acfs::GatewayTimeout.new response: response
|
78
93
|
else
|
79
94
|
raise ::Acfs::ErroneousResponse.new response: response
|
80
95
|
end
|
data/lib/acfs/request.rb
CHANGED
@@ -11,7 +11,6 @@ module Acfs
|
|
11
11
|
attr_reader :url, :headers, :params, :data, :method, :operation
|
12
12
|
|
13
13
|
include Request::Callbacks
|
14
|
-
|
15
14
|
def initialize(url, options = {}, &block)
|
16
15
|
@url = URI.parse(url.to_s).tap do |_url|
|
17
16
|
@data = options.delete(:data) || nil
|
@@ -20,7 +19,9 @@ module Acfs
|
|
20
19
|
@params = options.delete(:params) || {}
|
21
20
|
@method = options.delete(:method) || :get
|
22
21
|
end.to_s
|
22
|
+
|
23
23
|
@operation = options.delete(:operation) || nil
|
24
|
+
|
24
25
|
on_complete(&block) if block_given?
|
25
26
|
end
|
26
27
|
|
@@ -45,9 +45,9 @@ module Acfs
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def call_callback(res, index)
|
48
|
-
if index
|
49
|
-
|
50
|
-
|
48
|
+
return if index >= callbacks.size
|
49
|
+
|
50
|
+
callbacks[index].call(res, proc {|bres| call_callback bres, index + 1 })
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -48,9 +48,11 @@ class Acfs::Resource
|
|
48
48
|
# @return [HashWithIndifferentAccess{Symbol => Object}]
|
49
49
|
# Attributes and their values.
|
50
50
|
#
|
51
|
+
# rubocop:disable Naming/MemoizedInstanceVariableName
|
51
52
|
def attributes
|
52
53
|
@_attrs ||= HashWithIndifferentAccess.new
|
53
54
|
end
|
55
|
+
# rubocop:enable Naming/MemoizedInstanceVariableName
|
54
56
|
|
55
57
|
# @api public
|
56
58
|
#
|
data/lib/acfs/service.rb
CHANGED
@@ -40,21 +40,23 @@ module Acfs
|
|
40
40
|
|
41
41
|
action = opts[:action] || :list
|
42
42
|
|
43
|
-
path =
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
path = begin
|
44
|
+
if opts[:path].is_a?(Hash) && opts[:path].key?(action)
|
45
|
+
opts[:path].fetch(action)
|
46
|
+
else
|
47
|
+
path = if opts[:path].is_a?(Hash)
|
48
|
+
opts[:path][:all].to_s
|
49
|
+
else
|
50
|
+
opts[:path].to_s
|
51
|
+
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
if path.blank?
|
54
|
+
path = (resource_class.name || 'class').pluralize.underscore
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
57
|
+
resource_class.location_default_path(action, path.strip)
|
58
|
+
end
|
59
|
+
end
|
58
60
|
|
59
61
|
if path.nil?
|
60
62
|
raise ArgumentError.new "Location for `#{action}' explicit disabled by set to nil."
|
@@ -87,7 +89,10 @@ module Acfs
|
|
87
89
|
#
|
88
90
|
def base_url
|
89
91
|
unless (base = Acfs::Configuration.current.locate identity)
|
90
|
-
raise ArgumentError.new
|
92
|
+
raise ArgumentError.new \
|
93
|
+
"#{identity} not configured. Add `locate '" \
|
94
|
+
"#{identity.to_s.underscore}', 'http://service.url/'` " \
|
95
|
+
'to your configuration.'
|
91
96
|
end
|
92
97
|
|
93
98
|
base.to_s
|
data/lib/acfs/stub.rb
CHANGED
@@ -16,7 +16,7 @@ module Acfs
|
|
16
16
|
@opts[:with].stringify_keys! if @opts[:with].is_a? Hash
|
17
17
|
@opts[:return].stringify_keys! if @opts[:return].is_a? Hash
|
18
18
|
|
19
|
-
if @opts[:return].is_a?
|
19
|
+
if @opts[:return].is_a?(Array) # rubocop:disable Style/GuardClause
|
20
20
|
@opts[:return].map! {|h| h.stringify_keys! if h.is_a? Hash }
|
21
21
|
end
|
22
22
|
end
|
@@ -53,9 +53,8 @@ module Acfs
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def called?(count = nil)
|
56
|
-
if count.respond_to?
|
57
|
-
|
58
|
-
end # For `5.times` Enumerators
|
56
|
+
count = count.count if count.respond_to?(:count)
|
57
|
+
|
59
58
|
count.nil? ? calls.any? : calls.size == count
|
60
59
|
end
|
61
60
|
|
data/lib/acfs/version.rb
CHANGED
data/spec/acfs/global_spec.rb
CHANGED
@@ -122,8 +122,8 @@ describe ::Acfs::Global do
|
|
122
122
|
|
123
123
|
describe '#runner' do
|
124
124
|
it 'returns per-thread runner' do
|
125
|
-
runner1 = Thread.new { acfs.runner }
|
126
|
-
runner2 = Thread.new { acfs.runner }
|
125
|
+
runner1 = Thread.new { acfs.runner }.value
|
126
|
+
runner2 = Thread.new { acfs.runner }.value
|
127
127
|
|
128
128
|
expect(runner1).to_not equal runner2
|
129
129
|
end
|
@@ -47,7 +47,7 @@ describe Acfs::Middleware::JSON do
|
|
47
47
|
let(:body) { data.to_json[4..-4] }
|
48
48
|
|
49
49
|
it 'should raise an error' do
|
50
|
-
expect { request.complete! response }.to raise_error(
|
50
|
+
expect { request.complete! response }.to raise_error(::JSON::ParserError)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acfs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Graichen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -16,44 +16,44 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '5.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '5.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activesupport
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '5.2'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '5.2'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rack
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '1.0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rack
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: bundler
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|