searchgasm 0.9.6 → 0.9.7
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/CHANGELOG +2 -0
- data/Manifest +34 -21
- data/{README.mdown → README.rdoc} +96 -63
- data/Rakefile +1 -1
- data/examples/README.rdoc +4 -0
- data/lib/searchgasm/active_record/associations.rb +40 -42
- data/lib/searchgasm/active_record/base.rb +75 -61
- data/lib/searchgasm/condition/base.rb +127 -0
- data/lib/searchgasm/condition/begins_with.rb +20 -0
- data/lib/searchgasm/condition/child_of.rb +11 -0
- data/lib/searchgasm/condition/contains.rb +20 -0
- data/lib/searchgasm/condition/descendant_of.rb +24 -0
- data/lib/searchgasm/condition/does_not_equal.rb +28 -0
- data/lib/searchgasm/condition/ends_with.rb +20 -0
- data/lib/searchgasm/condition/equals.rb +20 -0
- data/lib/searchgasm/condition/greater_than.rb +25 -0
- data/lib/searchgasm/condition/greater_than_or_equal_to.rb +20 -0
- data/lib/searchgasm/condition/inclusive_descendant_of.rb +13 -0
- data/lib/searchgasm/condition/keywords.rb +33 -0
- data/lib/searchgasm/condition/less_than.rb +25 -0
- data/lib/searchgasm/condition/less_than_or_equal_to.rb +20 -0
- data/lib/searchgasm/condition/sibling_of.rb +16 -0
- data/lib/searchgasm/condition/tree.rb +16 -0
- data/lib/searchgasm/conditions/base.rb +221 -0
- data/lib/searchgasm/conditions/protection.rb +30 -0
- data/lib/searchgasm/config.rb +137 -0
- data/lib/searchgasm/helpers/form_helper.rb +159 -0
- data/lib/searchgasm/helpers/search_helper.rb +178 -0
- data/lib/searchgasm/helpers/utilities_helper.rb +125 -0
- data/lib/searchgasm/search/base.rb +73 -179
- data/lib/searchgasm/search/conditions.rb +42 -166
- data/lib/searchgasm/search/ordering.rb +149 -0
- data/lib/searchgasm/search/pagination.rb +69 -0
- data/lib/searchgasm/search/protection.rb +61 -0
- data/lib/searchgasm/utilities.rb +30 -0
- data/lib/searchgasm/version.rb +44 -47
- data/lib/searchgasm.rb +57 -21
- data/searchgasm.gemspec +71 -46
- data/test/test_active_record_associations.rb +1 -1
- data/test/test_active_record_base.rb +4 -4
- data/test/test_condition.rb +143 -0
- data/test/{test_searchgasm_conditions.rb → test_conditions_base.rb} +43 -33
- data/test/test_search_base.rb +189 -0
- data/test/test_search_ordering.rb +91 -0
- data/test/test_search_pagination.rb +56 -0
- data/test/test_search_protection.rb +35 -0
- metadata +70 -45
- data/lib/searchgasm/search/condition.rb +0 -105
- data/lib/searchgasm/search/condition_types/begins_with_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/child_of_condition.rb +0 -17
- data/lib/searchgasm/search/condition_types/contains_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/descendant_of_condition.rb +0 -30
- data/lib/searchgasm/search/condition_types/does_not_equal_condition.rb +0 -34
- data/lib/searchgasm/search/condition_types/ends_with_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/equals_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/greater_than_condition.rb +0 -31
- data/lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb +0 -19
- data/lib/searchgasm/search/condition_types/keywords_condition.rb +0 -39
- data/lib/searchgasm/search/condition_types/less_than_condition.rb +0 -31
- data/lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb +0 -26
- data/lib/searchgasm/search/condition_types/sibling_of_condition.rb +0 -22
- data/lib/searchgasm/search/condition_types/tree_condition.rb +0 -20
- data/lib/searchgasm/search/utilities.rb +0 -34
- data/test/test_searchgasm_base.rb +0 -185
- data/test/test_searchgasm_condition_types.rb +0 -143
@@ -1,171 +1,47 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def register_condition(klass)
|
11
|
-
raise(ArgumentError, "You can only register conditions that extend BinaryLogic::Searchgasm::Search::ConditionTypes::Condition") unless klass.ancestors.include?(Condition)
|
12
|
-
conditions << klass unless conditions.include?(klass)
|
13
|
-
end
|
14
|
-
|
15
|
-
def conditions
|
16
|
-
@@conditions ||= []
|
17
|
-
end
|
18
|
-
|
19
|
-
def needed?(klass, conditions)
|
20
|
-
if conditions.is_a?(Hash)
|
21
|
-
conditions.stringify_keys.keys.each do |condition|
|
22
|
-
return true unless klass.column_names.include?(condition)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
false
|
27
|
-
end
|
1
|
+
module Searchgasm
|
2
|
+
module Search
|
3
|
+
module Conditions
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
alias_method_chain :initialize, :conditions
|
7
|
+
alias_method_chain :conditions=, :conditions
|
8
|
+
alias_method_chain :include, :conditions
|
9
|
+
alias_method_chain :sanitize, :conditions
|
28
10
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
args << {:conditions => sanitize}
|
44
|
-
klass.#{method}(*args)
|
45
|
-
end
|
46
|
-
end_eval
|
47
|
-
end
|
48
|
-
|
49
|
-
def assert_valid_values(values)
|
50
|
-
keys = condition_names.collect { |condition_name| condition_name.to_sym }
|
51
|
-
keys += klass.reflect_on_all_associations.collect { |association| association.name }
|
52
|
-
values.symbolize_keys.assert_valid_keys(keys)
|
53
|
-
end
|
54
|
-
|
55
|
-
def associations
|
56
|
-
objects.select { |object| object.is_a?(self.class) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def condition_names
|
60
|
-
@condition_names ||= []
|
61
|
-
end
|
62
|
-
|
63
|
-
def includes
|
64
|
-
i = []
|
65
|
-
associations.each do |association|
|
66
|
-
association_includes = association.includes
|
67
|
-
i << (association_includes.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_includes})
|
68
|
-
end
|
69
|
-
i.blank? ? nil : (i.size == 1 ? i.first : i)
|
70
|
-
end
|
71
|
-
|
72
|
-
def objects
|
73
|
-
@objects ||= []
|
74
|
-
end
|
75
|
-
|
76
|
-
def protect?
|
77
|
-
protect == true
|
78
|
-
end
|
79
|
-
|
80
|
-
def sanitize
|
81
|
-
conditions = merge_conditions(*objects.collect { |object| object.sanitize })
|
82
|
-
return scope if conditions.blank?
|
83
|
-
merge_conditions(conditions, scope)
|
84
|
-
end
|
85
|
-
|
86
|
-
def value=(values)
|
87
|
-
case values
|
88
|
-
when Hash
|
89
|
-
assert_valid_values(values)
|
90
|
-
values.each { |condition, value| send("#{condition}=", value) }
|
91
|
-
else
|
92
|
-
raise(ArgumentError, "You can not set a scope or pass SQL while the search is being protected") if protect?
|
93
|
-
self.scope = values
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def value
|
98
|
-
values_hash = {}
|
99
|
-
objects.each do |object|
|
100
|
-
next unless object.explicitly_set_value?
|
101
|
-
values_hash[object.name.to_sym] = object.value
|
102
|
-
end
|
103
|
-
values_hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize_with_conditions(klass, init_options = {})
|
14
|
+
self.conditions = Searchgasm::Conditions::Base.new(klass)
|
15
|
+
initialize_without_conditions(klass, init_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sets conditions on the search. Accepts a hash or a Searchgasm::Conditions::Base object.
|
19
|
+
def conditions_with_conditions=(values)
|
20
|
+
case values
|
21
|
+
when Searchgasm::Conditions::Base
|
22
|
+
@conditions = values
|
23
|
+
else
|
24
|
+
@conditions.conditions = values
|
104
25
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def add_column_conditions!
|
126
|
-
klass.columns.each do |column|
|
127
|
-
self.class.conditions.each do |condition|
|
128
|
-
name = condition.name_for_column(column)
|
129
|
-
next if name.blank?
|
130
|
-
add_condition!(condition, name, column)
|
131
|
-
condition.aliases_for_column(column).each { |alias_name| add_condition_alias!(alias_name, name) }
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def add_condition!(condition, name, column = nil)
|
137
|
-
self.condition_names << name
|
138
|
-
self.class.class_eval <<-end_eval
|
139
|
-
def #{name}_object
|
140
|
-
if @#{name}.nil?
|
141
|
-
@#{name} = #{condition.name}.new(klass#{column.nil? ? "" : ", \"#{column.name}\""})
|
142
|
-
self.objects << @#{name}
|
143
|
-
end
|
144
|
-
@#{name}
|
145
|
-
end
|
146
|
-
|
147
|
-
def #{name}; #{name}_object.value; end
|
148
|
-
def #{name}=(value); #{name}_object.value = value; end
|
149
|
-
def reset_#{name}!; objects.delete(#{name}_object); @#{name} = nil; end
|
150
|
-
end_eval
|
151
|
-
end
|
152
|
-
|
153
|
-
def add_condition_alias!(alias_name, name)
|
154
|
-
self.condition_names << alias_name
|
155
|
-
self.class.class_eval do
|
156
|
-
alias_method alias_name, name
|
157
|
-
alias_method "#{alias_name}=", "#{name}="
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def add_klass_conditions!
|
162
|
-
self.class.conditions.each do |condition|
|
163
|
-
name = condition.name_for_klass(klass)
|
164
|
-
next if name.blank?
|
165
|
-
add_condition!(condition, name)
|
166
|
-
condition.aliases_for_klass(klass).each { |alias_name| add_condition_alias!(alias_name, name) }
|
167
|
-
end
|
168
|
-
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def include_with_conditions
|
29
|
+
includes = [include_without_conditions, conditions.includes].flatten.compact.uniq
|
30
|
+
includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
|
31
|
+
end
|
32
|
+
|
33
|
+
def sanitize_with_conditions(for_method = nil)
|
34
|
+
find_options = sanitize_without_conditions(for_method)
|
35
|
+
find_options[:conditions] = find_options[:conditions].sanitize if find_options[:conditions]
|
36
|
+
find_options
|
37
|
+
end
|
38
|
+
|
39
|
+
def scope
|
40
|
+
conditions.scope
|
41
|
+
end
|
42
|
+
|
43
|
+
def scope=(value)
|
44
|
+
conditions.scope = value
|
169
45
|
end
|
170
46
|
end
|
171
47
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Search
|
3
|
+
# = Search Ordering
|
4
|
+
#
|
5
|
+
# The purpose of this module is to provide easy ordering for your searches. All that these options do is
|
6
|
+
# build :order for you. This plays a huge part in ordering your data on the interface. See Searchgasm::Helpers::SearchHelper for more information.
|
7
|
+
module Ordering
|
8
|
+
def self.included(klass)
|
9
|
+
klass.class_eval do
|
10
|
+
alias_method_chain :include, :ordering
|
11
|
+
alias_method_chain :order=, :ordering
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def include_with_ordering
|
16
|
+
includes = [include_without_ordering, order_by_includes].flatten.compact.uniq
|
17
|
+
includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
|
18
|
+
end
|
19
|
+
|
20
|
+
def order_with_ordering=(value)
|
21
|
+
@order_by = nil
|
22
|
+
self.order_without_ordering = value
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convenience method for determining if the ordering is ascending
|
26
|
+
def asc?
|
27
|
+
!desc?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Convenience method for determining if the ordering is descending
|
31
|
+
def desc?
|
32
|
+
order_as == "DESC"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines how the search is being ordered: as DESC or ASC
|
36
|
+
def order_as
|
37
|
+
return "ASC" if order.blank?
|
38
|
+
order =~ /ASC$/i ? "ASC" : "DESC"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets how the results will be ordered: ASC or DESC
|
42
|
+
def order_as=(value)
|
43
|
+
value = value.to_s.upcase
|
44
|
+
raise(ArgumentError, "order_as only accepts a string as ASC or DESC") unless ["ASC", "DESC"].include?(value)
|
45
|
+
|
46
|
+
if order.blank?
|
47
|
+
self.order = order_by_to_order(order_by, value)
|
48
|
+
else
|
49
|
+
self.order.gsub!(/(ASC|DESC)/i, value)
|
50
|
+
end
|
51
|
+
|
52
|
+
value
|
53
|
+
end
|
54
|
+
|
55
|
+
# Determines by what columns the search is being ordered. This is nifty in that is reverse engineers the order SQL to determine this, only
|
56
|
+
# if you haven't explicitly set the order_by option yourself.
|
57
|
+
def order_by
|
58
|
+
return @order_by if @order_by
|
59
|
+
|
60
|
+
if !order.blank?
|
61
|
+
# Reversege engineer order, only go 1 level deep with relationships, anything beyond that is probably excessive and not good for performance
|
62
|
+
order_parts = order.split(",").collect do |part|
|
63
|
+
part.strip!
|
64
|
+
part.gsub!(/ (ASC|DESC)$/i, "").gsub!(/(.*)\./, "")
|
65
|
+
table_name = ($1 ? $1.gsub(/[^a-z0-9_]/i, "") : nil)
|
66
|
+
part.gsub!(/[^a-z0-9_]/i, "")
|
67
|
+
reflection = nil
|
68
|
+
if table_name && table_name != klass.table_name
|
69
|
+
reflection = klass.reflect_on_association(table_name.to_sym) || klass.reflect_on_association(table_name.singularize.to_sym)
|
70
|
+
next unless reflection
|
71
|
+
{reflection.name.to_s => part}
|
72
|
+
else
|
73
|
+
part
|
74
|
+
end
|
75
|
+
end.compact
|
76
|
+
order_parts.size <= 1 ? order_parts.first : order_parts
|
77
|
+
else
|
78
|
+
klass.primary_key
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Lets you set how to order the data
|
83
|
+
#
|
84
|
+
# === Examples
|
85
|
+
#
|
86
|
+
# In these examples "ASC" is determined by the value of order_as
|
87
|
+
#
|
88
|
+
# order_by = :id # => users.id ASC
|
89
|
+
# order_by = [:id, name] # => users.id ASC, user.name ASC
|
90
|
+
# order_by = [:id, {:user_group => :name}] # => users.id ASC, user_groups.name ASC
|
91
|
+
def order_by=(value)
|
92
|
+
@order_by = get_order_by_value(value)
|
93
|
+
@order = order_by_to_order(@order_by, order_as) # use @order so @order_by doesnt get reset
|
94
|
+
@order_by
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns the includes neccessary for the "order" statement so that we don't get an SQL error
|
98
|
+
def order_by_includes
|
99
|
+
@order_by_includes ||= []
|
100
|
+
@order_by_includes.compact!
|
101
|
+
@order_by_includes.uniq!
|
102
|
+
@order_by_includes
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def order_by_to_order(order_by, order_as, alt_klass = nil, new_includes = [])
|
107
|
+
table_name = (alt_klass || klass).table_name
|
108
|
+
sql_parts = []
|
109
|
+
|
110
|
+
case order_by
|
111
|
+
when Array
|
112
|
+
order_by.each { |part| sql_parts << order_by_to_order(part, order_as) }
|
113
|
+
when Hash
|
114
|
+
raise(ArgumentError, "when passing a hash to order_by you must only have 1 key: {:user_group => :name} not {:user_group => :name, :user_group => :id}. The latter should be [{:user_group => :name}, {:user_group => :id}]") if order_by.keys.size != 1
|
115
|
+
k = order_by.keys.first
|
116
|
+
v = order_by.values.first
|
117
|
+
new_includes << k.to_sym
|
118
|
+
sql_parts << order_by_to_order(v, order_as, k.to_s.classify.constantize, new_includes)
|
119
|
+
when Symbol, String
|
120
|
+
new_include = build_order_by_includes(new_includes)
|
121
|
+
self.order_by_includes << new_include if new_include
|
122
|
+
sql_parts << "#{quote_table_name(table_name)}.#{quote_column_name(order_by)} #{order_as}"
|
123
|
+
end
|
124
|
+
|
125
|
+
sql_parts.join(", ")
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_order_by_includes(includes)
|
129
|
+
return includes.first if includes.size <= 1
|
130
|
+
includes = includes.dup
|
131
|
+
|
132
|
+
key = includes.shift
|
133
|
+
{key => build_order_by_includes(includes)}
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_order_by_value(value)
|
137
|
+
Marshal.load(value.unpack("m").first) rescue value
|
138
|
+
end
|
139
|
+
|
140
|
+
def quote_column_name(column_name)
|
141
|
+
klass.connection.quote_column_name(column_name)
|
142
|
+
end
|
143
|
+
|
144
|
+
def quote_table_name(table_name)
|
145
|
+
klass.connection.quote_table_name(table_name)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Search
|
3
|
+
module Pagination
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
alias_method_chain :limit=, :pagination
|
7
|
+
alias_method_chain :offset=, :pagination
|
8
|
+
alias_method :per_page, :limit
|
9
|
+
alias_method :per_page=, :limit=
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def limit_with_pagination=(value)
|
14
|
+
self.limit_without_pagination = value
|
15
|
+
self.page = @page unless @page.nil? # retry page now that the limit has changed
|
16
|
+
limit
|
17
|
+
end
|
18
|
+
|
19
|
+
def offset_with_pagination=(value)
|
20
|
+
self.offset_without_pagination = value
|
21
|
+
@page = nil
|
22
|
+
offset
|
23
|
+
end
|
24
|
+
|
25
|
+
def page
|
26
|
+
return 1 if offset.blank? || limit.blank?
|
27
|
+
(offset.to_f / limit).floor + 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def page=(value)
|
31
|
+
# Have to use @offset, since self.offset= resets @page
|
32
|
+
if value.nil?
|
33
|
+
@page = value
|
34
|
+
return @offset = value
|
35
|
+
end
|
36
|
+
|
37
|
+
v = value.to_i
|
38
|
+
@page = v
|
39
|
+
|
40
|
+
if limit.blank?
|
41
|
+
@offset = nil
|
42
|
+
else
|
43
|
+
v -= 1 unless v == 0
|
44
|
+
@offset = v * limit
|
45
|
+
end
|
46
|
+
value
|
47
|
+
end
|
48
|
+
|
49
|
+
def page_count
|
50
|
+
return 1 if per_page.blank? || per_page <= 0
|
51
|
+
# Letting AR caching kick in with the count query
|
52
|
+
(count / per_page.to_f).ceil
|
53
|
+
end
|
54
|
+
alias_method :page_total, :page_count
|
55
|
+
|
56
|
+
def next_page!
|
57
|
+
raise("You are on the last page") if page == page_count
|
58
|
+
self.page += 1
|
59
|
+
all
|
60
|
+
end
|
61
|
+
|
62
|
+
def prev_page!
|
63
|
+
raise("You are on the first page") if page == 1
|
64
|
+
self.page -= 1
|
65
|
+
all
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Search
|
3
|
+
module Protection
|
4
|
+
# Options that are allowed when protecting against SQL injections (still checked though)
|
5
|
+
SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
|
6
|
+
|
7
|
+
# Options that are not allowed, at all, when protecting against SQL injections
|
8
|
+
VULNERABLE_OPTIONS = Base::VALID_FIND_OPTIONS - SAFE_OPTIONS
|
9
|
+
|
10
|
+
def self.included(klass)
|
11
|
+
klass.class_eval do
|
12
|
+
attr_reader :protect
|
13
|
+
alias_method_chain :options=, :protection
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def options_with_protection=(values)
|
18
|
+
return unless values.is_a?(Hash)
|
19
|
+
self.protect = values.delete(:protect) if values.has_key?(:protect) # make sure we do this first
|
20
|
+
frisk!(values) if protect?
|
21
|
+
self.options_without_protection = values
|
22
|
+
end
|
23
|
+
|
24
|
+
def protect=(value)
|
25
|
+
conditions.protect = value
|
26
|
+
@protect = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def protect?
|
30
|
+
protect == true
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def order_by_safe?(order_by, alt_klass = nil)
|
35
|
+
return true if order_by.blank?
|
36
|
+
|
37
|
+
k = alt_klass || klass
|
38
|
+
column_names = k.column_names
|
39
|
+
|
40
|
+
[order_by].flatten.each do |column|
|
41
|
+
case column
|
42
|
+
when Hash
|
43
|
+
return false unless k.reflect_on_association(column.keys.first.to_sym)
|
44
|
+
return false unless order_by_safe?(column.values.first, column.keys.first.to_s.classify.constantize)
|
45
|
+
when Array
|
46
|
+
return false unless order_by_safe?(column)
|
47
|
+
else
|
48
|
+
return false unless column_names.include?(column.to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def frisk!(options)
|
56
|
+
options.symbolize_keys.assert_valid_keys(SAFE_OPTIONS)
|
57
|
+
raise(ArgumentError, ":order_by can only contain colum names in the string, hash, or array format") unless order_by_safe?(get_order_by_value(options[:order_by]))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module Utilities # :nodoc:
|
3
|
+
private
|
4
|
+
def merge_conditions(*conditions)
|
5
|
+
options = conditions.extract_options!
|
6
|
+
conditions.delete_if { |condition| condition.blank? }
|
7
|
+
return if conditions.blank?
|
8
|
+
return conditions.first if conditions.size == 1
|
9
|
+
|
10
|
+
conditions_strs = []
|
11
|
+
conditions_subs = []
|
12
|
+
|
13
|
+
conditions.each do |condition|
|
14
|
+
next if condition.blank?
|
15
|
+
arr_condition = [condition].flatten
|
16
|
+
conditions_strs << arr_condition.first
|
17
|
+
conditions_subs += arr_condition[1..-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
return if conditions_strs.blank?
|
21
|
+
|
22
|
+
join = options[:any] ? "OR" : "AND"
|
23
|
+
conditions_str = "(#{conditions_strs.join(") #{join} (")})"
|
24
|
+
|
25
|
+
return conditions_str if conditions_subs.blank?
|
26
|
+
|
27
|
+
[conditions_str, *conditions_subs]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/searchgasm/version.rb
CHANGED
@@ -21,61 +21,58 @@
|
|
21
21
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
22
22
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
23
23
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
-
module
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
class Version
|
32
|
-
|
33
|
-
include Comparable
|
24
|
+
module Searchgasm
|
25
|
+
# = Version
|
26
|
+
#
|
27
|
+
# A class for describing the current version of a library. The version
|
28
|
+
# consists of three parts: the +major+ number, the +minor+ number, and the
|
29
|
+
# +tiny+ (or +patch+) number.
|
30
|
+
class Version
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
include Comparable
|
33
|
+
|
34
|
+
# A convenience method for instantiating a new Version instance with the
|
35
|
+
# given +major+, +minor+, and +tiny+ components.
|
36
|
+
def self.[](major, minor, tiny)
|
37
|
+
new(major, minor, tiny)
|
38
|
+
end
|
40
39
|
|
41
|
-
|
40
|
+
attr_reader :major, :minor, :tiny
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
# Create a new Version object with the given components.
|
43
|
+
def initialize(major, minor, tiny)
|
44
|
+
@major, @minor, @tiny = major, minor, tiny
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
# Compare this version to the given +version+ object.
|
48
|
+
def <=>(version)
|
49
|
+
to_i <=> version.to_i
|
50
|
+
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
# Converts this version object to a string, where each of the three
|
53
|
+
# version components are joined by the '.' character. E.g., 2.0.0.
|
54
|
+
def to_s
|
55
|
+
@to_s ||= [@major, @minor, @tiny].join(".")
|
56
|
+
end
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
58
|
+
# Converts this version to a canonical integer that may be compared
|
59
|
+
# against other version objects.
|
60
|
+
def to_i
|
61
|
+
@to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_a
|
65
|
+
[@major, @minor, @tiny]
|
66
|
+
end
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
MAJOR = 0
|
69
|
+
MINOR = 9
|
70
|
+
TINY = 7
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
72
|
+
# The current version as a Version instance
|
73
|
+
CURRENT = new(MAJOR, MINOR, TINY)
|
74
|
+
# The current version as a String
|
75
|
+
STRING = CURRENT.to_s
|
79
76
|
|
80
77
|
end
|
81
78
|
|