cw_condition 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gemtest ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new do |t|
3
+ t.libs << 'test'
4
+ end
5
+
6
+ desc "Run Tests"
7
+ task :default => :test
@@ -0,0 +1,239 @@
1
+ # Copyright 2011 Christopher Wojno
2
+ module CW
3
+
4
+ # Conditions for SQL Queries
5
+ #
6
+ # This case has happened to me so frequently, I decided to write a function to perform this logic for me.
7
+ #
8
+ # Say you have an Active Record query you want to make. You normally want to find all the Widgets with customizations, but this time, you want to search for customizations that are pink:
9
+ #
10
+ # Widget.find(:all, :conditions => 'customized' )
11
+ # Widget.find(:all, :conditions => ['color = ?', 'pink'] )
12
+ #
13
+ # But you want to combine them:
14
+ # Widget.find(:all, :conditions => ['customized AND color = ?','pink'] )
15
+ #
16
+ # Looks simple if you're doing this all hard-coded style, but if you want more flexible models where you're using functions to generate your conditions and then chaining up conditions, as I often do, then you'll need something to combine or chain up those conditions
17
+ #
18
+ # Enter Condition. It handles the 3 big cases: You're merging into a set of missing conditions (no conditions or nil conditions), conditions specified only as a string, or conditions as an Array.
19
+ #
20
+ # Examples:
21
+ # class User < ActiveRecord::Base
22
+ # def self.conditions_for_puppies
23
+ # 'EXISTS( SELECT * FROM users_with_puppies WHERE users_with_puppies.user_id = users.id )'
24
+ # end
25
+ # def self.conditions_for_of_age
26
+ # 'users.age >= 21' # Assuming United States adulthood, we mature a little more slowly than some to great effects ;-)
27
+ # end
28
+ # end
29
+ #
30
+ # And if you want to select all users over the age of 21 with puppies in your controller
31
+ # def list_of_age_with_puppies
32
+ # User.find(:all, :conditions => CW::Condition.and( User.conditions_for_puppies, User.conditions_for_of_age ) )
33
+ # end
34
+ #
35
+ # You can even chain up additional ones for more refinement. Assume you have a form that asks to filter by name. That field has a name "name_is_like".
36
+ #
37
+ # def list_of_age_with_puppies_filter_name
38
+ # User.find(:all, :conditions => CW::Condition.and( CW::Condition.and( User.conditions_for_puppies, User.conditions_for_of_age ), ['lower(users.name) LIKE ?',params[:name_is_like]] ) )
39
+ # end
40
+ #
41
+ # OK, it's a little ugly. But it was only designed to chain up a few of them. If you need to chain them up, instantiate the class and it will handle this for you.
42
+ #
43
+ # Cleaner use: An instantiated condition merger:
44
+ #
45
+ # Instead of using strings and arrays, use the class:
46
+ # class User < ActiveRecord::Base
47
+ # def self.conditions_for_puppies
48
+ # CW::Condition.new 'EXISTS( SELECT * FROM users_with_puppies WHERE users_with_puppies.user_id = users.id )'
49
+ # end
50
+ # def self.conditions_for_of_age
51
+ # CW::Condition.new 'users.age >= 21' # Assuming United States adulthood, we mature a little more slowly than some to great effects ;-)
52
+ # end
53
+ # end
54
+ #
55
+ # And if you want to select all users over the age of 21 with puppies in your controller
56
+ # def list_of_age_with_puppies
57
+ # User.find(:all, :conditions => User.conditions_for_puppies.and( User.conditions_for_of_age ).done )
58
+ # end
59
+ #
60
+ # Or, to filter:
61
+ #
62
+ # def list_of_age_with_puppies_filter_name
63
+ # User.find(:all, :conditions => User.conditions_for_puppies.and( User.conditions_for_of_age ).and( ['lower(users.name) LIKE ?',params[:name_is_like]] ).done )
64
+ # end
65
+ #
66
+ # The done call doesn't really do anything special. It simply gets the nil, string, or array representation of the conditions so far.
67
+ #
68
+ # On Arel:
69
+ # If you have arel, the use that. This was written for apps that don't use Arel. Though, I'm not sure if Arel lets you build up conditions separately...
70
+ class Condition
71
+
72
+ # Merge AND
73
+ #
74
+ # Merges two conditions with AND
75
+ #
76
+ # Arguments:
77
+ # src: (nil,String,Array) representing a query to chain with with
78
+ # with: (nil,String,Array) representing a query to append to src
79
+ #
80
+ # Return:
81
+ # (nil,String,Array): the conditions merged together with AND
82
+ def self.and( src, with )
83
+ join( src, with, 'AND' )
84
+ end
85
+
86
+ # Merge OR
87
+ #
88
+ # Merges two conditions with OR
89
+ #
90
+ # Arguments:
91
+ # src: (nil,String,Array) representing a query to chain with with
92
+ # with: (nil,String,Array) representing a query to append to src
93
+ #
94
+ # Return:
95
+ # (nil,String,Array): the conditions merged together with OR
96
+ def self.or( src, with )
97
+ join( src, with, 'OR' )
98
+ end
99
+
100
+ # Merge AND NOT
101
+ #
102
+ # Merges two conditions with AND NOT
103
+ #
104
+ # Arguments:
105
+ # src: (nil,String,Array) representing a query to chain with with
106
+ # with: (nil,String,Array) representing a query to append to src
107
+ #
108
+ # Return:
109
+ # (nil,String,Array): the conditions merged together with AND NOT
110
+ def self.and_not( src, with )
111
+ join( src, with, 'AND NOT' )
112
+ end
113
+
114
+ # Merge OR NOT
115
+ #
116
+ # Merges two conditions with OR NOT
117
+ #
118
+ # Arguments:
119
+ # src: (nil,String,Array) representing a query to chain with with
120
+ # with: (nil,String,Array) representing a query to append to src
121
+ #
122
+ # Return:
123
+ # (nil,String,Array): the conditions merged together with OR NOT
124
+ def self.or_not( src, with )
125
+ join( src, with, 'OR NOT' )
126
+ end
127
+
128
+ # Merge with arbitrary clause
129
+ #
130
+ # Merges two conditions given an arbitrary clause. It's realy the work horse of this class. All other merging methods use this
131
+ #
132
+ # Arguments:
133
+ # src: (nil,String,Array) representing a query to chain with with
134
+ # with: (nil,String,Array) representing a query to append to src
135
+ # join_clause: (String) representing how to join src and with
136
+ #
137
+ # Return:
138
+ # (nil,String,Array): the conditions merged together with join_clause
139
+ def self.join( src, with, join_clause )
140
+ if src.nil?
141
+ return with
142
+ end
143
+ if with.nil?
144
+ return src
145
+ end
146
+
147
+ raise ArgumentError.new("src can be nil, or a String or an Array or CW::Condition. It cannot be a #{src.class.name}") unless src.is_a?(Array) or src.is_a?(String) or src.is_a?(CW::Condition)
148
+ raise ArgumentError.new("with can be nil, or a String or an Array or CW::Condition. It cannot be a #{with.class.name}") unless with.is_a?(Array) or with.is_a?(String) or with.is_a?(CW::Condition)
149
+ raise ArgumentError.new("join_clause must be a String, not an #{join_clause.class.name}") unless join_clause.is_a?(String)
150
+
151
+ src = src.conditions if src.is_a?(CW::Condition)
152
+ with = with.conditions if with.is_a?(CW::Condition)
153
+
154
+ if src.is_a?(String)
155
+ if src.strip.empty?
156
+ # coerse to array for conformity
157
+ if with.is_a?(String)
158
+ return [with]
159
+ else
160
+ return with
161
+ end
162
+ end
163
+ src = [src]
164
+ end
165
+ if with.is_a?(String)
166
+ return src if with.strip.empty?
167
+ with = [with]
168
+ end
169
+
170
+ src[0] = '(' + src[0] + ') ' + join_clause + ' (' + with[0] + ')'
171
+ src.concat( with[1..-1] ) if with.size > 1
172
+ src
173
+ end
174
+
175
+ attr_reader :conditions
176
+ def initialize( conditions = nil )
177
+ @conditions = conditions
178
+ end
179
+
180
+ # Append AND
181
+ #
182
+ # Appends a condition or set of conditions with AND
183
+ #
184
+ # Arguments:
185
+ # c: (nil,String,Array,CW::Condition) The conditions to append
186
+ def and( c )
187
+ @conditions = self.class.and( conditions, c )
188
+ self
189
+ end
190
+
191
+ # Append OR
192
+ #
193
+ # Appends a condition or set of conditions with OR
194
+ #
195
+ # Arguments:
196
+ # c: (nil,String,Array,CW::Condition) The conditions to append
197
+ def or( c )
198
+ @conditions = self.class.or( conditions, c )
199
+ self
200
+ end
201
+
202
+ # Append AND NOT
203
+ #
204
+ # Appends a condition or set of conditions with AND NOT
205
+ #
206
+ # Arguments:
207
+ # c: (nil,String,Array,CW::Condition) The conditions to append
208
+ def and_not( c )
209
+ @conditions = self.class.and_not( conditions, c )
210
+ self
211
+ end
212
+
213
+ # Append OR NOT
214
+ #
215
+ # Appends a condition or set of conditions with OR NOT
216
+ #
217
+ # Arguments:
218
+ # c: (nil,String,Array,CW::Condition) The conditions to append
219
+ def or_not( c )
220
+ @conditions = self.class.or_not( conditions, c )
221
+ self
222
+ end
223
+
224
+ # Append custom join_clause
225
+ #
226
+ # Appends a condition or set of conditions with a join clause of your choosing
227
+ #
228
+ # Arguments:
229
+ # c: (nil,String,Array,CW::Condition) The conditions to append
230
+ # how: (String) If the other append functions don't have what you need, try this. This will insert how between your clauses that you want merged
231
+ def join( c, how = 'AND' )
232
+ @conditions = self.class.join( conditions, c, how )
233
+ self
234
+ end
235
+
236
+ alias :done :conditions
237
+
238
+ end # Condition
239
+ end # CW
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/cw_condition/cw_condition.rb'
@@ -0,0 +1,64 @@
1
+ require 'test/unit'
2
+ require 'cw_condition'
3
+
4
+ class PaginationOrderTest < Test::Unit::TestCase
5
+ def setup
6
+ end
7
+ def teardown
8
+ end
9
+
10
+ def test_nil_src
11
+ assert_equal 'customized', CW::Condition.and( nil, 'customized' )
12
+ end
13
+ def test_nil_with
14
+ assert_equal 'customized', CW::Condition.and( 'customized', nil )
15
+ end
16
+
17
+ def test_and
18
+ assert_equal ['(customized) AND (color = ?)','pink'], CW::Condition.and( 'customized', ['color = ?','pink'] )
19
+ end
20
+ def test_or
21
+ assert_equal ['(customized) OR (color = ?)','pink'], CW::Condition.or( 'customized', ['color = ?','pink'] )
22
+ end
23
+ def test_and_not
24
+ assert_equal ['(customized) AND NOT (color = ?)','pink'], CW::Condition.and_not( 'customized', ['color = ?','pink'] )
25
+ end
26
+ def test_or_not
27
+ assert_equal ['(customized) OR NOT (color = ?)','pink'], CW::Condition.or_not( 'customized', ['color = ?','pink'] )
28
+ end
29
+
30
+ def test_empty_condition
31
+ assert_equal ['customized'], CW::Condition.and( 'customized', '' )
32
+ assert_equal ['customized'], CW::Condition.and( '', 'customized' )
33
+ end
34
+
35
+ def test_array_string
36
+ assert_equal ['(color = ?) AND (customized)','pink'], CW::Condition.and( ['color = ?','pink'], 'customized' )
37
+ end
38
+ def test_array_array
39
+ assert_equal ['(color = ?) AND (customized = ?)','pink','ye'], CW::Condition.and( ['color = ?','pink'], ['customized = ?','ye'] )
40
+ end
41
+
42
+ def test_bad_arguments
43
+ assert_raises( ArgumentError ) { CW::Condition.and( 3, 'customized' ) }
44
+ assert_raises( ArgumentError ) { CW::Condition.and( 'customized', 5 ) }
45
+ assert_raises( ArgumentError ) { CW::Condition.join( ['color = ?','pink'], 'customized', ['clause'] ) }
46
+ end
47
+
48
+ def test_chained_conditions
49
+ conditions_for_puppies = CW::Condition.new 'EXISTS( SELECT * FROM users_with_puppies WHERE users_with_puppies.user_id = users.id )'
50
+ conditions_for_of_age = CW::Condition.new 'users.age >= 21'
51
+ params = {:name_is_like => 'Charles the Third'}
52
+
53
+ # NB: dup lets us test using the same vars above as the condition class is modified with each call
54
+ assert_equal ['((EXISTS( SELECT * FROM users_with_puppies WHERE users_with_puppies.user_id = users.id )) AND (users.age >= 21)) AND (lower(users.name) LIKE ?)','Charles the Third'], conditions_for_puppies.dup.and( conditions_for_of_age ).and( ['lower(users.name) LIKE ?',params[:name_is_like]] ).done
55
+
56
+ assert_equal ['((EXISTS( SELECT * FROM users_with_puppies WHERE users_with_puppies.user_id = users.id )) AND NOT (users.age >= 21)) OR (lower(users.name) LIKE ?)','Charles the Third'], conditions_for_puppies.dup.and_not( conditions_for_of_age ).or( ['lower(users.name) LIKE ?',params[:name_is_like]] ).done
57
+
58
+ assert_equal ['((EXISTS( SELECT * FROM users_with_puppies WHERE users_with_puppies.user_id = users.id )) OR NOT (users.age >= 21)) CUSTOM (lower(users.name) LIKE ?)','Charles the Third'], conditions_for_puppies.dup.or_not( conditions_for_of_age ).join( ['lower(users.name) LIKE ?',params[:name_is_like]], 'CUSTOM' ).done
59
+
60
+
61
+ # NIL condition start.. OK!
62
+ assert_equal ['(users.age >= 21) AND (lower(users.name) LIKE ?)','Charles the Third'], CW::Condition.new.or_not( conditions_for_of_age ).and( ['lower(users.name) LIKE ?',params[:name_is_like]] ).done
63
+ end
64
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cw_condition
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Christopher Wojno
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-17 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: Allows one to "merge" condition hashes used in active record in a safe and uniform manner. Originally designed for Rails 2.3.14. Completely superceeds cw_condition_merge gem. Use this one, not that one.
22
+ email: cw_condition.gem@wojnosystems.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - .gemtest
31
+ - lib/cw_condition/cw_condition.rb
32
+ - lib/cw_condition.rb
33
+ - Rakefile
34
+ - test/test_cw_condition.rb
35
+ homepage: http://www.wojnosystems.com
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ hash: 3
49
+ segments:
50
+ - 0
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.8.10
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Represent ActiveRecord Conditions & Merge them
68
+ test_files: []
69
+