itrigga-param_fu 0.2.0 → 0.3.0

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -1,11 +1,11 @@
1
1
  module Trigga
2
2
  module ParamFu
3
3
  def self.included(base)
4
- base.extend(ClassMethods)
5
- base.send(:include, InstanceMethods)
4
+ base.extend(AllMethods)
5
+ base.send(:include, AllMethods)
6
6
  end
7
7
 
8
- module ClassMethods
8
+ module AllMethods
9
9
  def require_obj_or_id(opts, key)
10
10
  obj_or_id(opts,key)
11
11
  raise ArgumentError.new("#{key} or #{key_with_id(key)} are required") unless opts[key_with_id(key)]
@@ -47,24 +47,70 @@ module Trigga
47
47
  condition
48
48
  end
49
49
 
50
+
50
51
  # fallback when we don't have ActiveSupport's pluralize method available
51
52
  def to_plural(s)
52
53
  (s.match(/[aeiou]$/i) ? s + 's' : s + 'es' )
53
54
  end
54
- end
55
-
56
- module InstanceMethods
57
- def require_obj_or_id(opts,key)
58
- self.class.require_obj_or_id(opts, key)
59
- end
60
- def obj_or_id(opts, key)
61
- self.class.obj_or_id(opts, key)
55
+
56
+ # Give this a hash per model in the format:
57
+ # Model class => { param_name => { :operator=>(db operator - defaults to '='),
58
+ # => :field_name=>(field_name in the database - defaults to param_name)
59
+ # => }
60
+ # => }
61
+ # and it will return an ActiveRecord finder :conditions clause using any params which are present in the given :params
62
+ # e.g. if you have params = { :created_date_from=>'2011-10-09 00:01:02', :status=>'A', :click_url=>'items.html' }
63
+ # then calling:
64
+ #
65
+ # params_to_conditions( :params=>params, :models=>{
66
+ # Click => {:click_url=>{ :operator=>"like", :field_name=>'request_path' },
67
+ # :status => {},
68
+ # :http_referer=>{:operator=>'like'},
69
+ # :created_date_from => {:operator=> ">=", :field_name=>'timestamp'}
70
+ # }
71
+ # })
72
+ #
73
+ #
74
+ # will generate:
75
+ #
76
+ # [ 'clicks.request_path like (?) AND clicks.status = ? AND clicks.created_date_from >= ?', '%items.html%', 'A', '2011-10-09 00:01:02' ]
77
+ #
78
+ # It will ignore any criteria given which are NOT in the given :params hash
79
+ #
80
+ def params_to_conditions( opts={} )
81
+ require_param(opts, :params, :models)
82
+
83
+ conds = { :where=>[], :values=>[] }
84
+
85
+ puts "<br />opts = #{opts.inspect}<br />"
86
+ opts[:models].each { |model_class, field_defs|
87
+ puts "model_class = #{model_class.inspect}"
88
+ parsed_model_conditions = model_conditions( opts[:params], model_class, field_defs )
89
+ puts "parsed_model_conditions = #{parsed_model_conditions.inspect}"
90
+ conds[:where] += parsed_model_conditions[:where]
91
+ conds[:values] += parsed_model_conditions[:values]
92
+ }
93
+
94
+ [ conds[:where].join(" AND ") ] + conds[:values]
62
95
  end
63
- def require_param(opts, *keys)
64
- self.class.require_param(opts, *keys)
96
+
97
+ def model_conditions( opts, model_class, field_defs )
98
+ h = {:where=>[], :values=>[]}
99
+ field_defs.each{ |field_name, definition|
100
+ parsed_field_def = parse_field_definition( opts, model_class, field_name, definition )
101
+ h[:where] += parsed_field_def[:where]
102
+ h[:values] += parsed_field_def[:values]
103
+ }
104
+ h
65
105
  end
66
- def require_one_of( opts, *keys )
67
- self.class.require_one_of( opts, *keys )
106
+
107
+ def parse_field_definition( opts, model_class, param_name, definition )
108
+ c = {:where=>[], :values=>[]}
109
+ if opts[param_name]
110
+ c[:where] << "( #{model_class.table_name}.#{definition[:field_name] || param_name.to_s} #{definition[:operator] || '='} (?) )"
111
+ c[:values] << opts[param_name]
112
+ end
113
+ c
68
114
  end
69
115
  end
70
116
  end
@@ -145,21 +145,284 @@ describe ::Trigga::ParamFu do
145
145
  end
146
146
  end
147
147
  end
148
- end
149
-
150
- describe "instance_methods" do
151
- describe "require_obj_or_id" do
152
- it "should call the class method require_obj_or_id" do
153
- Tester.should_receive(:require_obj_or_id).with( {:key=>'val'}, :key )
154
- @t.require_obj_or_id( {:key=>'val'}, :key )
148
+
149
+ describe "parse_field_definition" do
150
+ before(:each) do
151
+ @params = { :param_1 => 'param_1 value' }
152
+ @model_class = mock("model class")
153
+ @model_class.stub!(:table_name).and_return( 'model_class_table_name' )
154
+ @param_name = :param_name
155
+ @definition = { :operator=>'=', :field_name=>'field_name'}
156
+ end
157
+
158
+ it "should return a Hash" do
159
+ Tester.parse_field_definition( @params, @model_class, @param_name, @definition ).should be_a(Hash)
160
+ end
161
+
162
+ describe "returned Hash" do
163
+ it "should have a key for :where" do
164
+ Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where].should_not be_nil
165
+ end
166
+ it "should have a key for :values" do
167
+ Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:values].should_not be_nil
168
+ end
169
+
170
+ context "when the given opts has a key matching the given param_name" do
171
+ before(:each) do
172
+ @param_name = :param_1
173
+ end
174
+
175
+ it "should add an element to the :where key" do
176
+ Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where].size.should == 1
177
+ end
178
+
179
+ describe "where clause" do
180
+ it "should be wrapped in brackets" do
181
+ Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where][0].should =~ /^\s*\(.*\)\s*$/
182
+ end
183
+
184
+ describe "inside the brackets" do
185
+ before(:each) do
186
+ @inside_the_brackets = Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where][0].gsub(/^\s*\((.*)\)\s*$/, '\1')
187
+ end
188
+
189
+ it "should start with the model_class's table name as a scope" do
190
+ @inside_the_brackets.should =~ /\s*model_class_table_name\..*/
191
+ end
192
+
193
+ context "When the definition has a :fieldname" do
194
+ before(:each) do
195
+ @definition[:field_name] = 'field_name'
196
+ @inside_the_brackets = Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where][0].gsub(/^\s*\((.*)\)\s*$/, '\1')
197
+ end
198
+
199
+ it "should use the :fieldname" do
200
+ @inside_the_brackets.should =~ /\s*[^\.]+\.field_name.*/
201
+ end
202
+ end
203
+
204
+ context "When the definition has no :fieldname" do
205
+ before(:each) do
206
+ @definition[:field_name] = nil
207
+ @inside_the_brackets = Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where][0].gsub(/^\s*\((.*)\)\s*$/, '\1')
208
+ end
209
+
210
+ it "should use the given param_name converted to a string as the field name" do
211
+ @inside_the_brackets.should =~ /\s*[^\.]+\.param_1.*/
212
+ end
213
+ end
214
+
215
+ context "When the definition has a :operator" do
216
+ before(:each) do
217
+ @definition[:operator] = 'my_operator'
218
+ @inside_the_brackets = Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where][0].gsub(/^\s*\((.*)\)\s*$/, '\1')
219
+ end
220
+
221
+ it "should use the :operator" do
222
+ @inside_the_brackets.should =~ /.*\bmy_operator\b.*/
223
+ end
224
+ end
225
+
226
+ context "When the definition has no :operator" do
227
+ before(:each) do
228
+ @definition[:operator] = nil
229
+ @inside_the_brackets = Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:where][0].gsub(/^\s*\((.*)\)\s*$/, '\1')
230
+ end
231
+
232
+ it "should use = as the :operator" do
233
+ @inside_the_brackets.should =~ /[^=]+\s*=\s*[^=]+/
234
+ end
235
+ end
236
+
237
+ it "should end with (?)" do
238
+ @inside_the_brackets.should =~ /\(\?\)\s*$/
239
+ end
240
+ end
241
+ end
242
+
243
+ it "should add the corresponding value to the :values key" do
244
+ Tester.parse_field_definition( @params, @model_class, @param_name, @definition )[:values].should == ['param_1 value']
245
+ end
246
+ end
155
247
  end
156
248
  end
157
249
 
158
- describe "obj_or_id" do
159
- it "should call the class method obj_or_id" do
160
- Tester.should_receive(:obj_or_id).with( {:key=>'val'}, :key )
161
- @t.obj_or_id( {:key=>'val'}, :key )
250
+ describe "model_conditions" do
251
+ before(:each) do
252
+ @params = { :param_1 => 'param_1 value' }
253
+ @model_class = mock("model class")
254
+ @mock_parsed_field_def_1 = {:where=>['where 1'], :values=>['values 1']}
255
+ @mock_parsed_field_def_2 = {:where=>['where 2'], :values=>['values 2']}
256
+ Tester.stub!(:parse_field_definition).and_return( @mock_parsed_field_def_1, @mock_parsed_field_def_2 )
257
+ @definitions = { :key=>'=', :field_name=>'field_name'}
258
+ end
259
+
260
+ it "should return a Hash" do
261
+ Tester.model_conditions( @params, @model_class, @definitions ).should be_a(Hash)
262
+ end
263
+
264
+ describe "returned Hash" do
265
+ it "should have a key for :where" do
266
+ Tester.model_conditions( @params, @model_class, @definitions )[:where].should_not be_nil
267
+ end
268
+ it "should have a key for :values" do
269
+ Tester.model_conditions( @params, @model_class, @definitions )[:values].should_not be_nil
270
+ end
271
+ end
272
+
273
+ context "for each given field_def" do
274
+ before(:each) do
275
+ @definitions = { :field_name_1 => (@mock_definition_1={}), :field_name_2 => (@mock_definition_2={}) }
276
+ end
277
+
278
+ it "should call parse_field_definition" do
279
+ Tester.should_receive(:parse_field_definition).twice.and_return( @mock_parsed_field_def_1, @mock_parsed_field_def_2 )
280
+ Tester.model_conditions( @params, @model_class, @definitions )
281
+ end
282
+
283
+ describe "parse_field_definition call params" do
284
+ it "should pass the given opts" do
285
+ Tester.should_receive(:parse_field_definition).with( @params, anything, anything, anything ).twice.and_return( @mock_parsed_field_def_1, @mock_parsed_field_def_2 )
286
+ Tester.model_conditions( @params, @model_class, @definitions )
287
+ end
288
+
289
+ it "should pass the given model class" do
290
+ Tester.should_receive(:parse_field_definition).with( anything, @model_class, anything, anything ).twice.and_return( @mock_parsed_field_def_1, @mock_parsed_field_def_2 )
291
+ Tester.model_conditions( @params, @model_class, @definitions )
292
+ end
293
+
294
+ it "should pass each field_name" do
295
+ Tester.should_receive(:parse_field_definition) do |args|
296
+ args[2].should == :field_name_1
297
+ @mock_parsed_field_def_1
298
+ end
299
+ Tester.should_receive(:parse_field_definition) do |args|
300
+ args[2].should == :field_name_2
301
+ @mock_parsed_field_def_2
302
+ end
303
+ Tester.model_conditions( @params, @model_class, @definitions )
304
+ end
305
+
306
+ it "should pass the field_name's definition" do
307
+ Tester.should_receive(:parse_field_definition) do |args|
308
+ args[3].should == @mock_definition_1
309
+ @mock_parsed_field_def_1
310
+ end
311
+ Tester.should_receive(:parse_field_definition) do |args|
312
+ args[3].should == @mock_definition_2
313
+ @mock_parsed_field_def_2
314
+ end
315
+ Tester.model_conditions( @params, @model_class, @definitions )
316
+ end
317
+
318
+ end
319
+
320
+ it "should add the :where key from the parsed field definition to the :where key of the return hash " do
321
+ Tester.model_conditions( @params, @model_class, @definitions )[:where].should == ['where 1', 'where 2']
322
+ end
323
+
324
+ it "should add the :values key from the parsed field definition to the :values key of the return hash " do
325
+ Tester.model_conditions( @params, @model_class, @definitions )[:values].should == ['values 1', 'values 2']
326
+ end
327
+ end
328
+ end
329
+
330
+
331
+ describe "params_to_conditions" do
332
+ before(:each) do
333
+ @mock_params = mock('params')
334
+ @model_class_1 = "model class 1"
335
+ @model_class_2 = "model class 2"
336
+ @models = { @model_class_1 => 'model class def 1', @model_class_2 => 'model class def 2' }
337
+ @mock_parsed_model_def_1 = {:where=>['where 1'], :values=>['values 1']}
338
+ @mock_parsed_model_def_2 = {:where=>['where 2'], :values=>['values 2']}
339
+ Tester.stub!(:model_conditions).and_return( @mock_parsed_model_def_1, @mock_parsed_model_def_2 )
340
+ end
341
+
342
+ context "when not given a :params key" do
343
+ it "should raise an ArgumentError" do
344
+ lambda{ Tester.params_to_conditions(:blart=>'flange', :models=>'models') }.should raise_error(ArgumentError)
345
+ end
162
346
  end
347
+ context "when not given a :models key" do
348
+ it "should raise an ArgumentError" do
349
+ lambda{ Tester.params_to_conditions(:blart=>'flange', :params=>'params') }.should raise_error(ArgumentError)
350
+ end
351
+ end
352
+
353
+ context "for each given model" do
354
+ before(:each) do
355
+
356
+ end
357
+
358
+ it "should call model_conditions" do
359
+ Tester.should_receive(:model_conditions).twice.and_return( @mock_parsed_model_def_1, @mock_parsed_model_def_2 )
360
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )
361
+ end
362
+
363
+ describe "model_conditions_call_params" do
364
+ it "should pass on the given :params" do
365
+ Tester.should_receive(:model_conditions).with(@mock_params, anything, anything).twice.and_return( @mock_parsed_model_def_1, @mock_parsed_model_def_2 )
366
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )
367
+ end
368
+
369
+ it "should pass each model class" do
370
+ Tester.should_receive(:model_conditions) do |args|
371
+ args[1].should == 'model class 1'
372
+ @mock_parsed_model_def_1
373
+ end
374
+ Tester.should_receive(:model_conditions) do |args|
375
+ args[1].should == 'model class 2'
376
+ @mock_parsed_model_def_2
377
+ end
378
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )
379
+ end
380
+
381
+ it "should pass the models field definitions" do
382
+ Tester.should_receive(:model_conditions) do |args|
383
+ args[2].should == 'model class def 1'
384
+ @mock_parsed_model_def_1
385
+ end
386
+ Tester.should_receive(:model_conditions) do |args|
387
+ args[2].should == 'model class def 2'
388
+ @mock_parsed_model_def_2
389
+ end
390
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )
391
+ end
392
+ end
393
+ end
394
+
395
+ describe "returned value" do
396
+ it "should be an array" do
397
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models ).should be_a(Array)
398
+ end
399
+
400
+ describe "first element" do
401
+ it "should be a string" do
402
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )[0].should be_a(String)
403
+ end
404
+
405
+ it "should have the :where keys from the parsed model definitions joined with AND " do
406
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )[0].should == "where 1 AND where 2"
407
+ end
408
+ end
409
+
410
+ describe "subsequent elements" do
411
+ it "should be the values from the parsed model definitions" do
412
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )[1].should == 'values 1'
413
+ Tester.params_to_conditions( :params=>@mock_params, :models=>@models )[2].should == 'values 2'
414
+ end
415
+ end
416
+ end
417
+
418
+ end
419
+ end
420
+
421
+ describe "instance_methods" do
422
+ it "should not raise an error" do
423
+ lambda {
424
+ @t.require_obj_or_id( {:key=>'val'}, :key )
425
+ }.should_not raise_error
163
426
  end
164
427
  end
165
428
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itrigga-param_fu
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Al Davidson
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-11-03 00:00:00 +00:00
19
+ date: 2011-11-10 00:00:00 +00:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency