json-crud-api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d29a60e6d78b872ce5373491643e567e2c6131d8
4
+ data.tar.gz: ec36f07906e2cb7c7fa18811b586c33331879ddf
5
+ SHA512:
6
+ metadata.gz: 58cb2c22a099f4898bf5ccea53481e7ee8e1ae625991f319603cc58e824058e17c232242f7b0715e02283004225b6b51ad09d64a061411b8ed03b02f09a5d7c7
7
+ data.tar.gz: 7e4112a6b627e591b1017a9ceed570f880dddf526e8589368e6eb1c544f7c92f824877a9e04df0634f40602f6e6a6254a5ed585dc1a0ba8d3a1d153dfc1e3164
@@ -0,0 +1,4 @@
1
+ require "json-crud-api/service"
2
+ require "json-crud-api/crud"
3
+ require "json-crud-api/api"
4
+ require "json-crud-api/auth_client"
@@ -0,0 +1,102 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+
4
+ module JsonCrudApi
5
+ class API < Sinatra::Base
6
+
7
+ register JsonCrudApi::Crud
8
+
9
+ before do
10
+ # HTTPS Only (if configured)
11
+ if settings.respond_to? :https_only and settings.https_only and env['HTTPS'] != 'on' and env['HTTP_X_FORWARDED_PROTO'] != 'https'
12
+ fail_with_error 403, 'SSL_ONLY', 'The API can only be accessed over SSL (HTTPS)'
13
+ end
14
+
15
+ # JSON Errors
16
+ @errors = []
17
+
18
+ # No-Cache by default
19
+ cache_control :no_cache, :max_age => 0
20
+
21
+ # Session
22
+ @user = nil
23
+ @logged_in = false
24
+ if settings.respond_to? :auth_client
25
+ @session_id = env['HTTP_X_SESSION_ID']
26
+ unless @session_id.nil?
27
+ @user = settings.auth_client.get(@session_id)
28
+ @logged_in = !@user.nil?
29
+ end
30
+ end
31
+ settings.services.each do |k,service|
32
+ service.set_user @user if service.respond_to? :set_user
33
+ end
34
+
35
+ # JSON Payload
36
+ request.body.rewind
37
+ body = request.body.read
38
+ if body.length > 2
39
+ begin
40
+ @payload = JSON.parse body, :symbolize_names => true
41
+ rescue JSON::ParserError
42
+ fail_with_error 422, 'JSON_PARSE_ERROR', 'The JSON payload cannot be parsed'
43
+ end
44
+ else
45
+ @payload = nil
46
+ end
47
+
48
+ # CORS
49
+ content_type 'application/json; charset=utf-8'
50
+ response.headers['Access-Control-Allow-Origin'] = '*'
51
+ response.headers['Access-Control-Allow-Headers'] = 'Content-Type'
52
+ end
53
+
54
+ def logged_in?
55
+ @logged_in
56
+ end
57
+
58
+ def add_error(code, message, reference = nil)
59
+ @errors = [] if @errors.nil?
60
+
61
+ error = {
62
+ :code => code,
63
+ :message => message,
64
+ }
65
+ error[:reference] = reference unless reference.nil?
66
+ @errors.push error
67
+ end
68
+
69
+ def fail_with_error(status, code, message, reference = nil)
70
+ add_error code,message,reference
71
+ fail_with_errors status
72
+ end
73
+
74
+ def fail_with_errors(status = 422)
75
+ halt status, JSON.fast_generate({
76
+ :success => false,
77
+ :errors => @errors
78
+ })
79
+ end
80
+
81
+ def fail_not_found
82
+ fail_with_error 404, 'NOT_FOUND','The resource cannot be found.'
83
+ end
84
+
85
+ def fail_unauthorized
86
+ fail_with_error 401, 'UNAUTHORIZED','Authorization is required to perform this operation on the resource.'
87
+ end
88
+
89
+ def fail_forbidden
90
+ fail_with_error 403, 'FORBIDDEN','The user is not allowed to perform this operation on the resource.'
91
+ end
92
+
93
+ not_found do
94
+ fail_not_found
95
+ end
96
+
97
+ error do
98
+ fail_with_error 500, 'INTERNAL_ERROR','The server has encountered an unknown error.'
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+
3
+ module JsonCrudApi
4
+ class AuthClient
5
+
6
+ attr_accessor :redis, :session_ttl, :prefix
7
+
8
+ def initialize(options)
9
+ @redis = options[:redis_client]
10
+ @session_ttl = options[:session_ttl]
11
+ @prefix = options[:key_prefix]
12
+ end
13
+
14
+ def get(key)
15
+ key = get_redis_key(key)
16
+ data = @redis.get(key)
17
+ return nil if data.nil?
18
+ touch(key)
19
+ JSON.parse(data, :symbolize_names => true)
20
+ end
21
+
22
+ def delete(key)
23
+ key = get_redis_key(key)
24
+ return false unless @redis.exists(key)
25
+ @redis.del(key)
26
+ true
27
+ end
28
+
29
+ def touch(key)
30
+ key = get_redis_key(key)
31
+ return false unless @redis.exists(key)
32
+ @redis.expire(key, @session_ttl)
33
+ true
34
+ end
35
+
36
+ def get_redis_key(key)
37
+ return key.to_s if @prefix.nil?
38
+ @prefix.to_s+key.to_s
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,71 @@
1
+ module JsonCrudApi
2
+ module Crud
3
+
4
+ def crud_api(url, key, options = [])
5
+
6
+ unless options.include? :disable_read
7
+
8
+ unless options.include? :disable_get_all
9
+ get url do
10
+ service = settings.services[key]
11
+ presenter = settings.presenters[key]
12
+ fail_unauthorized unless service.user_authorized_for? :get_all
13
+ entities = service.get_all
14
+ fail_not_found if entities.nil?
15
+
16
+ JSON.fast_generate settings.presenters[key].render(entities)
17
+ end
18
+ end
19
+
20
+ unless options.include? :disable_get
21
+ get url+"/:id" do
22
+ service = settings.services[key]
23
+ presenter = settings.presenters[key]
24
+ fail_unauthorized unless service.user_authorized_for? :get
25
+ entity = service.get(params["id"])
26
+ fail_not_found if entity.nil?
27
+
28
+ JSON.fast_generate settings.presenters[key].render(entity)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ unless options.include? :disable_write
35
+
36
+ unless options.include? :disable_post
37
+ post url do
38
+ service = settings.services[key]
39
+ presenter = settings.presenters[key]
40
+ fail_unauthorized 'create' unless service.user_authorized_for? :create
41
+ entity = service.create(presenter.parse(@payload))
42
+
43
+ JSON.fast_generate settings.presenters[key].render(entity)
44
+ end
45
+ end
46
+
47
+ unless options.include? :disable_put
48
+ put url+"/:id" do
49
+ service = settings.services[key]
50
+ presenter = settings.presenters[key]
51
+ fail_unauthorized 'update' unless service.user_authorized_for? :update
52
+ fail_not_found unless service.update(params["id"], presenter.parse(@payload))
53
+ entity = service.get(params["id"])
54
+ JSON.fast_generate settings.presenters[key].render(entity)
55
+ end
56
+ end
57
+
58
+ unless options.include? :disable_delete
59
+ delete url+"/:id" do
60
+ service = settings.services[key]
61
+ presenter = settings.presenters[key]
62
+ fail_unauthorized 'delete' unless service.user_authorized_for? :delete
63
+ fail_not_found unless service.delete(params["id"])
64
+ 204
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,85 @@
1
+ require 'rubygems'
2
+
3
+ module JsonCrudApi
4
+ class Service
5
+
6
+ attr_accessor :log_service, :repo, :user, :scope_map, :user_scopes
7
+
8
+ def initialize(options)
9
+ @log_service = options[:log_service]
10
+ @repo = options[:repository]
11
+ @scope_map = options[:scope_map]
12
+ @user = nil
13
+ @user_scopes = nil
14
+ end
15
+
16
+ # Create a record with the given attributes
17
+ def create(params)
18
+ @repo.create(params)
19
+ end
20
+
21
+ # Determine if a record with the given id exists
22
+ def exists?(id)
23
+ @repo.all(:id => id).count > 0
24
+ end
25
+
26
+ # Get all records
27
+ def get_all
28
+ @repo.all
29
+ end
30
+
31
+ # Get the first record with the given id
32
+ def get(id)
33
+ @repo.first(:id => id)
34
+ end
35
+
36
+ # Update a record with the given id with the given attributes
37
+ # Returns false if the record does not exist.
38
+ def update(id, params)
39
+ record = get(id)
40
+ return false if record.nil?
41
+
42
+ record.update(params)
43
+ end
44
+
45
+ # Delete a record with the given id
46
+ # Returns false if the record does not exist.
47
+ def delete(id)
48
+ record = get(id)
49
+ return false if record.nil?
50
+
51
+ record.destroy
52
+ end
53
+
54
+ # Set the current user
55
+ def set_user(user)
56
+ @user = user
57
+ set_user_scopes(user[:scopes]) unless @user.nil?
58
+ end
59
+
60
+ # Set the current user scopes
61
+ def set_user_scopes(user_scopes)
62
+ @user_scopes = user_scopes
63
+ end
64
+
65
+ # Determine if the current user is authorized for the given operation
66
+ def user_authorized_for?(operation)
67
+ # Auth is disabled if scope map is nil
68
+ return true if @scope_map.nil?
69
+ # Auth succeeds if there is no map for this operation
70
+ return true if @scope_map[operation].nil?
71
+ # Auth fails if user is not logged in
72
+ return false if @user.nil?
73
+ # Auth fails if user has no scopes
74
+ return false if @user_scopes.nil? or @user_scopes.empty?
75
+
76
+ if @scope_map[operation].is_a?(Array)
77
+ # Auth succeeds if the intersection of allowed scopes and mapped scopes is non-empty.
78
+ return !((@scope_map[operation] & @user_scopes).empty?)
79
+ end
80
+
81
+ # Auth succeeds if the mapped scope is singular and the user posesses it
82
+ @user_scopes.include?(@scope_map[operation])
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,12 @@
1
+ require "coveralls"
2
+ Coveralls.wear!
3
+ SimpleCov.coverage_dir('spec/coverage')
4
+
5
+ require "dotenv"
6
+ Dotenv.load
7
+
8
+ require "json-crud-api"
9
+
10
+ RSpec.configure do |c|
11
+ c.include Helpers
12
+ end
@@ -0,0 +1,3 @@
1
+ module Helpers
2
+ # Spec helpers here
3
+ end
@@ -0,0 +1,101 @@
1
+ require "helper"
2
+
3
+ describe JsonCrudApi::AuthClient do
4
+ before(:each) do
5
+ @mock_redis = double('redis')
6
+
7
+ @client = JsonCrudApi::AuthClient.new({
8
+ :redis_client => @mock_redis,
9
+ :session_ttl => 200,
10
+ :key_prefix => 789
11
+ })
12
+ end
13
+
14
+ describe '#initialize' do
15
+ it 'should inject dependencies correctly' do
16
+ @client.redis.should be @mock_redis
17
+ @client.session_ttl.should be 200
18
+ @client.prefix.should be 789
19
+ end
20
+ end
21
+
22
+ describe '#get' do
23
+ it 'should call get_redis_key and redis get' do
24
+ @client.should_receive(:get_redis_key).with('one')
25
+ @mock_redis.should_receive(:get).and_return nil
26
+ @client.get('one')
27
+ end
28
+
29
+ it 'should return nil if redis get return nil' do
30
+ @mock_redis.should_receive(:get).and_return nil
31
+ @client.get('one').should be nil
32
+ end
33
+
34
+ it 'should call touch if redis get is non nil' do
35
+ @mock_redis.should_receive(:get).and_return '{}'
36
+ @client.should_receive(:touch).with('789one')
37
+ @client.get('one')
38
+ end
39
+
40
+ it 'should parse JSON from redis get' do
41
+ @mock_redis.should_receive(:get).and_return '{"five":5}'
42
+ @client.should_receive(:touch).with('789one')
43
+ @client.get('one').should eq({ :five => 5 })
44
+ end
45
+ end
46
+
47
+ describe '#delete' do
48
+ it 'should call get_redis_key and redis exists and return false if exists is false' do
49
+ @client.should_receive(:get_redis_key).with('one')
50
+ @mock_redis.should_receive(:exists).and_return false
51
+ @client.delete('one').should be false
52
+ end
53
+
54
+ it 'should call redis del and return true if redis exists is true' do
55
+ @mock_redis.should_receive(:exists).and_return true
56
+ @mock_redis.should_receive(:del).with('789one')
57
+ @client.delete('one').should be true
58
+ end
59
+ end
60
+
61
+ describe '#touch' do
62
+ it 'should call get_redis_key and redis exists and return false if exists is false' do
63
+ @client.should_receive(:get_redis_key).with('one')
64
+ @mock_redis.should_receive(:exists).and_return false
65
+ @client.touch('one').should be false
66
+ end
67
+
68
+ it 'should call redis expire and return true if redis exists is true' do
69
+ @mock_redis.should_receive(:exists).and_return true
70
+ @mock_redis.should_receive(:expire).with('789one', 200)
71
+ @client.touch('one').should be true
72
+ end
73
+ end
74
+
75
+ describe '#get_redis_key' do
76
+ it 'should return key if prefix is nil' do
77
+ @client.prefix = nil
78
+ @client.get_redis_key('one').should eq 'one'
79
+ end
80
+
81
+ it 'should return key.to_s if prefix is nil' do
82
+ @client.prefix = nil
83
+ @client.get_redis_key(1).should eq '1'
84
+ end
85
+
86
+ it 'should return prefix plus key if prefix is not nil' do
87
+ @client.prefix = 'pre-'
88
+ @client.get_redis_key('one').should eq 'pre-one'
89
+ end
90
+
91
+ it 'should return prefix plus key.to_s if prefix is not nil' do
92
+ @client.prefix = 'post-'
93
+ @client.get_redis_key(1).should eq 'post-1'
94
+ end
95
+
96
+ it 'should return prefix.to_s plus key.to_s if prefix is not nil' do
97
+ @client.prefix = 5
98
+ @client.get_redis_key(1).should eq '51'
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,219 @@
1
+ require "helper"
2
+
3
+ describe JsonCrudApi::Service do
4
+ before(:each) do
5
+ @mock_repo = double('repo')
6
+ @mock_log = double('Log')
7
+ @mock_map = double('Map')
8
+
9
+ @service = JsonCrudApi::Service.new({
10
+ :log_service => @mock_log,
11
+ :repository => @mock_repo,
12
+ :scope_map => @mock_map
13
+ })
14
+ end
15
+
16
+ describe '#initialize' do
17
+ it 'should inject dependencies correctly' do
18
+ @service.log_service.should be @mock_log
19
+ @service.repo.should be @mock_repo
20
+ @service.scope_map.should be @mock_map
21
+ end
22
+
23
+ it 'should initialize user and scopes to nil' do
24
+ @service.user.should be nil
25
+ @service.user_scopes.should be nil
26
+ end
27
+ end
28
+
29
+ describe '#create' do
30
+ it 'should call create on repo with params' do
31
+ params = { :one => 'one', :two => 'two' }
32
+ @mock_repo.should_receive(:create).with(params).and_return(2)
33
+ @service.create(params).should eq 2
34
+ end
35
+ end
36
+
37
+ describe '#exists?' do
38
+ it 'should call all on repo with correct id' do
39
+ query_object = OpenStruct.new :count => 1
40
+ @mock_repo.should_receive(:all).with(:id => 3)
41
+ .and_return(query_object)
42
+ @service.exists?(3).should eq true
43
+ end
44
+
45
+ it 'should return false when count is zero' do
46
+ query_object = OpenStruct.new :count => 0
47
+ @mock_repo.should_receive(:all).with(:id => 3)
48
+ .and_return(query_object)
49
+ @service.exists?(3).should eq false
50
+ end
51
+
52
+ it 'should return true when count is one' do
53
+ query_object = OpenStruct.new :count => 1
54
+ @mock_repo.should_receive(:all).with(:id => 3)
55
+ .and_return(query_object)
56
+ @service.exists?(3).should eq true
57
+ end
58
+
59
+ it 'should return true when count is more than one' do
60
+ query_object = OpenStruct.new :count => 2
61
+ @mock_repo.should_receive(:all).with(:id => 3)
62
+ .and_return(query_object)
63
+ @service.exists?(3).should eq true
64
+ end
65
+ end
66
+
67
+ describe '#get_all' do
68
+ it 'should call all on repo and return output' do
69
+ @mock_repo.should_receive(:all).with().and_return(67)
70
+ @service.get_all.should eq 67
71
+ end
72
+ end
73
+
74
+ describe '#get' do
75
+ it 'should call first on repo with correct id and return result' do
76
+ @mock_repo.should_receive(:first).with({:id=>8}).and_return(123)
77
+ @service.get(8).should eq 123
78
+ end
79
+ end
80
+
81
+ describe '#update' do
82
+ it 'should call get on service with correct id' do
83
+ @service.should_receive(:get).with(5).and_return(nil)
84
+ @service.update(5,nil)
85
+ end
86
+
87
+ it 'should return false if get returns nil' do
88
+ @service.should_receive(:get).with(5).and_return(nil)
89
+ @service.update(5,nil).should eq false
90
+ end
91
+
92
+ it 'should call update on record with correct params' do
93
+ params = { :one => 'one', :two => 'two' }
94
+ record = double('entity')
95
+ @service.should_receive(:get).with(5)
96
+ .and_return(record)
97
+ record.should_receive(:update).with(params)
98
+ .and_return(789)
99
+ @service.update(5,params).should eq 789
100
+ end
101
+ end
102
+
103
+ describe '#delete' do
104
+ it 'should call get on service with correct id' do
105
+ @service.should_receive(:get).with(5).and_return(nil)
106
+ @service.delete(5)
107
+ end
108
+
109
+ it 'should return false if get returns nil' do
110
+ @service.should_receive(:get).with(5).and_return(nil)
111
+ @service.delete(5).should eq false
112
+ end
113
+
114
+ it 'should call delete on record' do
115
+ record = double('entity')
116
+ @service.should_receive(:get).with(5)
117
+ .and_return(record)
118
+ record.should_receive(:destroy).and_return(109)
119
+ @service.delete(5).should eq 109
120
+ end
121
+ end
122
+
123
+ describe '#set_user' do
124
+ it 'should set user in service to param' do
125
+ @service.set_user(nil)
126
+ @service.user.should eq nil
127
+ end
128
+
129
+ it 'should not call set_user_scopes if user is nil' do
130
+ @service.should_not_receive(:set_user_scopes)
131
+ @service.set_user(nil)
132
+ @service.user.should eq nil
133
+ end
134
+
135
+ it 'should call set_user_scopes if user is not' do
136
+ user = { :scopes => [1,2] }
137
+ @service.should_receive(:set_user_scopes).with([1,2])
138
+ @service.set_user(user)
139
+ @service.user.should eq user
140
+ end
141
+ end
142
+
143
+ describe '#set_user_scopes' do
144
+ it 'should set user_scopes in service to param' do
145
+ @service.set_user_scopes(nil)
146
+ @service.user_scopes.should eq nil
147
+
148
+ @service.set_user_scopes(234234)
149
+ @service.user_scopes.should eq 234234
150
+ end
151
+ end
152
+
153
+ describe '#user_authorized_for?' do
154
+ it 'should return true if scope_map is nil' do
155
+ @service.scope_map = nil
156
+ @service.user_authorized_for?(:one).should be true
157
+ end
158
+
159
+ it 'should return true if scope_map is not nil but no map for operation' do
160
+ @service.scope_map = { :two => 'TWO' }
161
+ @service.user_authorized_for?(:one).should be true
162
+ end
163
+
164
+ it 'should return false if user is nil' do
165
+ @service.scope_map = { :two => 'TWO' }
166
+ @service.user = nil
167
+ @service.user_authorized_for?(:two).should be false
168
+ end
169
+
170
+ it 'should return false if user has nil scopes' do
171
+ @service.scope_map = { :two => 'TWO' }
172
+ @service.user = { :name => "Tom" }
173
+ @service.user_scopes = nil
174
+ @service.user_authorized_for?(:two).should be false
175
+ end
176
+
177
+ it 'should return false if user has empty scopes' do
178
+ @service.scope_map = { :two => 'TWO' }
179
+ @service.user = { :name => "Tom" }
180
+ @service.user_scopes = []
181
+ @service.user_authorized_for?(:two).should be false
182
+ end
183
+
184
+ it 'should return true if scope map exists in user scopes' do
185
+ @service.scope_map = { :two => 'FIVE'}
186
+ @service.user = { :name => "Tom" }
187
+ @service.user_scopes = [ 'ONE', 'TWO', 'FIVE']
188
+ @service.user_authorized_for?(:two).should be true
189
+ end
190
+
191
+ it 'should return false if scope map does not exist in user scopes' do
192
+ @service.scope_map = { :two => 'SEVEN'}
193
+ @service.user = { :name => "Tom" }
194
+ @service.user_scopes = [ 'ONE', 'TWO', 'FIVE']
195
+ @service.user_authorized_for?(:two).should be false
196
+ end
197
+
198
+ it 'should return true if scope map is array and shares one scope with user' do
199
+ @service.scope_map = { :two => ['TWO'] }
200
+ @service.user = { :name => "Tom" }
201
+ @service.user_scopes = [ 'ONE', 'TWO', 'THREE']
202
+ @service.user_authorized_for?(:two).should be true
203
+ end
204
+
205
+ it 'should return true if scope map is array and shares more than one scope with user' do
206
+ @service.scope_map = { :two => ['TWO','THREE'] }
207
+ @service.user = { :name => "Tom" }
208
+ @service.user_scopes = [ 'ONE', 'TWO', 'THREE']
209
+ @service.user_authorized_for?(:two).should be true
210
+ end
211
+
212
+ it 'should return false if scope map is array and does not share scopes with user' do
213
+ @service.scope_map = { :two => ['FOUR'] }
214
+ @service.user = { :name => "Tom" }
215
+ @service.user_scopes = [ 'ONE', 'TWO', 'THREE']
216
+ @service.user_authorized_for?(:two).should be false
217
+ end
218
+ end
219
+ end
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-crud-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Cully
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: coveralls
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-mocks
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: dalli
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 2.6.4
104
+ - - '>='
105
+ - !ruby/object:Gem::Version
106
+ version: 2.6.4
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ~>
112
+ - !ruby/object:Gem::Version
113
+ version: 2.6.4
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: 2.6.4
117
+ - !ruby/object:Gem::Dependency
118
+ name: json
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ~>
122
+ - !ruby/object:Gem::Version
123
+ version: 1.8.0
124
+ - - '>='
125
+ - !ruby/object:Gem::Version
126
+ version: 1.8.0
127
+ type: :runtime
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 1.8.0
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: 1.8.0
137
+ - !ruby/object:Gem::Dependency
138
+ name: mysql2
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ~>
142
+ - !ruby/object:Gem::Version
143
+ version: 0.3.13
144
+ - - '>='
145
+ - !ruby/object:Gem::Version
146
+ version: 0.3.13
147
+ type: :runtime
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ~>
152
+ - !ruby/object:Gem::Version
153
+ version: 0.3.13
154
+ - - '>='
155
+ - !ruby/object:Gem::Version
156
+ version: 0.3.13
157
+ - !ruby/object:Gem::Dependency
158
+ name: sinatra
159
+ requirement: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ~>
162
+ - !ruby/object:Gem::Version
163
+ version: 1.4.3
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: 1.4.3
167
+ type: :runtime
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 1.4.3
174
+ - - '>='
175
+ - !ruby/object:Gem::Version
176
+ version: 1.4.3
177
+ - !ruby/object:Gem::Dependency
178
+ name: datamapper
179
+ requirement: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ~>
182
+ - !ruby/object:Gem::Version
183
+ version: 1.2.0
184
+ - - '>='
185
+ - !ruby/object:Gem::Version
186
+ version: 1.2.0
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ~>
192
+ - !ruby/object:Gem::Version
193
+ version: 1.2.0
194
+ - - '>='
195
+ - !ruby/object:Gem::Version
196
+ version: 1.2.0
197
+ description: A set of classes to simplify JSON APIs
198
+ email: tomhughcully@gmail.com
199
+ executables: []
200
+ extensions: []
201
+ extra_rdoc_files: []
202
+ files:
203
+ - lib/json-crud-api/api.rb
204
+ - lib/json-crud-api/auth_client.rb
205
+ - lib/json-crud-api/crud.rb
206
+ - lib/json-crud-api/service.rb
207
+ - lib/json-crud-api.rb
208
+ - spec/helper.rb
209
+ - spec/helpers_spec.rb
210
+ - spec/unit/auth_client_spec.rb
211
+ - spec/unit/service_spec.rb
212
+ homepage: http://rubygems.org/gems/json-crud-api
213
+ licenses:
214
+ - MIT
215
+ metadata: {}
216
+ post_install_message:
217
+ rdoc_options: []
218
+ require_paths:
219
+ - lib
220
+ required_ruby_version: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - '>='
223
+ - !ruby/object:Gem::Version
224
+ version: 2.0.0
225
+ required_rubygems_version: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - '>='
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ requirements: []
231
+ rubyforge_project:
232
+ rubygems_version: 2.1.11
233
+ signing_key:
234
+ specification_version: 4
235
+ summary: Sinatra JSON API Framework Classes
236
+ test_files:
237
+ - spec/helper.rb
238
+ - spec/helpers_spec.rb
239
+ - spec/unit/auth_client_spec.rb
240
+ - spec/unit/service_spec.rb