naranya_ecm-sdk 0.0.42 → 0.0.43
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rspec +3 -0
- data/Gemfile +1 -2
- data/lib/aasm/persistence/rest_persistence.rb +11 -6
- data/lib/naranya_ecm-sdk.rb +4 -65
- data/lib/naranya_ecm-sdk/version.rb +1 -1
- data/lib/naranya_ecm/models/media_resource.rb +1 -1
- data/lib/naranya_ecm/models/notification.rb +2 -2
- data/lib/naranya_ecm/rest/errors.rb +16 -3
- data/lib/naranya_ecm/rest/finder_methods.rb +4 -4
- data/lib/naranya_ecm/rest/model.rb +87 -19
- data/lib/naranya_ecm/rest/persistence.rb +234 -46
- data/lib/naranya_ecm/rest/relation.rb +15 -5
- data/lib/naranya_ecm/search/results.rb +19 -17
- data/lib/ncontent-sdk-testing.rb +13 -0
- data/lib/ncontent-sdk.rb +85 -0
- data/lib/ncontent/sdk/config.rb +75 -0
- data/lib/ncontent/sdk/faraday_middleware.rb +4 -0
- data/lib/ncontent/sdk/faraday_middleware/required_response_format.rb +14 -0
- data/lib/ncontent/sdk/faraday_middleware/response_parser.rb +35 -0
- data/lib/ncontent/sdk/faraday_middleware/rest_api_call_benchmark.rb +49 -0
- data/lib/ncontent/sdk/railtie.rb +59 -0
- data/lib/ncontent/sdk/rest_client.rb +131 -0
- data/lib/ncontent/sdk/testing/server_mock.rb +316 -0
- data/naranya_ecm-sdk.gemspec +24 -10
- metadata +85 -68
- data/lib/naranya_ecm/rest/client.rb +0 -92
- data/spec/models/category_spec.rb +0 -16
- data/spec/models/content_spec.rb +0 -20
- data/spec/models/content_version_spec.rb +0 -7
- data/spec/models/download_authorization.rb +0 -7
- data/spec/models/media_spec.rb +0 -7
- data/spec/models/module_spec.rb +0 -18
- data/spec/spec_helper.rb +0 -47
- data/spec/support/naranya_ecms_shared_specs.rb +0 -21
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'oauth2'
|
2
|
+
require 'ncontent/sdk/faraday_middleware'
|
3
|
+
|
4
|
+
module NContent
|
5
|
+
|
6
|
+
module SDK
|
7
|
+
|
8
|
+
class RESTClient
|
9
|
+
|
10
|
+
CLIENT_TOKEN_CACHE_KEY = "ncontent/api_client/client_token".freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
delegate :config, :logger, :cache, to: NContent::SDK
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the thread's OAuth2 client instance.
|
17
|
+
#
|
18
|
+
# In multi-thread environments, each thread will have it's own client
|
19
|
+
# instance object - since it's saved as a @instance_variable - but
|
20
|
+
# shouldn't be a problem since the mutable data is actually the
|
21
|
+
# client_token (see self.client_token).
|
22
|
+
def self.instance
|
23
|
+
@instance ||= OAuth2::Client.new(
|
24
|
+
config.api_key, config.api_secret, site: config.api_host
|
25
|
+
) do |faraday|
|
26
|
+
### Oauth2 Client (Faraday) builder:
|
27
|
+
|
28
|
+
faraday.request :url_encoded # them posts...
|
29
|
+
|
30
|
+
faraday.response :logger, logger
|
31
|
+
|
32
|
+
faraday.use NContent::SDK::FaradayMiddleware::RESTAPICallBenchmark
|
33
|
+
|
34
|
+
faraday.use NContent::SDK::FaradayMiddleware::ResponseParser
|
35
|
+
|
36
|
+
if defined?(Patron)
|
37
|
+
faraday.adapter :patron # Prefer patron if exists
|
38
|
+
else
|
39
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a client_token initialized from cache.
|
45
|
+
#
|
46
|
+
# In multi-thread environments, each thread will have it's own
|
47
|
+
# client_token object - since it's saved as a @instance_variable -, but
|
48
|
+
# all of them should have the same token data - since it's initialized
|
49
|
+
# from cache (We're assuming a thread-safe cache such as redis gem).
|
50
|
+
def self.client_token
|
51
|
+
@client_token ||= begin
|
52
|
+
cached_token_data = cache.read CLIENT_TOKEN_CACHE_KEY
|
53
|
+
if cached_token_data.present?
|
54
|
+
OAuth2::AccessToken.from_hash instance, cached_token_data
|
55
|
+
else
|
56
|
+
new_token = instance.client_credentials.get_token
|
57
|
+
cache.write CLIENT_TOKEN_CACHE_KEY, new_token.to_hash
|
58
|
+
new_token
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Clears the thread's token object. If called with 'true', it also
|
64
|
+
# clears the token data from the cache.
|
65
|
+
def self.clear_client_token!(clear_from_cache = false)
|
66
|
+
cache.delete CLIENT_TOKEN_CACHE_KEY if clear_from_cache
|
67
|
+
@client_token = nil
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
# Make a request to the API:
|
72
|
+
#
|
73
|
+
# @param [Symbol] verb the HTTP request method
|
74
|
+
# @param [String] path the HTTP URL path of the request
|
75
|
+
# @param [Hash] opts the options to make the request with
|
76
|
+
def self.request(verb, path=nil, opts = {}, &block)
|
77
|
+
|
78
|
+
cleared_cached_token_data = false
|
79
|
+
|
80
|
+
begin
|
81
|
+
|
82
|
+
# If we beforehand know that the current token expired then clear the
|
83
|
+
# token object from the current thread. We'll not clear it from the
|
84
|
+
# cache store here, as maybe another thread has already updated the
|
85
|
+
# cached token data:
|
86
|
+
clear_client_token! if client_token.expired?
|
87
|
+
|
88
|
+
client_token.request(verb, path, opts) do |req|
|
89
|
+
req.options.timeout = config.api_call_timeout
|
90
|
+
req.options.open_timeout = config.api_call_open_timeout
|
91
|
+
yield req if block_given?
|
92
|
+
end
|
93
|
+
|
94
|
+
rescue OAuth2::Error => error
|
95
|
+
|
96
|
+
if error.response.status == 401 && !cleared_cached_token_data
|
97
|
+
# Here the cached token data is stale, so we'll force the cached
|
98
|
+
# token data to be cleared:
|
99
|
+
cleared_cached_token_data = clear_client_token!(true)
|
100
|
+
retry
|
101
|
+
else
|
102
|
+
::NaranyaEcm::Rest::RestError.raise_by_failed_response(error.response)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.get(path=nil, opts = {}, &block)
|
108
|
+
request(:get, path, opts, &block)
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.post(path=nil, opts = {}, &block)
|
112
|
+
request(:post, path, opts, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.put(path=nil, opts = {}, &block)
|
116
|
+
request(:put, path, opts, &block)
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.patch(path=nil, opts = {}, &block)
|
120
|
+
request(:patch, path, opts, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.delete(path=nil, opts = {}, &block)
|
124
|
+
request(:delete, path, opts, &block)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'bson'
|
3
|
+
|
4
|
+
module NContent
|
5
|
+
module SDK
|
6
|
+
module Testing
|
7
|
+
|
8
|
+
##
|
9
|
+
# Clase que simula el servicio de nContent:
|
10
|
+
# Ver https://robots.thoughtbot.com/how-to-stub-external-services-in-tests
|
11
|
+
class ServerMock < Sinatra::Base
|
12
|
+
|
13
|
+
class ValidationError < StandardError
|
14
|
+
attr_reader :errors
|
15
|
+
def initialize(errors = {})
|
16
|
+
@errors = errors.collect({}) do |hsh, keyval|
|
17
|
+
key, val = keyval
|
18
|
+
hsh[key] = [] unless hsh[key].is_a? Array
|
19
|
+
hsh[key] << val
|
20
|
+
hsh
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
delegate :config, to: :class
|
26
|
+
|
27
|
+
class << self
|
28
|
+
delegate :config, to: NContent::SDK
|
29
|
+
end
|
30
|
+
|
31
|
+
COLLECTION_NAMES = %w(
|
32
|
+
categories content_versions contents download_authorizations
|
33
|
+
media_processes media_profiles media_resources
|
34
|
+
).freeze
|
35
|
+
|
36
|
+
RESOURCE_TYPES = COLLECTION_NAMES.map(&:singularize).freeze
|
37
|
+
|
38
|
+
def self.get_blank_collection_set
|
39
|
+
COLLECTION_NAMES.inject({}) do |hash, collection_name|
|
40
|
+
hash[collection_name] = {}
|
41
|
+
hash
|
42
|
+
end.with_indifferent_access
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.clear_collections!
|
46
|
+
@collections = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.collections
|
50
|
+
@collections ||= get_blank_collection_set
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.method_missing(method_name, *args, &block)
|
54
|
+
if COLLECTION_NAMES.include? method_name.to_s
|
55
|
+
|
56
|
+
# Create the method:
|
57
|
+
define_singleton_method method_name do
|
58
|
+
collections[method_name.to_s]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Call the newly defined method:
|
62
|
+
self.send method_name, *args
|
63
|
+
elsif method_name =~ /\Aget_(\w+)\z/ && RESOURCE_TYPES.include?($1)
|
64
|
+
|
65
|
+
# Create the method:
|
66
|
+
define_singleton_method method_name do |resource_id|
|
67
|
+
get_resource $1, resource_id
|
68
|
+
end
|
69
|
+
|
70
|
+
# Call the newly defined method:
|
71
|
+
self.send method_name, *args
|
72
|
+
elsif method_name =~ /\A(create|update|upsert)_(\w+)\z/ && RESOURCE_TYPES.include?($2)
|
73
|
+
|
74
|
+
# Create the methods:
|
75
|
+
%w(create update upsert).each do |method_prefix|
|
76
|
+
new_method_name = "#{method_prefix}_#{$2}".to_sym
|
77
|
+
define_singleton_method new_method_name do |*args|
|
78
|
+
upsert_resource $2.pluralize, *args
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Call the newly defined method:
|
83
|
+
self.send method_name, *args
|
84
|
+
else
|
85
|
+
super # You *must* call super if you don't handle the
|
86
|
+
# method, otherwise you'll mess up Ruby's method
|
87
|
+
# lookup.
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.upsert_resource(collection_name, given_resource_attributes, resource_id = nil)
|
92
|
+
# Ignore the ID:
|
93
|
+
given_resource_attributes.delete 'id'
|
94
|
+
|
95
|
+
invalid_attributes = given_resource_attributes.select do |k, v|
|
96
|
+
v =~ /invalid/i
|
97
|
+
end
|
98
|
+
|
99
|
+
raise ValidationError, invalid_attributes.collect {} do |h, kv|
|
100
|
+
k, v = kv;
|
101
|
+
h[k] = ['is invalid']
|
102
|
+
h
|
103
|
+
end if invalid_attributes.any?
|
104
|
+
|
105
|
+
if resource_id.present? && collections[collection_name].key?(resource_id)
|
106
|
+
collections[collection_name][resource_id].merge! given_resource_attributes
|
107
|
+
else
|
108
|
+
# Caso especial:
|
109
|
+
if collection_name == 'contents'
|
110
|
+
given_resource_attributes.reverse_merge "lifecycle_state" => 'draft'
|
111
|
+
|
112
|
+
# Generar iconos y screenshots en caso de que sea Game:
|
113
|
+
if given_resource_attributes['type'] == "Game"
|
114
|
+
icon = self.upsert_media_resource(
|
115
|
+
roles: ["icon"],
|
116
|
+
downloadable_url: "http://test-bucket.s3.amazonaws.com/icons/original/#{resource_id}.jpg",
|
117
|
+
type: "image/jpg",
|
118
|
+
access_type: "public",
|
119
|
+
parent_document_id: resource_id,
|
120
|
+
parent_document_type: "Content"
|
121
|
+
)
|
122
|
+
|
123
|
+
%w(120w 150w 300w).each do |processed_role|
|
124
|
+
self.upsert_media_resource(
|
125
|
+
roles: ["icon", "processed", "v2", processed_role],
|
126
|
+
downloadable_url: "http://test-bucket.s3.amazonaws.com/icons/#{processed_role}/#{resource_id}.jpg",
|
127
|
+
type: "image/jpg",
|
128
|
+
access_type: "public",
|
129
|
+
parent_document_id: icon[:id],
|
130
|
+
parent_document_type: "MediaResource"
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
resource_id = (BSON::ObjectId.new).to_s
|
138
|
+
collections[collection_name][resource_id] = { id: resource_id }.merge(
|
139
|
+
given_resource_attributes
|
140
|
+
).with_indifferent_access
|
141
|
+
end
|
142
|
+
|
143
|
+
collections[collection_name][resource_id]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Éste método es útil para llamar "stub_request", usando el host+port configurado por ENV["NARANYA_ECM_SITE"]
|
147
|
+
def self.url_matcher
|
148
|
+
@url_matcher ||= begin
|
149
|
+
site_url = URI config.api_host
|
150
|
+
pattern_string = site_url.host
|
151
|
+
pattern_string += ":#{site_url.port}" unless site_url.port == 80
|
152
|
+
Regexp.new pattern_string, "i"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.get_resource(resource_type, resource_id)
|
157
|
+
collection_name = resource_type.pluralize
|
158
|
+
requested_resource = self.collections[collection_name.to_s].fetch resource_id, nil
|
159
|
+
|
160
|
+
# Traerse los MR's:
|
161
|
+
requested_resource[:media_resources] = self.media_resources.values.select do |mr|
|
162
|
+
mr[:parent_document_type] == "Content" && mr[:parent_document_id] == resource_id
|
163
|
+
end.map do |mr|
|
164
|
+
mr[:media_resources] = self.media_resources.values.select do |c_mr|
|
165
|
+
c_mr[:parent_document_type] == "MediaResource" && c_mr[:parent_document_id] == mr[:id]
|
166
|
+
end
|
167
|
+
mr
|
168
|
+
end if requested_resource.present? && collection_name == 'contents'
|
169
|
+
|
170
|
+
requested_resource
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.get_resource_headers(collection_name, resource_id)
|
174
|
+
raise "no collection name given" unless collection_name.present?
|
175
|
+
raise "no resource id given" unless resource_id.present?
|
176
|
+
|
177
|
+
headers = {}
|
178
|
+
headers = {
|
179
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
180
|
+
"X-Xss-Protection" => "1; mode=block",
|
181
|
+
"X-Content-Type-Options" => "nosniff",
|
182
|
+
"Location" => "http://ecm.naranya.net:5000/#{collection_name}/#{resource_id}",
|
183
|
+
"Content-Type" => "application/json; charset=utf-8",
|
184
|
+
"Vary" => "Accept-Encoding",
|
185
|
+
"Etag" => '"a547f3ead872de01928f79724390aba4"',
|
186
|
+
"Cache-Control" => "max-age=0, private, must-revalidate",
|
187
|
+
"Set-Cookie" => "request_method=POST; path=/",
|
188
|
+
"X-Request-Id" => "a6463225-df54-450d-b571-7068f3c96364",
|
189
|
+
"X-Runtime" => "'0.241093'",
|
190
|
+
"Transfer-Encoding" => "chunked"
|
191
|
+
} if self.collections[collection_name].key? resource_id
|
192
|
+
end
|
193
|
+
|
194
|
+
delegate :collections, :get_resource, :get_resource_headers,
|
195
|
+
:upsert_resource, to: :class
|
196
|
+
|
197
|
+
# Servicio de token de oauth:
|
198
|
+
# Responderá con un token "fake" de prueba...
|
199
|
+
post "/oauth/token" do
|
200
|
+
case params[:grant_type]
|
201
|
+
when "client_credentials"
|
202
|
+
status 201
|
203
|
+
|
204
|
+
headers \
|
205
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
206
|
+
"X-XSS-Protection" => "1; mode=block",
|
207
|
+
"X-Content-Type-Options" => "nosniff",
|
208
|
+
"Cache-Control" => "no-store",
|
209
|
+
"Pragma" => "no-cache",
|
210
|
+
"Content-Type" => "application/json; charset=utf-8",
|
211
|
+
"Vary" => "Accept-Encoding",
|
212
|
+
"ETag" => '"75e236bc0e47293b0bfbb7a5d16a06c3"',
|
213
|
+
"X-Request-Id" => "f4d38d78-84b9-4c41-8943-cf2c6f519061",
|
214
|
+
"X-Runtime" => "0.015009",
|
215
|
+
"Transfer-Encoding" => "chunked"
|
216
|
+
|
217
|
+
body ActiveSupport::JSON.encode(
|
218
|
+
access_token: "NContentClientTestAccessTokenA",
|
219
|
+
refresh_token: "NContentClientTestRefreshToken",
|
220
|
+
token_type: "bearer",
|
221
|
+
expires_in: 7200
|
222
|
+
)
|
223
|
+
else
|
224
|
+
status 401
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
get '/:collection_name.json' do
|
230
|
+
status 200
|
231
|
+
|
232
|
+
headers \
|
233
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
234
|
+
"X-Xss-Protection" => "1; mode=block",
|
235
|
+
"X-Content-Type-Options" => "nosniff",
|
236
|
+
"Content-Type" => "application/json; charset=utf-8",
|
237
|
+
"Vary" => "Accept-Encoding",
|
238
|
+
"Etag" => '"d751713988987e9331980363e24189ce"',
|
239
|
+
"Cache-Control" => "max-age=0, private, must-revalidate",
|
240
|
+
"Set-Cookie" => "request_method=GET; path=/",
|
241
|
+
"X-Request-Id" => "4fe9e39f-2b2d-400f-9da7-0b9ad611ce26",
|
242
|
+
"X-Runtime" => "'0.019181'",
|
243
|
+
"Transfer-Encoding" => "chunked"
|
244
|
+
|
245
|
+
body ActiveSupport::JSON.encode collections[params[:collection_name]].values
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
post "/:collection_name.json" do
|
250
|
+
collection_name = params[:collection_name]
|
251
|
+
params = ActiveSupport::JSON.decode request.body.read
|
252
|
+
attributes = params[collection_name.singularize]
|
253
|
+
|
254
|
+
if attributes.present?
|
255
|
+
begin
|
256
|
+
resource = upsert_resource collection_name, attributes
|
257
|
+
status 201
|
258
|
+
headers get_resource_headers collection_name, resource[:id]
|
259
|
+
body ActiveSupport::JSON.encode resource
|
260
|
+
rescue ValidationError => e
|
261
|
+
status 422
|
262
|
+
body ActiveSupport::JSON.encode e.errors
|
263
|
+
end
|
264
|
+
else
|
265
|
+
status 422
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
get "/:collection_name/:id.json" do
|
271
|
+
resource = get_resource params[:collection_name], params[:id]
|
272
|
+
if resource.present?
|
273
|
+
status 201
|
274
|
+
headers get_resource_headers params[:collection_name], resource[:id]
|
275
|
+
body ActiveSupport::JSON.encode resource
|
276
|
+
else
|
277
|
+
status 404
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
put '/:collection_name/:id.json' do
|
282
|
+
|
283
|
+
collection_name = params[:collection_name]
|
284
|
+
resource_id = params[:id]
|
285
|
+
|
286
|
+
if collection_name != 'download_authorizations' and resource = get_resource(collection_name, resource_id)
|
287
|
+
|
288
|
+
params = ActiveSupport::JSON.decode(request.body.read)
|
289
|
+
|
290
|
+
if attributes = params[collection_name.singularize]
|
291
|
+
begin
|
292
|
+
resource = upsert_resource collection_name, attributes, resource_id
|
293
|
+
status 200
|
294
|
+
headers get_resource_headers collection_name, resource[:id]
|
295
|
+
body ActiveSupport::JSON.encode resource
|
296
|
+
rescue ValidationError => e
|
297
|
+
status 422
|
298
|
+
body ActiveSupport::JSON.encode e.errors
|
299
|
+
end
|
300
|
+
else
|
301
|
+
status 422
|
302
|
+
end
|
303
|
+
|
304
|
+
else
|
305
|
+
status 404
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
get // do
|
310
|
+
byebug
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
data/naranya_ecm-sdk.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
|
5
4
|
require "naranya_ecm-sdk/version"
|
6
5
|
|
7
6
|
Gem::Specification.new do |spec|
|
@@ -9,26 +8,41 @@ Gem::Specification.new do |spec|
|
|
9
8
|
spec.version = NaranyaEcm::VERSION
|
10
9
|
spec.authors = ["Roberto Quintanilla"]
|
11
10
|
spec.email = ["roberto.quintanilla@naranya.com"]
|
11
|
+
|
12
12
|
spec.summary = %q{Cliente Ruby de NaranyaEcm}
|
13
13
|
spec.description = %q{Cliente Ruby de NaranyaEcm para aplicaciones cliente.}
|
14
14
|
spec.homepage = "http://naranya.com"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.bindir = "exe"
|
20
28
|
spec.require_paths = ["lib"]
|
21
29
|
|
22
30
|
spec.add_dependency "activesupport", "~> 4.0", ">= 4.0.5"
|
23
31
|
spec.add_dependency "activemodel", "~> 4.0", ">= 4.0.5"
|
24
|
-
|
32
|
+
|
33
|
+
spec.add_dependency "oauth2", "~> 1.0"
|
34
|
+
# which uses Faraday as it's client... we get to choose which HTTP driver to use!
|
35
|
+
|
25
36
|
spec.add_dependency "aasm", "~> 3.2", ">= 3.2.1"
|
26
37
|
spec.add_dependency "fog-core", "1.25.0"
|
27
38
|
spec.add_dependency "fog", "~> 1.22", ">= 1.22.1"
|
28
39
|
|
29
|
-
spec.add_development_dependency "bundler",
|
30
|
-
spec.add_development_dependency "rake",
|
31
|
-
spec.add_development_dependency "
|
32
|
-
spec.add_development_dependency "
|
33
|
-
spec.add_development_dependency "
|
40
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
41
|
+
spec.add_development_dependency "rake", "~> 10.3"
|
42
|
+
spec.add_development_dependency "factory_girl", "~> 4.5"
|
43
|
+
spec.add_development_dependency "rspec", "~> 3.2"
|
44
|
+
spec.add_development_dependency "sinatra", "~> 1.4"
|
45
|
+
spec.add_development_dependency "bson", "~> 3.0"
|
46
|
+
|
47
|
+
spec.add_development_dependency "webmock", "~> 1.18"
|
34
48
|
end
|