searchlogic 2.4.25 → 2.4.26
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.yml +1 -1
- data/lib/searchlogic.rb +9 -0
- data/lib/searchlogic/search.rb +7 -240
- data/lib/searchlogic/search/base.rb +26 -0
- data/lib/searchlogic/search/conditions.rb +58 -0
- data/lib/searchlogic/search/date_parts.rb +23 -0
- data/lib/searchlogic/search/implementation.rb +14 -0
- data/lib/searchlogic/search/method_missing.rb +123 -0
- data/lib/searchlogic/search/ordering.rb +10 -0
- data/lib/searchlogic/search/scopes.rb +19 -0
- data/lib/searchlogic/search/to_yaml.rb +38 -0
- data/lib/searchlogic/search/unknown_condition_error.rb +15 -0
- data/searchlogic.gemspec +11 -2
- data/spec/searchlogic/search_spec.rb +18 -2
- data/spec/spec_helper.rb +2 -2
- metadata +12 -7
data/VERSION.yml
CHANGED
data/lib/searchlogic.rb
CHANGED
@@ -9,6 +9,15 @@ require "searchlogic/named_scopes/association_conditions"
|
|
9
9
|
require "searchlogic/named_scopes/association_ordering"
|
10
10
|
require "searchlogic/named_scopes/alias_scope"
|
11
11
|
require "searchlogic/named_scopes/or_conditions"
|
12
|
+
require "searchlogic/search/base"
|
13
|
+
require "searchlogic/search/conditions"
|
14
|
+
require "searchlogic/search/date_parts"
|
15
|
+
require "searchlogic/search/implementation"
|
16
|
+
require "searchlogic/search/method_missing"
|
17
|
+
require "searchlogic/search/ordering"
|
18
|
+
require "searchlogic/search/scopes"
|
19
|
+
require "searchlogic/search/to_yaml"
|
20
|
+
require "searchlogic/search/unknown_condition_error"
|
12
21
|
require "searchlogic/search"
|
13
22
|
|
14
23
|
Proc.send(:include, Searchlogic::CoreExt::Proc)
|
data/lib/searchlogic/search.rb
CHANGED
@@ -15,245 +15,12 @@ module Searchlogic
|
|
15
15
|
#
|
16
16
|
# User.username_like("bjohnson").all
|
17
17
|
class Search
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
Search.new(self, scope(:find), conditions)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Is an invalid condition is used this error will be raised. Ex:
|
30
|
-
#
|
31
|
-
# User.search(:unkown => true)
|
32
|
-
#
|
33
|
-
# Where unknown is not a valid named scope for the User model.
|
34
|
-
class UnknownConditionError < StandardError
|
35
|
-
def initialize(condition)
|
36
|
-
msg = "The #{condition} is not a valid condition. You may only use conditions that map to a named scope"
|
37
|
-
super(msg)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
attr_accessor :klass, :current_scope, :conditions
|
42
|
-
undef :id if respond_to?(:id)
|
43
|
-
|
44
|
-
# Creates a new search object for the given class. Ex:
|
45
|
-
#
|
46
|
-
# Searchlogic::Search.new(User, {}, {:username_like => "bjohnson"})
|
47
|
-
def initialize(klass, current_scope, conditions = {})
|
48
|
-
self.klass = klass
|
49
|
-
self.current_scope = current_scope
|
50
|
-
@conditions ||= {}
|
51
|
-
self.conditions = conditions if conditions.is_a?(Hash)
|
52
|
-
end
|
53
|
-
|
54
|
-
def clone
|
55
|
-
self.class.new(klass, current_scope && current_scope.clone, conditions.clone)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Returns a hash of the current conditions set.
|
59
|
-
def conditions
|
60
|
-
mass_conditions.clone.merge(@conditions)
|
61
|
-
end
|
62
|
-
|
63
|
-
def compact_conditions
|
64
|
-
conditions.select { |k,v| !v.blank? }
|
65
|
-
end
|
66
|
-
|
67
|
-
# Accepts a hash of conditions.
|
68
|
-
def conditions=(values)
|
69
|
-
values.each do |condition, value|
|
70
|
-
# if a condition name ends with "(1i)", assume it's date / datetime
|
71
|
-
if condition =~ /(.*)\(1i\)$/
|
72
|
-
date_scope_name = $1
|
73
|
-
date_parts = (1..6).to_a.map do |idx|
|
74
|
-
values.delete("#{ date_scope_name }(#{ idx }i)")
|
75
|
-
end.reject{|s| s.blank? }.map{|s| s.to_i }
|
76
|
-
|
77
|
-
# did we get enough info to build a time?
|
78
|
-
if date_parts.length >= 3
|
79
|
-
values[date_scope_name] = Time.zone.local(*date_parts)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
values.each do |condition, value|
|
85
|
-
mass_conditions[condition.to_sym] = value
|
86
|
-
value.delete_if { |v| ignore_value?(v) } if value.is_a?(Array)
|
87
|
-
next if ignore_value?(value)
|
88
|
-
send("#{condition}=", value)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Delete a condition from the search. Since conditions map to named scopes,
|
93
|
-
# if a named scope accepts a parameter there is no way to actually delete
|
94
|
-
# the scope if you do not want it anymore. A nil value might be meaningful
|
95
|
-
# to that scope.
|
96
|
-
def delete(*names)
|
97
|
-
names.each do |name|
|
98
|
-
@conditions.delete(name.to_sym)
|
99
|
-
mass_conditions.delete(name)
|
100
|
-
end
|
101
|
-
self
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns the column we are currently ordering by
|
105
|
-
def ordering_by
|
106
|
-
order && order.to_s.gsub(/^(ascend|descend)_by_/, '')
|
107
|
-
end
|
108
|
-
|
109
|
-
def respond_to?(*args)
|
110
|
-
super || scope?(normalize_scope_name(args.first))
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
def method_missing(name, *args, &block)
|
115
|
-
condition_name = condition_name(name)
|
116
|
-
scope_name = scope_name(condition_name)
|
117
|
-
|
118
|
-
if setter?(name)
|
119
|
-
if scope?(scope_name)
|
120
|
-
if args.size == 1
|
121
|
-
write_condition(
|
122
|
-
condition_name,
|
123
|
-
type_cast(
|
124
|
-
args.first,
|
125
|
-
cast_type(scope_name),
|
126
|
-
scope_options(scope_name).respond_to?(:searchlogic_options) ? scope_options(scope_name).searchlogic_options : {}
|
127
|
-
)
|
128
|
-
)
|
129
|
-
else
|
130
|
-
write_condition(condition_name, args)
|
131
|
-
end
|
132
|
-
else
|
133
|
-
raise UnknownConditionError.new(condition_name)
|
134
|
-
end
|
135
|
-
elsif scope?(scope_name) && args.size <= 1
|
136
|
-
if args.size == 0
|
137
|
-
read_condition(condition_name)
|
138
|
-
else
|
139
|
-
send("#{condition_name}=", *args)
|
140
|
-
self
|
141
|
-
end
|
142
|
-
else
|
143
|
-
scope = conditions_array.inject(klass.scoped(current_scope) || {}) do |scope, condition|
|
144
|
-
scope_name, value = condition
|
145
|
-
scope_name = normalize_scope_name(scope_name)
|
146
|
-
klass.send(scope_name, value) if !klass.respond_to?(scope_name)
|
147
|
-
arity = klass.named_scope_arity(scope_name)
|
148
|
-
|
149
|
-
if !arity || arity == 0
|
150
|
-
if value == true
|
151
|
-
scope.send(scope_name)
|
152
|
-
else
|
153
|
-
scope
|
154
|
-
end
|
155
|
-
elsif arity == -1
|
156
|
-
scope.send(scope_name, *(value.is_a?(Array) ? value : [value]))
|
157
|
-
else
|
158
|
-
scope.send(scope_name, value)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
scope.send(name, *args, &block)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# This is here as a hook to allow people to modify the order in which the conditions are called, for whatever reason.
|
166
|
-
def conditions_array
|
167
|
-
@conditions.to_a
|
168
|
-
end
|
169
|
-
|
170
|
-
def normalize_scope_name(scope_name)
|
171
|
-
case
|
172
|
-
when klass.scopes.key?(scope_name.to_sym) then scope_name.to_sym
|
173
|
-
when klass.column_names.include?(scope_name.to_s) then "#{scope_name}_equals".to_sym
|
174
|
-
else scope_name.to_sym
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def setter?(name)
|
179
|
-
!(name.to_s =~ /=$/).nil?
|
180
|
-
end
|
181
|
-
|
182
|
-
def condition_name(name)
|
183
|
-
condition = name.to_s.match(/(\w+)=?$/)
|
184
|
-
condition ? condition[1].to_sym : nil
|
185
|
-
end
|
186
|
-
|
187
|
-
def write_condition(name, value)
|
188
|
-
@conditions[name] = value
|
189
|
-
end
|
190
|
-
|
191
|
-
def read_condition(name)
|
192
|
-
@conditions[name]
|
193
|
-
end
|
194
|
-
|
195
|
-
def mass_conditions
|
196
|
-
@mass_conditions ||= {}
|
197
|
-
end
|
198
|
-
|
199
|
-
def scope_name(condition_name)
|
200
|
-
condition_name && normalize_scope_name(condition_name)
|
201
|
-
end
|
202
|
-
|
203
|
-
def scope?(scope_name)
|
204
|
-
klass.scopes.key?(scope_name) || klass.condition?(scope_name)
|
205
|
-
end
|
206
|
-
|
207
|
-
def scope_options(name)
|
208
|
-
klass.send(name, nil) if !klass.respond_to?(name) # We need to set up the named scope if it doesn't exist, so we can get a value for named_scope_options
|
209
|
-
klass.named_scope_options(name)
|
210
|
-
end
|
211
|
-
|
212
|
-
def cast_type(name)
|
213
|
-
named_scope_options = scope_options(name)
|
214
|
-
arity = klass.named_scope_arity(name)
|
215
|
-
if !arity || arity == 0
|
216
|
-
:boolean
|
217
|
-
else
|
218
|
-
named_scope_options.respond_to?(:searchlogic_options) ? named_scope_options.searchlogic_options[:type] : :string
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
def type_cast(value, type, options = {})
|
223
|
-
case value
|
224
|
-
when Array
|
225
|
-
value.collect { |v| type_cast(v, type) }
|
226
|
-
when Range
|
227
|
-
Range.new(type_cast(value.first, type), type_cast(value.last, type))
|
228
|
-
else
|
229
|
-
# Let's leverage ActiveRecord's type casting, so that casting is consistent
|
230
|
-
# with the other models.
|
231
|
-
column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
|
232
|
-
column_for_type_cast.instance_variable_set(:@type, type)
|
233
|
-
casted_value = column_for_type_cast.type_cast(value)
|
234
|
-
|
235
|
-
if Time.zone && casted_value.is_a?(Time)
|
236
|
-
if value.is_a?(String)
|
237
|
-
if options[:skip_conversion]
|
238
|
-
casted_value.utc
|
239
|
-
else
|
240
|
-
(casted_value + (Time.zone.utc_offset * -1)).in_time_zone
|
241
|
-
end
|
242
|
-
else
|
243
|
-
if options[:skip_conversion]
|
244
|
-
casted_value.utc
|
245
|
-
else
|
246
|
-
casted_value.in_time_zone
|
247
|
-
end
|
248
|
-
end
|
249
|
-
else
|
250
|
-
casted_value
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
def ignore_value?(value)
|
256
|
-
(value.is_a?(String) && value.blank?) || (value.is_a?(Array) && value.empty?)
|
257
|
-
end
|
18
|
+
include Base
|
19
|
+
include Conditions
|
20
|
+
include DateParts
|
21
|
+
include MethodMissing
|
22
|
+
include Scopes
|
23
|
+
include Ordering
|
24
|
+
include ToYaml
|
258
25
|
end
|
259
26
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
module Base
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
attr_accessor :klass, :current_scope
|
7
|
+
undef :id if respond_to?(:id)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Creates a new search object for the given class. Ex:
|
12
|
+
#
|
13
|
+
# Searchlogic::Search.new(User, {}, {:username_like => "bjohnson"})
|
14
|
+
def initialize(klass, current_scope, conditions = {})
|
15
|
+
self.klass = klass
|
16
|
+
self.current_scope = current_scope
|
17
|
+
@conditions ||= {}
|
18
|
+
self.conditions = conditions if conditions.is_a?(Hash)
|
19
|
+
end
|
20
|
+
|
21
|
+
def clone
|
22
|
+
self.class.new(klass, current_scope && current_scope.clone, conditions.clone)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
module Conditions
|
4
|
+
# Returns a hash of the current conditions set.
|
5
|
+
def conditions
|
6
|
+
mass_conditions.clone.merge(@conditions)
|
7
|
+
end
|
8
|
+
|
9
|
+
def compact_conditions
|
10
|
+
conditions.select { |k,v| !v.blank? }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Accepts a hash of conditions.
|
14
|
+
def conditions=(values)
|
15
|
+
values.each do |condition, value|
|
16
|
+
mass_conditions[condition.to_sym] = value
|
17
|
+
value.delete_if { |v| ignore_value?(v) } if value.is_a?(Array)
|
18
|
+
next if ignore_value?(value)
|
19
|
+
send("#{condition}=", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Delete a condition from the search. Since conditions map to named scopes,
|
24
|
+
# if a named scope accepts a parameter there is no way to actually delete
|
25
|
+
# the scope if you do not want it anymore. A nil value might be meaningful
|
26
|
+
# to that scope.
|
27
|
+
def delete(*names)
|
28
|
+
names.each do |name|
|
29
|
+
@conditions.delete(name.to_sym)
|
30
|
+
mass_conditions.delete(name)
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
# This is here as a hook to allow people to modify the order in which the conditions are called, for whatever reason.
|
37
|
+
def conditions_array
|
38
|
+
@conditions.to_a
|
39
|
+
end
|
40
|
+
|
41
|
+
def write_condition(name, value)
|
42
|
+
@conditions[name] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_condition(name)
|
46
|
+
@conditions[name]
|
47
|
+
end
|
48
|
+
|
49
|
+
def mass_conditions
|
50
|
+
@mass_conditions ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def ignore_value?(value)
|
54
|
+
(value.is_a?(String) && value.blank?) || (value.is_a?(Array) && value.empty?)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
module DateParts
|
4
|
+
def conditions=(values)
|
5
|
+
values.clone.each do |condition, value|
|
6
|
+
# if a condition name ends with "(1i)", assume it's date / datetime
|
7
|
+
if condition =~ /(.*)\(1i\)$/
|
8
|
+
date_scope_name = $1
|
9
|
+
date_parts = (1..6).to_a.map do |idx|
|
10
|
+
values.delete("#{ date_scope_name }(#{ idx }i)")
|
11
|
+
end.reject{|s| s.blank? }.map{|s| s.to_i }
|
12
|
+
|
13
|
+
# did we get enough info to build a time?
|
14
|
+
if date_parts.length >= 3
|
15
|
+
values[date_scope_name] = Time.zone.local(*date_parts)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
# Responsible for adding a "search" method into your models.
|
4
|
+
module Implementation
|
5
|
+
# Additional method, gets aliased as "search" if that method
|
6
|
+
# is available. A lot of other libraries like to use "search"
|
7
|
+
# as well, so if you have a conflict like this, you can use
|
8
|
+
# this method directly.
|
9
|
+
def searchlogic(conditions = {})
|
10
|
+
Search.new(self, scope(:find), conditions)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
module MethodMissing
|
4
|
+
def respond_to?(*args)
|
5
|
+
super || scope?(normalize_scope_name(args.first))
|
6
|
+
rescue Searchlogic::NamedScopes::OrConditions::UnknownConditionError
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def method_missing(name, *args, &block)
|
12
|
+
condition_name = condition_name(name)
|
13
|
+
scope_name = scope_name(condition_name)
|
14
|
+
|
15
|
+
if setter?(name)
|
16
|
+
if scope?(scope_name)
|
17
|
+
if args.size == 1
|
18
|
+
write_condition(
|
19
|
+
condition_name,
|
20
|
+
type_cast(
|
21
|
+
args.first,
|
22
|
+
cast_type(scope_name),
|
23
|
+
scope_options(scope_name).respond_to?(:searchlogic_options) ? scope_options(scope_name).searchlogic_options : {}
|
24
|
+
)
|
25
|
+
)
|
26
|
+
else
|
27
|
+
write_condition(condition_name, args)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
raise UnknownConditionError.new(condition_name)
|
31
|
+
end
|
32
|
+
elsif scope?(scope_name) && args.size <= 1
|
33
|
+
if args.size == 0
|
34
|
+
read_condition(condition_name)
|
35
|
+
else
|
36
|
+
send("#{condition_name}=", *args)
|
37
|
+
self
|
38
|
+
end
|
39
|
+
else
|
40
|
+
scope = conditions_array.inject(klass.scoped(current_scope) || {}) do |scope, condition|
|
41
|
+
scope_name, value = condition
|
42
|
+
scope_name = normalize_scope_name(scope_name)
|
43
|
+
klass.send(scope_name, value) if !klass.respond_to?(scope_name)
|
44
|
+
arity = klass.named_scope_arity(scope_name)
|
45
|
+
|
46
|
+
if !arity || arity == 0
|
47
|
+
if value == true
|
48
|
+
scope.send(scope_name)
|
49
|
+
else
|
50
|
+
scope
|
51
|
+
end
|
52
|
+
elsif arity == -1
|
53
|
+
scope.send(scope_name, *(value.is_a?(Array) ? value : [value]))
|
54
|
+
else
|
55
|
+
scope.send(scope_name, value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
scope.send(name, *args, &block)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def normalize_scope_name(scope_name)
|
63
|
+
case
|
64
|
+
when klass.scopes.key?(scope_name.to_sym) then scope_name.to_sym
|
65
|
+
when klass.column_names.include?(scope_name.to_s) then "#{scope_name}_equals".to_sym
|
66
|
+
else scope_name.to_sym
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def setter?(name)
|
71
|
+
!(name.to_s =~ /=$/).nil?
|
72
|
+
end
|
73
|
+
|
74
|
+
def condition_name(name)
|
75
|
+
condition = name.to_s.match(/(\w+)=?$/)
|
76
|
+
condition ? condition[1].to_sym : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def cast_type(name)
|
80
|
+
named_scope_options = scope_options(name)
|
81
|
+
arity = klass.named_scope_arity(name)
|
82
|
+
if !arity || arity == 0
|
83
|
+
:boolean
|
84
|
+
else
|
85
|
+
named_scope_options.respond_to?(:searchlogic_options) ? named_scope_options.searchlogic_options[:type] : :string
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def type_cast(value, type, options = {})
|
90
|
+
case value
|
91
|
+
when Array
|
92
|
+
value.collect { |v| type_cast(v, type) }
|
93
|
+
when Range
|
94
|
+
Range.new(type_cast(value.first, type), type_cast(value.last, type))
|
95
|
+
else
|
96
|
+
# Let's leverage ActiveRecord's type casting, so that casting is consistent
|
97
|
+
# with the other models.
|
98
|
+
column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
|
99
|
+
column_for_type_cast.instance_variable_set(:@type, type)
|
100
|
+
casted_value = column_for_type_cast.type_cast(value)
|
101
|
+
|
102
|
+
if Time.zone && casted_value.is_a?(Time)
|
103
|
+
if value.is_a?(String)
|
104
|
+
if options[:skip_conversion]
|
105
|
+
casted_value.utc
|
106
|
+
else
|
107
|
+
(casted_value + (Time.zone.utc_offset * -1)).in_time_zone
|
108
|
+
end
|
109
|
+
else
|
110
|
+
if options[:skip_conversion]
|
111
|
+
casted_value.utc
|
112
|
+
else
|
113
|
+
casted_value.in_time_zone
|
114
|
+
end
|
115
|
+
end
|
116
|
+
else
|
117
|
+
casted_value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
module Scopes
|
4
|
+
private
|
5
|
+
def scope_name(condition_name)
|
6
|
+
condition_name && normalize_scope_name(condition_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def scope?(scope_name)
|
10
|
+
klass.scopes.key?(scope_name) || klass.condition?(scope_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def scope_options(name)
|
14
|
+
klass.send(name, nil) if !klass.respond_to?(name) # We need to set up the named scope if it doesn't exist, so we can get a value for named_scope_options
|
15
|
+
klass.named_scope_options(name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
module ToYaml
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
yaml_as "tag:ruby.yaml.org,2002:class"
|
7
|
+
extend ClassMethods
|
8
|
+
include InstanceMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def yaml_new(klass, tag, val)
|
14
|
+
raise "ass"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
def to_yaml( opts = {} )
|
20
|
+
YAML::quick_emit( self, opts ) do |out|
|
21
|
+
out.map("tag:ruby.yaml.org,2002:object:Searchlogic::Search") do |map|
|
22
|
+
map.add('class_name', klass.name)
|
23
|
+
map.add('current_scope', current_scope)
|
24
|
+
map.add('conditions', conditions)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def yaml_initialize(taguri, attributes = {})
|
30
|
+
self.klass = attributes["class_name"].constantize
|
31
|
+
self.current_scope = attributes["current_scope"]
|
32
|
+
@conditions ||= {}
|
33
|
+
self.conditions = attributes["conditions"]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
class Search
|
3
|
+
# Is an invalid condition is used this error will be raised. Ex:
|
4
|
+
#
|
5
|
+
# User.search(:unkown => true)
|
6
|
+
#
|
7
|
+
# Where unknown is not a valid named scope for the User model.
|
8
|
+
class UnknownConditionError < StandardError
|
9
|
+
def initialize(condition)
|
10
|
+
msg = "The #{condition} is not a valid condition. You may only use conditions that map to a named scope"
|
11
|
+
super(msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/searchlogic.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{searchlogic}
|
8
|
-
s.version = "2.4.
|
8
|
+
s.version = "2.4.26"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ben Johnson of Binary Logic"]
|
12
|
-
s.date = %q{2010-09-
|
12
|
+
s.date = %q{2010-09-10}
|
13
13
|
s.description = %q{Searchlogic makes using ActiveRecord named scopes easier and less repetitive.}
|
14
14
|
s.email = %q{bjohnson@binarylogic.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -37,6 +37,15 @@ Gem::Specification.new do |s|
|
|
37
37
|
"lib/searchlogic/named_scopes/ordering.rb",
|
38
38
|
"lib/searchlogic/rails_helpers.rb",
|
39
39
|
"lib/searchlogic/search.rb",
|
40
|
+
"lib/searchlogic/search/base.rb",
|
41
|
+
"lib/searchlogic/search/conditions.rb",
|
42
|
+
"lib/searchlogic/search/date_parts.rb",
|
43
|
+
"lib/searchlogic/search/implementation.rb",
|
44
|
+
"lib/searchlogic/search/method_missing.rb",
|
45
|
+
"lib/searchlogic/search/ordering.rb",
|
46
|
+
"lib/searchlogic/search/scopes.rb",
|
47
|
+
"lib/searchlogic/search/to_yaml.rb",
|
48
|
+
"lib/searchlogic/search/unknown_condition_error.rb",
|
40
49
|
"rails/init.rb",
|
41
50
|
"searchlogic.gemspec",
|
42
51
|
"spec/searchlogic/active_record/association_proxy_spec.rb",
|
@@ -424,15 +424,18 @@ describe Searchlogic::Search do
|
|
424
424
|
context "#respond_to?" do
|
425
425
|
it "should respond to created_at_lte" do
|
426
426
|
s = User.search
|
427
|
-
s.created_at_lte
|
428
427
|
s.respond_to?(:created_at_lte).should == true
|
429
428
|
end
|
430
429
|
|
431
430
|
it "should respond to created_at" do
|
432
431
|
s = User.search
|
433
|
-
s.created_at_lte
|
434
432
|
s.respond_to?(:created_at).should == true
|
435
433
|
end
|
434
|
+
|
435
|
+
it "should not respond to created_at_or_whatever" do
|
436
|
+
s = User.search
|
437
|
+
s.respond_to?(:created_at_or_whatever)
|
438
|
+
end
|
436
439
|
end
|
437
440
|
|
438
441
|
context "delegation" do
|
@@ -478,4 +481,17 @@ describe Searchlogic::Search do
|
|
478
481
|
search.paged(0, 0).count.should == 0
|
479
482
|
end
|
480
483
|
end
|
484
|
+
|
485
|
+
context "yaml" do
|
486
|
+
it "should load yaml" do
|
487
|
+
time = Time.now
|
488
|
+
search = User.search(:name_like => "Ben", :created_at_after => time)
|
489
|
+
search.current_scope = {:conditions => "1=1"}
|
490
|
+
yaml = search.to_yaml
|
491
|
+
loaded_search = YAML.load(yaml)
|
492
|
+
loaded_search.current_scope.should == {:conditions => "1=1"}
|
493
|
+
loaded_search.name_like.should == "Ben"
|
494
|
+
loaded_search.created_at_after.should == time
|
495
|
+
end
|
496
|
+
end
|
481
497
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'spec'
|
2
2
|
require 'rubygems'
|
3
|
-
|
3
|
+
require 'ruby-debug'
|
4
4
|
require 'active_record'
|
5
5
|
|
6
6
|
ENV['TZ'] = 'UTC'
|
7
|
-
|
7
|
+
Time.zone = 'Eastern Time (US & Canada)'
|
8
8
|
|
9
9
|
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
10
10
|
ActiveRecord::Base.configurations = true
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchlogic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 45
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 2
|
8
7
|
- 4
|
9
|
-
-
|
10
|
-
version: 2.4.
|
8
|
+
- 26
|
9
|
+
version: 2.4.26
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Ben Johnson of Binary Logic
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-09-
|
17
|
+
date: 2010-09-10 00:00:00 -04:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -26,7 +25,6 @@ dependencies:
|
|
26
25
|
requirements:
|
27
26
|
- - ">="
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 15
|
30
28
|
segments:
|
31
29
|
- 2
|
32
30
|
- 0
|
@@ -64,6 +62,15 @@ files:
|
|
64
62
|
- lib/searchlogic/named_scopes/ordering.rb
|
65
63
|
- lib/searchlogic/rails_helpers.rb
|
66
64
|
- lib/searchlogic/search.rb
|
65
|
+
- lib/searchlogic/search/base.rb
|
66
|
+
- lib/searchlogic/search/conditions.rb
|
67
|
+
- lib/searchlogic/search/date_parts.rb
|
68
|
+
- lib/searchlogic/search/implementation.rb
|
69
|
+
- lib/searchlogic/search/method_missing.rb
|
70
|
+
- lib/searchlogic/search/ordering.rb
|
71
|
+
- lib/searchlogic/search/scopes.rb
|
72
|
+
- lib/searchlogic/search/to_yaml.rb
|
73
|
+
- lib/searchlogic/search/unknown_condition_error.rb
|
67
74
|
- rails/init.rb
|
68
75
|
- searchlogic.gemspec
|
69
76
|
- spec/searchlogic/active_record/association_proxy_spec.rb
|
@@ -92,7 +99,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
92
99
|
requirements:
|
93
100
|
- - ">="
|
94
101
|
- !ruby/object:Gem::Version
|
95
|
-
hash: 3
|
96
102
|
segments:
|
97
103
|
- 0
|
98
104
|
version: "0"
|
@@ -101,7 +107,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
107
|
requirements:
|
102
108
|
- - ">="
|
103
109
|
- !ruby/object:Gem::Version
|
104
|
-
hash: 3
|
105
110
|
segments:
|
106
111
|
- 0
|
107
112
|
version: "0"
|