yodatra 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/lib/yodatra/models_controller.rb +42 -0
- data/lib/yodatra/version.rb +1 -1
- data/spec/active_record/connection_adapters/fake_adapter.rb +43 -0
- data/spec/data/ar_model.rb +7 -0
- data/spec/data/ar_models_controller.rb +2 -0
- data/spec/data/model.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/models_controller_spec.rb +58 -19
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f1a54f5054c7cd360df54de7fc0ef1a5bcdd361
|
4
|
+
data.tar.gz: 169102147cefde0172ba4194b60fe7db2c8b5a55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4db8ae05cbc09f9f844d983f8729ef95e4c85c67149ec83e2a1e5aea6e10112598532c6c5c88536cd37e2ac432c16d58f19ac067a9590ded6b1add8265c44fc
|
7
|
+
data.tar.gz: 6c1946756ba824933066c0babe246d220e602ad914d0f7abbfc09b10ecc5818f9676892d4679d2acf04cd29bb654333de724a14ee090f398ac89c5ad6a074609
|
data/Rakefile
CHANGED
@@ -23,6 +23,12 @@ module Yodatra
|
|
23
23
|
# If your model is referenced by another model, nested routes are also created for you. And you don't need to worry about the references/joins, they are done automaticly!
|
24
24
|
# For example, imagine a <b>Team</b> model that has many <b>User</b>s, the following routes will be exposed:
|
25
25
|
# GET /team/:team_id/users, GET /team/:team_id/users/:id, POST /team/:team_id/users, PUT /team/:team_id/users/:id and DESTROY /team/:team_id/users/:id
|
26
|
+
#
|
27
|
+
# _Note_: You can disable any of these five actions by using the `#disable` class method
|
28
|
+
# and giving in parameters the list of actions you want to disable
|
29
|
+
# e.g. `disable :read, :read_all, :create, :update, :delete`
|
30
|
+
#
|
31
|
+
# _Note2_: You can enable a special "search" action by using the `#enable_search_on` class method
|
26
32
|
class ModelsController < Sinatra::Base
|
27
33
|
|
28
34
|
before do
|
@@ -37,6 +43,10 @@ module Yodatra
|
|
37
43
|
ALL_ROUTE =
|
38
44
|
%r{\A/([\w]+?)(?:/([0-9]+)/([\w]+?)){0,1}\Z}
|
39
45
|
|
46
|
+
# Search route
|
47
|
+
SEARCH_ROUTE =
|
48
|
+
%r{\A/([\w]+?)(?:/([0-9]+)/([\w]+?)){0,1}/search\Z}
|
49
|
+
|
40
50
|
READ_ALL = :read_all
|
41
51
|
get ALL_ROUTE do
|
42
52
|
retrieve_resources READ_ALL do |resource|
|
@@ -118,6 +128,10 @@ module Yodatra
|
|
118
128
|
self.name.split('::').last.gsub(/sController/, '')
|
119
129
|
end
|
120
130
|
|
131
|
+
def model
|
132
|
+
model_name.constantize
|
133
|
+
end
|
134
|
+
|
121
135
|
# This helper gives the ability to disable default root by specifying
|
122
136
|
# a list of routes to disable.
|
123
137
|
# @param *opts list of routes to disable (e.g. :create, :destroy)
|
@@ -128,6 +142,30 @@ module Yodatra
|
|
128
142
|
define_method method, Proc.new {|| true}
|
129
143
|
end
|
130
144
|
end
|
145
|
+
|
146
|
+
def enable_search_on(*attributes)
|
147
|
+
self.instance_eval do
|
148
|
+
get SEARCH_ROUTE do
|
149
|
+
retrieve_resources '' do |resource|
|
150
|
+
|
151
|
+
pass if !involved? || params[:q].blank? || params[:q].size > 100
|
152
|
+
|
153
|
+
terms = params[:q].split(/[\+ ]/)
|
154
|
+
search_terms = []
|
155
|
+
|
156
|
+
# Seperate terms to match
|
157
|
+
terms.each do |term|
|
158
|
+
attributes.each do |attr|
|
159
|
+
search_terms << resource.arel_table[attr.to_sym].matches("%#{term}%")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
resource.where(search_terms.reduce(:or)).limit(100).
|
164
|
+
flatten.as_json(read_scope).to_json
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
131
169
|
end
|
132
170
|
|
133
171
|
private
|
@@ -181,6 +219,10 @@ module Yodatra
|
|
181
219
|
self.class.model_name
|
182
220
|
end
|
183
221
|
|
222
|
+
def model
|
223
|
+
self.class.model
|
224
|
+
end
|
225
|
+
|
184
226
|
def disabled? key
|
185
227
|
method = ((nested? ? 'nested_' : '')+"#{key}_disabled?").to_sym
|
186
228
|
self.class.method_defined?(method) && self.send(method)
|
data/lib/yodatra/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "active_record"
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Base
|
5
|
+
def self.fake_connection(config)
|
6
|
+
ConnectionAdapters::FakeAdapter.new nil, logger
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ConnectionAdapters
|
11
|
+
class FakeAdapter < AbstractAdapter
|
12
|
+
attr_accessor :tables, :primary_keys
|
13
|
+
|
14
|
+
@columns = Hash.new { |h,k| h[k] = [] }
|
15
|
+
class << self
|
16
|
+
attr_reader :columns
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(connection, logger)
|
20
|
+
super
|
21
|
+
@tables = []
|
22
|
+
@primary_keys = {}
|
23
|
+
@columns = self.class.columns
|
24
|
+
end
|
25
|
+
|
26
|
+
def primary_key(table)
|
27
|
+
@primary_keys[table]
|
28
|
+
end
|
29
|
+
|
30
|
+
def merge_column(table_name, name, sql_type = nil, options = {})
|
31
|
+
@columns[table_name] << ActiveRecord::ConnectionAdapters::Column.new(
|
32
|
+
name.to_s,
|
33
|
+
options[:default],
|
34
|
+
sql_type.to_s,
|
35
|
+
options[:null])
|
36
|
+
end
|
37
|
+
|
38
|
+
def columns(table_name, message)
|
39
|
+
@columns[table_name]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Mock model constructed for the tests
|
2
|
+
class ArModel < ActiveRecord::Base
|
3
|
+
end
|
4
|
+
|
5
|
+
ActiveRecord::Base.establish_connection(:adapter => 'fake')
|
6
|
+
ActiveRecord::Base.connection.merge_column('ar_models', :email, :string)
|
7
|
+
ActiveRecord::Base.connection.merge_column('ar_models', :name, :string)
|
data/spec/data/model.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -8,6 +8,7 @@ require 'rspec'
|
|
8
8
|
|
9
9
|
require File.expand_path '../../lib/yodatra.rb', __FILE__
|
10
10
|
require File.expand_path '../../lib/yodatra/models_controller.rb', __FILE__
|
11
|
+
require File.expand_path '../data/ar_models_controller.rb', __FILE__
|
11
12
|
|
12
13
|
module RSpecMixin
|
13
14
|
include Rack::Test::Methods
|
@@ -15,6 +16,7 @@ module RSpecMixin
|
|
15
16
|
Sinatra.new {
|
16
17
|
use Yodatra::Base
|
17
18
|
use Yodatra::ModelsController
|
19
|
+
use ArModelsController
|
18
20
|
}
|
19
21
|
end
|
20
22
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require File.expand_path '../../spec_helper.rb', __FILE__
|
2
|
+
require File.expand_path '../../active_record/connection_adapters/fake_adapter.rb', __FILE__
|
2
3
|
require File.expand_path '../../data/model.rb', __FILE__
|
4
|
+
require File.expand_path '../../data/ar_model.rb', __FILE__
|
3
5
|
|
4
6
|
describe 'Model controller' do
|
5
7
|
|
@@ -13,7 +15,7 @@ describe 'Model controller' do
|
|
13
15
|
it 'should have a GET all route' do
|
14
16
|
get '/models'
|
15
17
|
|
16
|
-
last_response.
|
18
|
+
expect(last_response).to be_ok
|
17
19
|
expect(last_response.body).to eq(Model::ALL.map{|e| {:data => e} }.to_json)
|
18
20
|
end
|
19
21
|
end
|
@@ -21,7 +23,7 @@ describe 'Model controller' do
|
|
21
23
|
it 'should have a GET all route' do
|
22
24
|
get '/models/1/models'
|
23
25
|
|
24
|
-
last_response.
|
26
|
+
expect(last_response).to be_ok
|
25
27
|
expect(last_response.body).to eq(Model::ALL.map{|e| {:data => e} }.to_json)
|
26
28
|
end
|
27
29
|
end
|
@@ -34,7 +36,7 @@ describe 'Model controller' do
|
|
34
36
|
it 'should fail with no route available' do
|
35
37
|
get '/models'
|
36
38
|
|
37
|
-
last_response.
|
39
|
+
expect(last_response).to_not be_ok
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
@@ -42,7 +44,7 @@ describe 'Model controller' do
|
|
42
44
|
it 'should have a GET one route' do
|
43
45
|
get '/models/2'
|
44
46
|
|
45
|
-
last_response.
|
47
|
+
expect(last_response).to be_ok
|
46
48
|
expect(last_response.body).to eq({ :data => 'c'}.to_json)
|
47
49
|
end
|
48
50
|
context 'forced GET one route disabled' do
|
@@ -54,7 +56,7 @@ describe 'Model controller' do
|
|
54
56
|
it 'should fail with no route available' do
|
55
57
|
get '/models/1'
|
56
58
|
|
57
|
-
last_response.
|
59
|
+
expect(last_response).to_not be_ok
|
58
60
|
end
|
59
61
|
end
|
60
62
|
end
|
@@ -65,7 +67,7 @@ describe 'Model controller' do
|
|
65
67
|
post '/models', {:data => 'd'}
|
66
68
|
}.to change(Model::ALL, :length).by(1)
|
67
69
|
|
68
|
-
last_response.
|
70
|
+
expect(last_response).to be_ok
|
69
71
|
end
|
70
72
|
end
|
71
73
|
context 'with incorrect params' do
|
@@ -74,7 +76,7 @@ describe 'Model controller' do
|
|
74
76
|
post '/models', {}
|
75
77
|
}.to change(Model::ALL, :length).by(0)
|
76
78
|
|
77
|
-
last_response.
|
79
|
+
expect(last_response).to_not be_ok
|
78
80
|
expect(last_response.body).to eq(@errors.to_json)
|
79
81
|
end
|
80
82
|
end
|
@@ -87,7 +89,7 @@ describe 'Model controller' do
|
|
87
89
|
it 'should fail with no route available' do
|
88
90
|
post '/models', {:data => 'd'}
|
89
91
|
|
90
|
-
last_response.
|
92
|
+
expect(last_response).to_not be_ok
|
91
93
|
end
|
92
94
|
end
|
93
95
|
end
|
@@ -98,7 +100,7 @@ describe 'Model controller' do
|
|
98
100
|
put '/models/21', {:data => 'e'}
|
99
101
|
}.to change(Model::ALL, :length).by(0)
|
100
102
|
|
101
|
-
last_response.
|
103
|
+
expect(last_response).to_not be_ok
|
102
104
|
expect(last_response.body).to eq(['record not found'].to_json)
|
103
105
|
end
|
104
106
|
end
|
@@ -109,7 +111,7 @@ describe 'Model controller' do
|
|
109
111
|
put '/models/2', {:data => 'e'}
|
110
112
|
}.to change(Model::ALL, :length).by(0)
|
111
113
|
|
112
|
-
last_response.
|
114
|
+
expect(last_response).to be_ok
|
113
115
|
expect(last_response.body).to eq({ :data => 'e'}.to_json)
|
114
116
|
expect(Model.find(2).to_json).to eq({ :data => 'e'}.to_json)
|
115
117
|
end
|
@@ -120,7 +122,7 @@ describe 'Model controller' do
|
|
120
122
|
put '/models/2', {:data => 321}
|
121
123
|
}.to change(Model::ALL, :length).by(0)
|
122
124
|
|
123
|
-
last_response.
|
125
|
+
expect(last_response).to_not be_ok
|
124
126
|
expect(last_response.body).to eq(@errors.to_json)
|
125
127
|
end
|
126
128
|
end
|
@@ -133,7 +135,7 @@ describe 'Model controller' do
|
|
133
135
|
it 'should fail with no route available' do
|
134
136
|
put '/models', {:data => 'd'}
|
135
137
|
|
136
|
-
last_response.
|
138
|
+
expect(last_response).to_not be_ok
|
137
139
|
end
|
138
140
|
end
|
139
141
|
end
|
@@ -145,7 +147,7 @@ describe 'Model controller' do
|
|
145
147
|
delete '/models/1'
|
146
148
|
}.to change(Model::ALL, :length).by(-1)
|
147
149
|
|
148
|
-
last_response.
|
150
|
+
expect(last_response).to be_ok
|
149
151
|
end
|
150
152
|
end
|
151
153
|
context 'targeting an existing instance but deletion fails' do
|
@@ -157,7 +159,7 @@ describe 'Model controller' do
|
|
157
159
|
delete '/models/1/models/1'
|
158
160
|
}.to change(Model::ALL, :length).by(0)
|
159
161
|
|
160
|
-
last_response.
|
162
|
+
expect(last_response).to_not be_ok
|
161
163
|
expect(last_response.body).to eq(@errors.to_json)
|
162
164
|
end
|
163
165
|
end
|
@@ -167,7 +169,7 @@ describe 'Model controller' do
|
|
167
169
|
delete '/models/6'
|
168
170
|
}.to change(Model::ALL, :length).by(0)
|
169
171
|
|
170
|
-
last_response.
|
172
|
+
expect(last_response).to_not be_ok
|
171
173
|
end
|
172
174
|
end
|
173
175
|
context 'when the deletion route is disabled' do
|
@@ -179,7 +181,7 @@ describe 'Model controller' do
|
|
179
181
|
it 'should fail with no route available' do
|
180
182
|
delete '/models/2'
|
181
183
|
|
182
|
-
last_response.
|
184
|
+
expect(last_response).to_not be_ok
|
183
185
|
end
|
184
186
|
end
|
185
187
|
end
|
@@ -196,7 +198,7 @@ describe 'Model controller' do
|
|
196
198
|
it 'fails with a record not found message' do
|
197
199
|
get '/modeels/1/models'
|
198
200
|
|
199
|
-
last_response.
|
201
|
+
expect(last_response).to_not be_ok
|
200
202
|
expect(last_response.body).to eq(['record not found'].to_json)
|
201
203
|
end
|
202
204
|
end
|
@@ -204,7 +206,7 @@ describe 'Model controller' do
|
|
204
206
|
it 'fails with a record not found message' do
|
205
207
|
get '/models/123/models'
|
206
208
|
|
207
|
-
last_response.
|
209
|
+
expect(last_response).to_not be_ok
|
208
210
|
expect(last_response.body).to eq(['record not found'].to_json)
|
209
211
|
end
|
210
212
|
end
|
@@ -212,4 +214,41 @@ describe 'Model controller' do
|
|
212
214
|
|
213
215
|
end
|
214
216
|
|
215
|
-
|
217
|
+
describe 'Model search' do
|
218
|
+
describe 'when the search feature is not enabled (default)' do
|
219
|
+
it 'fails with no route found' do
|
220
|
+
get '/ar_models/search?q=john4'
|
221
|
+
|
222
|
+
expect(last_response).to_not be_ok
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe 'when the search is enabled of the models controller' do
|
227
|
+
before do
|
228
|
+
class ArModelsController
|
229
|
+
enable_search_on :email
|
230
|
+
end
|
231
|
+
|
232
|
+
@search_term = 'john4'
|
233
|
+
|
234
|
+
expect_arel_matches = [ArModel.arel_table[:email].matches("%#{@search_term}%")]
|
235
|
+
expect(ArModel).to receive(:where).with(expect_arel_matches.reduce(:or)) do |arg|
|
236
|
+
ActiveRecord::Relation.new ArModel, 'ar_models'
|
237
|
+
end
|
238
|
+
allow_any_instance_of(ActiveRecord::Relation).to receive(:limit) do |arg|
|
239
|
+
FakeModel = Struct.new(:name, :email)
|
240
|
+
@fake_instance = FakeModel.new('john', 'john4@swcc.com')
|
241
|
+
[FakeModel.new('john', 'john4@swcc.com')]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should search in the list of available models' do
|
246
|
+
get "/ar_models/search?q=#{@search_term}"
|
247
|
+
|
248
|
+
expect(last_response).to be_ok
|
249
|
+
expect(last_response.body).to eq([@fake_instance.as_json({})].to_json)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yodatra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Bonaud
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -129,6 +129,9 @@ files:
|
|
129
129
|
- lib/yodatra/throttling.rb
|
130
130
|
- lib/yodatra/utils.rb
|
131
131
|
- lib/yodatra/version.rb
|
132
|
+
- spec/active_record/connection_adapters/fake_adapter.rb
|
133
|
+
- spec/data/ar_model.rb
|
134
|
+
- spec/data/ar_models_controller.rb
|
132
135
|
- spec/data/model.rb
|
133
136
|
- spec/spec_helper.rb
|
134
137
|
- spec/unit/models_controller_spec.rb
|