rice_cooker 0.1.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.
@@ -0,0 +1,264 @@
1
+ require 'pry'
2
+ require 'rice_cooker'
3
+ require 'active_record'
4
+ require 'spec_helper'
5
+
6
+ RSpec.describe RiceCooker::Filter do
7
+ include RiceCooker::Helpers
8
+
9
+ # class User < ActiveRecord::Base; end
10
+
11
+ before do
12
+ @collection_class = User
13
+ @allowed_params = filterable_fields_for(@collection_class)
14
+ @collection = @collection_class.all
15
+
16
+ @test_filter = {
17
+ with_the_letter: { proc: -> (value) { where('first_name ILIKE ?', value.map { |e| "%#{e}%" }) } },
18
+ without_the_letter: { proc: -> (value) { where.not('first_name ILIKE ?', value.map { |e| "%#{e}%" }) } }
19
+ }
20
+
21
+ @proc = -> (value) { value }
22
+ @all = -> (_value) { [1, 2, 3] }
23
+ end
24
+
25
+ describe 'Filter params must be okay' do
26
+ it 'Null filtering' do
27
+ # Default null filtering
28
+ filtering_params = parse_filtering_param('', @allowed_params)
29
+ expect(filtering_params).to be_eql({})
30
+ end
31
+
32
+ it 'Default filtering' do
33
+ params = {
34
+ login: 'aaubin'
35
+ }
36
+
37
+ filtering_params = parse_filtering_param(params, @allowed_params)
38
+ expect(filtering_params).to be_eql(login: ['aaubin'])
39
+ end
40
+
41
+ it 'Double filtering' do
42
+ params = {
43
+ login: 'aaubin,qbollach'
44
+ }
45
+
46
+ filtering_params = parse_filtering_param(params, @allowed_params)
47
+ expect(filtering_params).to be_eql(login: %w(aaubin qbollach))
48
+ end
49
+
50
+ it 'Multiple filtering' do
51
+ params = {
52
+ login: 'aaubin,qbollach,andre',
53
+ id: '74,75,76'
54
+ }
55
+
56
+ filtering_params = parse_filtering_param(params, @allowed_params)
57
+ expect(filtering_params).to be_eql(login: %w(aaubin qbollach andre),
58
+ id: %w(74 75 76))
59
+ end
60
+
61
+ it 'invalid args' do
62
+ # invalid args
63
+
64
+ params = {
65
+ wtf: 'aaubin,qbollach,andre',
66
+ id: '74,75,76'
67
+ }
68
+
69
+ expect { parse_filtering_param(params, @allowed_params) }.to raise_error(RiceCooker::InvalidFilterException)
70
+ end
71
+ end
72
+
73
+ describe 'Must apply filter to given collection' do
74
+ it 'Default null filtering' do
75
+ filtered_collection = apply_filter_to_collection(@collection, {})
76
+ # puts filtered_collection.to_sql
77
+ expect(filtered_collection.to_sql).to match(/^((?!WHERE).)*$/)
78
+ end
79
+
80
+ it 'Default filtering' do
81
+ filtered_collection = apply_filter_to_collection(@collection, login: ['aaubin'])
82
+ # puts filtered_collection.to_sql
83
+ expect(filtered_collection.to_sql).to match(/WHERE/)
84
+ expect(filtered_collection.to_sql).to match(/"login" = 'aaubin'/)
85
+ end
86
+
87
+ it 'Double filtering' do
88
+ # Desc filtering
89
+ filtered_collection = apply_filter_to_collection(@collection, login: %w(aaubin qbollach))
90
+ # puts filtered_collection.to_sql
91
+ expect(filtered_collection.to_sql).to match(/WHERE/)
92
+ expect(filtered_collection.to_sql).to match(/"login" IN \('aaubin', 'qbollach'\)/)
93
+ end
94
+
95
+ it 'Multiple filtering' do
96
+ # Desc filtering
97
+ filtered_collection = apply_filter_to_collection(@collection, login: %w(aaubin qbollach andre),
98
+ id: %w(74 75 76))
99
+ # puts filtered_collection.to_sql
100
+ expect(filtered_collection.to_sql).to match(/WHERE/)
101
+ expect(filtered_collection.to_sql).to match(/"login" IN \('aaubin', 'qbollach', 'andre'\)/)
102
+ expect(filtered_collection.to_sql).to match(/AND/)
103
+ end
104
+ end
105
+
106
+ describe 'Must apply custom filters to given collection' do
107
+ it 'Default null filtering' do
108
+ filtered_collection = apply_filter_to_collection(@collection, {}, @test_filter)
109
+ # puts filtered_collection.to_sql
110
+ expect(filtered_collection.to_sql).to match(/^((?!WHERE).)*$/)
111
+ end
112
+
113
+ it 'Default filtering' do
114
+ filtered_collection = apply_filter_to_collection(@collection, { with_the_letter: ['a'] }, @test_filter)
115
+ # puts filtered_collection.to_sql
116
+ expect(filtered_collection.to_sql).to match(/WHERE/)
117
+ expect(filtered_collection.to_sql).to match(/ILIKE/)
118
+ end
119
+
120
+ it 'Double filtering' do
121
+ # Desc filtering
122
+ filtered_collection = apply_filter_to_collection(@collection, { with_the_letter: ['a'], without_the_letter: ['l'] }, @test_filter)
123
+ # puts filtered_collection.to_sql
124
+ expect(filtered_collection.to_sql).to match(/WHERE/)
125
+ expect(filtered_collection.to_sql).to match(/first_name ILIKE '%a%'/)
126
+ expect(filtered_collection.to_sql).to match(/NOT \(first_name ILIKE '%l%'\)/)
127
+ end
128
+
129
+ it 'Multiple filtering' do
130
+ # Desc filtering
131
+ filtered_collection = apply_filter_to_collection(@collection, login: %w(aaubin qbollach andre),
132
+ id: %w(74 75 76))
133
+ # puts filtered_collection.to_sql
134
+ expect(filtered_collection.to_sql).to match(/WHERE/)
135
+ expect(filtered_collection.to_sql).to match(/"login" IN \('aaubin', 'qbollach', 'andre'\)/)
136
+ expect(filtered_collection.to_sql).to match(/AND/)
137
+ end
138
+
139
+ it 'invalid args' do
140
+ # invalid args
141
+ expect do
142
+ apply_filter_to_collection(
143
+ @collection,
144
+ { sorted: %w(true baguette) },
145
+ format_additional_param(sorted: [-> (v) { v }, %w(true false maybe)])
146
+ )
147
+ end.to raise_error(RiceCooker::InvalidFilterValueException)
148
+ end
149
+ end
150
+
151
+ describe 'Additional params must be correctly formated' do
152
+ it 'No additional params' do
153
+ formated = format_additional_param({})
154
+ expect(formated).to be_eql({})
155
+ end
156
+
157
+ it 'Already correctly formatted additional params' do
158
+ p = { filter: {
159
+ proc: @proc,
160
+ all: [1, 2, 3],
161
+ description: 'A good filter'
162
+ } }
163
+ formated = format_additional_param(p)
164
+ expect(formated).to be_eql(p)
165
+ end
166
+
167
+ it 'Missing description additional params' do
168
+ p = { filter: {
169
+ proc: @proc,
170
+ all: [1, 2, 3]
171
+ } }
172
+ expected = { filter: {
173
+ proc: @proc,
174
+ all: [1, 2, 3],
175
+ description: ''
176
+ } }
177
+ formated = format_additional_param(p)
178
+ expect(formated).to be_eql(expected)
179
+ end
180
+
181
+ it 'Only proc additional params' do
182
+ p = { filter: @proc }
183
+ expected = { filter: {
184
+ proc: @proc,
185
+ all: [],
186
+ description: ''
187
+ } }
188
+ formated = format_additional_param(p)
189
+ expect(formated).to be_eql(expected)
190
+ end
191
+
192
+ it 'Array with proc and all additional params' do
193
+ p = { filter: [@proc, @all] }
194
+ expected = { filter: {
195
+ proc: @proc,
196
+ all: @all,
197
+ description: ''
198
+ } }
199
+ formated = format_additional_param(p)
200
+ expect(formated).to be_eql(expected)
201
+ end
202
+
203
+ it 'Multiple, std + Array with proc and all additional params' do
204
+ p = {
205
+ tata: @proc,
206
+ toto: { proc: @proc, all: [1, 2] },
207
+ filter: [@proc, @all],
208
+ tutu: { proc: @proc, description: 'Buuuuh' }
209
+ }
210
+ expected = {
211
+ tata: {
212
+ proc: @proc,
213
+ all: [],
214
+ description: ''
215
+ },
216
+ toto: {
217
+ proc: @proc,
218
+ all: [1, 2],
219
+ description: ''
220
+ },
221
+ filter: {
222
+ proc: @proc,
223
+ all: @all,
224
+ description: ''
225
+ },
226
+ tutu: {
227
+ proc: @proc,
228
+ all: [],
229
+ description: 'Buuuuh'
230
+ }
231
+ }
232
+ formated = format_additional_param(p)
233
+ expect(formated).to be_eql(expected)
234
+ end
235
+ end
236
+ end
237
+
238
+ RSpec.describe UsersController, type: :controller do
239
+ include RiceCooker::Helpers
240
+
241
+ before { request.host = 'example.org' }
242
+
243
+ describe 'GET #index' do
244
+ it 'without filter parameter' do
245
+ process :index, method: :get, params: { filter: '', format: :json }
246
+ expect(response.body).to eq(User.all.order(id: :desc).to_json)
247
+ end
248
+
249
+ it 'with simple filter parameter' do
250
+ process :index, method: :get, params: { filter: { login: 'aaubin' }, format: :json }
251
+ expect(response.body).to eq(User.where(login: 'aaubin').order(id: :desc).to_json)
252
+ end
253
+
254
+ it 'with double filter parameter' do
255
+ process :index, method: :get, params: { filter: { login: 'aaubin,qbollach' }, format: :json }
256
+ expect(response.body).to eq(User.where(login: %w(aaubin qbollach)).order(id: :desc).to_json)
257
+ end
258
+
259
+ it 'with double and multiple filter parameter' do
260
+ process :index, method: :get, params: { filter: { login: 'aaubin,qbollach', email: 'tata' }, format: :json }
261
+ expect(response.body).to eq(User.where(login: %w(aaubin qbollach), email: 'tata').order(id: :desc).to_json)
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,2 @@
1
+ ActiveRecord::Base.configurations = { 'test' => { adapter: 'sqlite3', database: ':memory:' } }
2
+ ActiveRecord::Base.establish_connection :test
@@ -0,0 +1,58 @@
1
+ require 'action_controller'
2
+ require 'ostruct'
3
+ require 'has_scope'
4
+
5
+ module Rails
6
+ def self.application
7
+ @application ||= begin
8
+ routes = ActionDispatch::Routing::RouteSet.new
9
+ OpenStruct.new(routes: routes, env_config: {})
10
+ end
11
+ end
12
+ end
13
+
14
+ module ControllerExampleGroup
15
+ def self.included(base)
16
+ base.extend ClassMethods
17
+ base.send(:include, ActionController::TestCase::Behavior)
18
+
19
+ base.prepend_before do
20
+ @routes = Rails.application.routes
21
+ @controller = described_class.new
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def setup(*methods)
27
+ methods.each do |method|
28
+ if method.to_s =~ /^setup_(fixtures|controller_request_and_response)$/
29
+ prepend_before { send method }
30
+ else
31
+ before { send method }
32
+ end
33
+ end
34
+ end
35
+
36
+ def teardown(*methods)
37
+ methods.each { |method| after { send method } }
38
+ end
39
+ end
40
+ end
41
+
42
+ Rails.application.routes.draw do
43
+ resources :users, only: [:index]
44
+ end
45
+
46
+ class UsersController < ActionController::Base
47
+ include Rails.application.routes.url_helpers
48
+
49
+ sorted
50
+ filtered
51
+ ranged
52
+
53
+ def index
54
+ @users = apply_scopes(User).all
55
+ # p @users.to_sql
56
+ render json: @users
57
+ end
58
+ end
@@ -0,0 +1,4 @@
1
+
2
+ require File.join(File.dirname(__FILE__), *%w(config))
3
+ require File.join(File.dirname(__FILE__), *%w(models user))
4
+ require File.join(File.dirname(__FILE__), *%w(controllers users_controller))
@@ -0,0 +1,31 @@
1
+ class User < ActiveRecord::Base
2
+ has_many :comments
3
+
4
+ def self.rangeable_fields
5
+ [:id, :login, :email]
6
+ end
7
+ end
8
+
9
+ class Comment < ActiveRecord::Base
10
+ belongs_to :user
11
+ end
12
+
13
+ # migrations
14
+ class CreateAllTables < ActiveRecord::Migration
15
+ def self.up
16
+ create_table(:users) { |t| t.string :login; t.string :email; t.timestamps }
17
+ create_table(:comments) { |t| t.integer :user_id; t.string :content; t.timestamps }
18
+ end
19
+ end
20
+
21
+ ActiveRecord::Migration.verbose = true
22
+ CreateAllTables.up
23
+
24
+ [
25
+ { login: 'andre', email: 'tata' },
26
+ { login: 'mathieu', email: 'toto' },
27
+ { login: 'bobol', email: 'titi' },
28
+ { login: 'fred', email: 'gratti' },
29
+ { login: 'jeanne', email: 'zapata' },
30
+ { login: 'angie', email: 'tutu' }
31
+ ].each { |u| User.create!(u) }
@@ -0,0 +1,265 @@
1
+ require 'rice_cooker'
2
+ require 'active_record'
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RiceCooker::Range do
6
+ include RiceCooker::Helpers
7
+
8
+ before do
9
+ @collection_class = User
10
+ @allowed_params = rangeable_fields_for(@collection_class)
11
+ @collection = @collection_class.all
12
+
13
+ @test_range = {
14
+ between_the_letter: { proc: -> (start_field, end_field) { where('first_name BETWEEN ? AND ?', start_field, end_field) } },
15
+ not_between_the_letter: { proc: -> (start_field, end_field) { where('first_name NOT BETWEEN ? AND ?', start_field, end_field) } }
16
+ }
17
+
18
+ @proc = -> (value) { value }
19
+ @all = -> (_value) { [1, 2, 3] }
20
+ end
21
+
22
+ describe 'Range params must be okay' do
23
+ it 'Null ranged' do
24
+ # Default null ranged
25
+ ranged_params = parse_ranged_param('', @allowed_params)
26
+ expect(ranged_params).to be_eql({})
27
+ end
28
+
29
+ it 'Default ranged' do
30
+ params = {
31
+ id: '1,5'
32
+ }
33
+ ranged_params = parse_ranged_param(params, @allowed_params)
34
+ expect(ranged_params).to be_eql(id: %w(1 5))
35
+ end
36
+
37
+ it 'Multiple ranged' do
38
+ params = {
39
+ login: 'aaubin,bobol',
40
+ id: '4,5'
41
+ }
42
+ ranged_params = parse_ranged_param(params, @allowed_params)
43
+ expect(ranged_params).to be_eql(login: %w(aaubin bobol),
44
+ id: %w(4 5))
45
+ end
46
+
47
+ it 'too many args' do
48
+ # invalid args
49
+
50
+ params = {
51
+ wtf: 'aaubin,qbollach,andre',
52
+ id: '74,75,76'
53
+ }
54
+
55
+ expect { parse_ranged_param(params, @allowed_params) }.to raise_error(RiceCooker::InvalidRangeException)
56
+ end
57
+
58
+ it 'too few args' do
59
+ # invalid args
60
+
61
+ params = {
62
+ wtf: 'aaubin',
63
+ id: '74'
64
+ }
65
+
66
+ expect { parse_ranged_param(params, @allowed_params) }.to raise_error(RiceCooker::InvalidRangeException)
67
+ end
68
+ end
69
+
70
+ describe 'Must apply range to given collection' do
71
+ it 'Default null ranged' do
72
+ ranged_collection = apply_range_to_collection(@collection, {})
73
+ expect(ranged_collection.to_sql).to match(/^((?!WHERE).)*$/)
74
+ end
75
+
76
+ it 'Default ranged' do
77
+ ranged_collection = apply_range_to_collection(@collection, login: %w(aaubin qbollach))
78
+ expect(ranged_collection.to_sql).to match(/BETWEEN/)
79
+ expect(ranged_collection.to_sql).to match(/'aaubin'/)
80
+ end
81
+
82
+ it 'Double ranged' do
83
+ # Desc ranged
84
+ ranged_collection = apply_range_to_collection(@collection, login: %w(aaubin qbollach), id: [1, 2])
85
+ expect(ranged_collection.to_sql).to match(/BETWEEN/)
86
+ expect(ranged_collection.to_sql).to match(/'aaubin'/)
87
+ end
88
+
89
+ it 'Invalid ranged' do
90
+ # Desc ranged
91
+ expect do
92
+ apply_range_to_collection(@collection, login: %w(aaubin qbollach andre),
93
+ id: ['74'])
94
+ end.to raise_error(RiceCooker::InvalidRangeValueException)
95
+ end
96
+ end
97
+
98
+ describe 'Must apply custom ranges to given collection' do
99
+
100
+ it 'Default null ranged' do
101
+ ranged_collection = apply_range_to_collection(@collection, {}, @test_range)
102
+ expect(ranged_collection.to_sql).to match(/^((?!WHERE).)*$/)
103
+ end
104
+
105
+ it 'Default ranged' do
106
+ ranged_collection = apply_range_to_collection(@collection, {between_the_letter: ['a', 'b']}, @test_range)
107
+ expect(ranged_collection.to_sql).to match(/WHERE/)
108
+ expect(ranged_collection.to_sql).to match(/BETWEEN/)
109
+ end
110
+
111
+ it 'Double ranged' do
112
+ # Desc ranged
113
+ ranged_collection = apply_range_to_collection(@collection, {
114
+ between_the_letter: ['a', 'b'],
115
+ not_between_the_letter: ['x', 'z']
116
+ }, @test_range)
117
+ expect(ranged_collection.to_sql).to match(/WHERE/)
118
+ expect(ranged_collection.to_sql).to match(/first_name BETWEEN 'a' AND 'b'/)
119
+ expect(ranged_collection.to_sql).to match(/AND \(first_name NOT BETWEEN 'x' AND 'z'\)/)
120
+ end
121
+
122
+ it 'Multiple ranged' do
123
+ # Desc ranged
124
+ ranged_collection = apply_range_to_collection(@collection, {
125
+ login: ['aaubin', 'qbollach'],
126
+ id: ['74', '76']
127
+ })
128
+ expect(ranged_collection.to_sql).to match(/WHERE/)
129
+ expect(ranged_collection.to_sql).to match(/BETWEEN 'aaubin' AND 'qbollach'/)
130
+ expect(ranged_collection.to_sql).to match(/\) AND \(/)
131
+ expect(ranged_collection.to_sql).to match(/BETWEEN 74 AND 76/)
132
+ end
133
+
134
+ it 'invalid args' do
135
+ # invalid args
136
+ expect do
137
+ apply_range_to_collection(
138
+ @collection,
139
+ {sorted: ['true', 'baguette']},
140
+ format_additional_param({sorted: [-> (v, w) { v }, ['true', 'false', 'maybe']]}, 'ranged')
141
+ )
142
+ end.to raise_error(RiceCooker::InvalidRangeValueException)
143
+ end
144
+ end
145
+
146
+ describe 'Additional params must be correctly formated' do
147
+
148
+ it 'No additional params' do
149
+ formated = format_additional_param({}, 'ranged')
150
+ expect(formated).to be_eql({})
151
+ end
152
+
153
+ it 'Already correctly formatted additional params' do
154
+ p = {range: {
155
+ proc: @proc,
156
+ all: [1, 2, 3],
157
+ description: 'A good filter'
158
+ }}
159
+ formated = format_additional_param(p, 'ranged')
160
+ expect(formated).to be_eql(p)
161
+ end
162
+
163
+ it 'Missing description additional params' do
164
+ p = {filter: {
165
+ proc: @proc,
166
+ all: [1, 2, 3]
167
+ }}
168
+ expected = {filter: {
169
+ proc: @proc,
170
+ all: [1, 2, 3],
171
+ description: ''
172
+ }}
173
+ formated = format_additional_param(p, 'ranged')
174
+ expect(formated).to be_eql(expected)
175
+ end
176
+
177
+ it 'Only proc additional params' do
178
+
179
+ p = {filter: @proc}
180
+ expected = {filter: {
181
+ proc: @proc,
182
+ all: [],
183
+ description: ''
184
+ }}
185
+ formated = format_additional_param(p, 'ranged')
186
+ expect(formated).to be_eql(expected)
187
+ end
188
+
189
+ it 'Array with proc and all additional params' do
190
+
191
+ p = {filter: [@proc, @all]}
192
+ expected = {filter: {
193
+ proc: @proc,
194
+ all: @all,
195
+ description: ''
196
+ }}
197
+ formated = format_additional_param(p, 'ranged')
198
+ expect(formated).to be_eql(expected)
199
+ end
200
+
201
+ it 'Multiple, std + Array with proc and all additional params' do
202
+
203
+ p = {
204
+ tata: @proc,
205
+ toto: {proc: @proc, all: [1, 2]},
206
+ filter: [@proc, @all],
207
+ tutu: {proc: @proc, description: 'Buuuuh'}
208
+ }
209
+ expected = {
210
+ tata: {
211
+ proc: @proc,
212
+ all: [],
213
+ description: ''
214
+ },
215
+ toto: {
216
+ proc: @proc,
217
+ all: [1, 2],
218
+ description: ''
219
+ },
220
+ filter: {
221
+ proc: @proc,
222
+ all: @all,
223
+ description: ''
224
+ },
225
+ tutu: {
226
+ proc: @proc,
227
+ all: [],
228
+ description: 'Buuuuh'
229
+ }
230
+ }
231
+ formated = format_additional_param(p, 'ranged')
232
+ expect(formated).to be_eql(expected)
233
+ end
234
+
235
+ end
236
+ end
237
+
238
+ RSpec.describe UsersController, type: :controller do
239
+ include RiceCooker::Helpers
240
+
241
+ before { request.host = 'example.org' }
242
+
243
+ describe 'GET #index' do
244
+ it 'without range parameter' do
245
+ process :index, method: :get, params: { range: '', format: :json }
246
+ expect(response.body).to eq(User.all.order(id: :desc).to_json)
247
+ end
248
+
249
+ it 'with simple range parameter' do
250
+ process :index, method: :get, params: { range: { login: 'aaubin,qbollach' }, format: :json }
251
+ expect(response.body).to eq(User.where(login: 'aaubin'..'qbollach').order(id: :desc).to_json)
252
+ end
253
+
254
+ it 'with multiple range parameter' do
255
+ process :index, method: :get, params: { range: { login: 'aaubin,qbollach', id: '1,5' }, format: :json }
256
+ expect(response.body).to eq(User.where(login: 'aaubin'..'qbollach', id: 1..5).order(id: :desc).to_json)
257
+ end
258
+
259
+ it 'with invalid range parameter' do
260
+ expect do
261
+ process :index, method: :get, params: { range: { created_at: '2016-02-04,qbollach', login: '^,&' }, format: :json }
262
+ end.to raise_error(RiceCooker::InvalidRangeException)
263
+ end
264
+ end
265
+ end