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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed95b4a06145c24432bfb2bdaf1e2a5a4547e40a
4
- data.tar.gz: 05feecbc8562dcefd7eaccba2e8feb3392e8da3b
3
+ metadata.gz: 2f1a54f5054c7cd360df54de7fc0ef1a5bcdd361
4
+ data.tar.gz: 169102147cefde0172ba4194b60fe7db2c8b5a55
5
5
  SHA512:
6
- metadata.gz: 8ccc7aed599ecac8101a0e51685ff1c3bdfa495635810cba1e00732c5d5322e7e55c032840689bbb22f6f2c5c307e2a2bc402e296a3d0fe082a574da124dbd54
7
- data.tar.gz: 645dbd4386668dcb74ff5abdb09444d6e9faebfe403ae02a51233e86caeacdec4395379d7ee1a208634ad12f13de24bb14ac0e7bf759e2673f28fcb160848c3f
6
+ metadata.gz: c4db8ae05cbc09f9f844d983f8729ef95e4c85c67149ec83e2a1e5aea6e10112598532c6c5c88536cd37e2ac432c16d58f19ac067a9590ded6b1add8265c44fc
7
+ data.tar.gz: 6c1946756ba824933066c0babe246d220e602ad914d0f7abbfc09b10ecc5818f9676892d4679d2acf04cd29bb654333de724a14ee090f398ac89c5ad6a074609
data/Rakefile CHANGED
@@ -10,4 +10,4 @@ require 'coveralls/rake/task'
10
10
  Coveralls::RakeTask.new
11
11
  task :test_with_coveralls => [:spec, :features, 'coveralls:push']
12
12
 
13
- task :default => :spec
13
+ task :default => :spec
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Yodatra
2
- VERSION = '0.3.0'
2
+ VERSION = '0.3.1'
3
3
  end
@@ -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)
@@ -0,0 +1,2 @@
1
+ class ArModelsController < Yodatra::ModelsController
2
+ end
data/spec/data/model.rb CHANGED
@@ -47,4 +47,4 @@ class Model
47
47
  Model
48
48
  end
49
49
  def errors; []; end
50
- end
50
+ end
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.should be_ok
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.should be_ok
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.should_not be_ok
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.should be_ok
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.should_not be_ok
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.should be_ok
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.should_not be_ok
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.should_not be_ok
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.should_not be_ok
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.should be_ok
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.should_not be_ok
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.should_not be_ok
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.should be_ok
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.should_not be_ok
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.should_not be_ok
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.should_not be_ok
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.should_not be_ok
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.should_not be_ok
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
- end
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.0
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-05-25 00:00:00.000000000 Z
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