cw_condition 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/Rakefile +7 -0
- data/lib/cw_condition/cw_condition.rb +239 -0
- data/lib/cw_condition.rb +1 -0
- data/test/test_cw_condition.rb +64 -0
- metadata +69 -0
data/.gemtest
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -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
|
data/lib/cw_condition.rb
ADDED
@@ -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
|
+
|