yodatra 0.3.0 → 0.3.1
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.
- 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
|