searchlogic 2.2.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +5 -0
- data/README.rdoc +75 -23
- data/Rakefile +4 -12
- data/VERSION.yml +2 -2
- data/lib/searchlogic.rb +4 -9
- data/lib/searchlogic/active_record/consistency.rb +1 -1
- data/lib/searchlogic/active_record/named_scopes.rb +9 -0
- data/lib/searchlogic/named_scopes/alias_scope.rb +19 -17
- data/lib/searchlogic/named_scopes/association_conditions.rb +23 -30
- data/lib/searchlogic/named_scopes/association_ordering.rb +9 -11
- data/lib/searchlogic/named_scopes/conditions.rb +45 -36
- data/lib/searchlogic/named_scopes/or_conditions.rb +106 -0
- data/lib/searchlogic/named_scopes/ordering.rb +6 -11
- data/lib/searchlogic/rails_helpers.rb +4 -1
- data/lib/searchlogic/search.rb +22 -8
- data/searchlogic.gemspec +10 -4
- data/spec/named_scopes/association_conditions_spec.rb +1 -0
- data/spec/named_scopes/conditions_spec.rb +3 -0
- data/spec/named_scopes/or_conditions_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -0
- metadata +7 -4
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
== 2.3.0 released 2009-08-22
|
2
|
+
|
3
|
+
* Thanks to laserlemon for support of ranges and arrays in the equals condition.
|
4
|
+
* Added feature to combine condition with 'or'.
|
5
|
+
|
1
6
|
== 2.2.3 released 2009-07-31
|
2
7
|
|
3
8
|
* Fixed bug when an associations named scope joins is a string or an array of strings, the joins we add in automatically should also be a string, not a symbol.
|
data/README.rdoc
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
= Searchlogic
|
2
2
|
|
3
|
-
Searchlogic provides
|
3
|
+
Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive. It helps keep your code DRY, clean, and simple.
|
4
4
|
|
5
5
|
== Helpful links
|
6
6
|
|
7
7
|
* <b>Documentation:</b> http://rdoc.info/projects/binarylogic/searchlogic
|
8
8
|
* <b>Repository:</b> http://github.com/binarylogic/searchlogic/tree/master
|
9
|
-
* <b>
|
9
|
+
* <b>Issues:</b> http://github.com/binarylogic/searchlogic/issues
|
10
10
|
* <b>Google group:</b> http://groups.google.com/group/searchlogic
|
11
11
|
|
12
12
|
<b>Before contacting me directly, please read:</b>
|
13
13
|
|
14
|
-
If you find a bug or a problem please post it
|
14
|
+
If you find a bug or a problem please post it in the issues section. If you need help with something, please use google groups. I check both regularly and get emails when anything happens, so that is the best place to get help. This also benefits other people in the future with the same questions / problems. Thank you.
|
15
15
|
|
16
16
|
== Install & use
|
17
17
|
|
@@ -40,35 +40,48 @@ Instead of explaining what Searchlogic can do, let me show you. Let's start at t
|
|
40
40
|
|
41
41
|
# Searchlogic gives you a bunch of named scopes for free:
|
42
42
|
User.username_equals("bjohnson")
|
43
|
+
User.username_equals(["bjohnson", "thunt"])
|
44
|
+
User.username_equals("a".."b")
|
43
45
|
User.username_does_not_equal("bjohnson")
|
44
46
|
User.username_begins_with("bjohnson")
|
47
|
+
User.username_not_begin_with("bjohnson")
|
45
48
|
User.username_like("bjohnson")
|
49
|
+
User.username_not_like("bjohnson")
|
46
50
|
User.username_ends_with("bjohnson")
|
51
|
+
User.username_not_end_with("bjohnson")
|
47
52
|
User.age_greater_than(20)
|
48
53
|
User.age_greater_than_or_equal_to(20)
|
49
54
|
User.age_less_than(20)
|
50
55
|
User.age_less_than_or_equal_to(20)
|
51
56
|
User.username_null
|
57
|
+
User.username_not_null
|
52
58
|
User.username_blank
|
53
|
-
|
54
|
-
# You can also order by columns
|
55
|
-
User.ascend_by_username
|
56
|
-
User.descend_by_username
|
57
|
-
User.order("ascend_by_username")
|
58
59
|
|
59
60
|
Any named scope Searchlogic creates is dynamic and created via method_missing. Meaning it will only create what you need. Also, keep in mind, these are just named scopes, you can chain them, call methods off of them, etc:
|
60
61
|
|
61
|
-
scope = User.username_like("bjohnson").age_greater_than(20).
|
62
|
+
scope = User.username_like("bjohnson").age_greater_than(20).id_less_than(55)
|
62
63
|
scope.all
|
63
64
|
scope.first
|
64
65
|
scope.count
|
65
66
|
# etc...
|
66
67
|
|
67
|
-
|
68
|
+
For a complete list of conditions please see the constants in Searchlogic::NamedScopes::Conditions.
|
69
|
+
|
70
|
+
== Use condition aliases
|
71
|
+
|
72
|
+
Typing out 'greater_than_or_equal_to' is not fun. Instead Searchlogic provides various aliases for the conditions. For a complete list please see Searchlogic::NamedScopes::Conditions. But they are pretty straightforward:
|
73
|
+
|
74
|
+
User.username_is(10)
|
75
|
+
User.username_eq(10)
|
76
|
+
User.id_lt(10)
|
77
|
+
User.id_lte(10)
|
78
|
+
# etc...
|
79
|
+
|
80
|
+
== Search using scopes in associated classes
|
68
81
|
|
69
|
-
|
82
|
+
This is my favorite part of Searchlogic. You can dynamically call scopes on associated classes and Searchlogic will take care of creating the necessary joins for you. This is REALY nice for keeping your code DRY. The best way to explain this is to show you:
|
70
83
|
|
71
|
-
|
84
|
+
Let's take some basic scopes that Searchlogic provides:
|
72
85
|
|
73
86
|
# We have the following relationships
|
74
87
|
User.has_many :orders
|
@@ -83,7 +96,17 @@ You also get named scopes for any of your associations:
|
|
83
96
|
User.ascend_by_order_total
|
84
97
|
User.descend_by_orders_line_items_price
|
85
98
|
|
86
|
-
|
99
|
+
This is recursive, you can travel through your associations simply by typing it in the name of the method. Again these are just named scopes. You can chain them together, call methods off of them, etc.
|
100
|
+
|
101
|
+
Also, these conditions aren't limited to the scopes Searchlogic provides. You can use your own scopes. Like this:
|
102
|
+
|
103
|
+
LineItem.named_scope :expensive, :conditions => "line_items.price > 500"
|
104
|
+
|
105
|
+
User.orders_line_items_expensive(true)
|
106
|
+
|
107
|
+
As I stated above, Searchlogic will take care of creating the necessary joins for you. This is REALLY nice when trying to keep your code DRY, because if you wanted to use a scope like this in your User model you would have to copy over the conditions. Now you have 2 named scopes that are essentially doing the same thing. Why do that when you can dynamically access that scope using this feature?
|
108
|
+
|
109
|
+
Another thing to note is that the joins created by Searchlogic do NOT use the :include option, making them <em>much</em> faster. Instead they leverage the :joins option, which is great for performance. To prove my point here is a quick benchmark from an application I am working on:
|
87
110
|
|
88
111
|
Benchmark.bm do |x|
|
89
112
|
x.report { 10.times { Event.tickets_id_gt(10).all(:include => :tickets) } }
|
@@ -99,6 +122,45 @@ If you want to use the :include option, just specify it:
|
|
99
122
|
|
100
123
|
Obviously, only do this if you want to actually use the included objects. Including objects into a query can be helpful with performance, especially when solving an N+1 query problem.
|
101
124
|
|
125
|
+
== Order your search
|
126
|
+
|
127
|
+
Just like the various conditions, Searchlogic gives you some very basic scopes for ordering your data:
|
128
|
+
|
129
|
+
User.ascend_by_id
|
130
|
+
User.descend_by_id
|
131
|
+
User.ascend_by_orders_line_items_price
|
132
|
+
# etc...
|
133
|
+
|
134
|
+
== Use any or all
|
135
|
+
|
136
|
+
Every condition you've seen in this readme also has 2 related conditions that you can use. Example:
|
137
|
+
|
138
|
+
User.username_like_any("bjohnson", "thunt") # will return any users that have either of the strings in their username
|
139
|
+
User.username_like_all("bjohnson", "thunt") # will return any users that have all of the strings in their username
|
140
|
+
User.username_like_any(["bjohnson", "thunt"]) # also accepts an array
|
141
|
+
|
142
|
+
This is great for checkbox filters, etc. Where you can pass an array right from your form to this condition.
|
143
|
+
|
144
|
+
== Combine scopes with 'OR'
|
145
|
+
|
146
|
+
In the same fashion that Searchlogic provides a tool for accessing scopes in associated classes, it also provides a tool for combining scopes with 'OR'. As we all know, when scopes are combined they are joined with 'AND', but sometimes you need to combine scopes with 'OR'. Searchlogic solves this problem:
|
147
|
+
|
148
|
+
User.username_or_first_name_like("ben")
|
149
|
+
=> "username LIKE '%ben%' OR first_name like'%ben%'"
|
150
|
+
|
151
|
+
User.id_or_age_lt_or_username_or_first_name_begins_with(10)
|
152
|
+
=> "id < 10 OR age < 10 OR username LIKE 'ben%' OR first_name like'ben%'"
|
153
|
+
|
154
|
+
Notice you don't have to specify the explicit condition (like, gt, lt, begins with, etc.). You just need to eventually specify it. If you specify a column it will just use the next condition specified. So instead of:
|
155
|
+
|
156
|
+
User.username_like_or_first_name_like("ben")
|
157
|
+
|
158
|
+
You can do:
|
159
|
+
|
160
|
+
User.username_or_first_name_like("ben")
|
161
|
+
|
162
|
+
Again, these just map to named scopes. Use Searchlogic's dynamic scopes, use scopes on associations, use your own custom scopes. As long as it maps to a named scope it will join the conditions with 'OR'. There are no limitations.
|
163
|
+
|
102
164
|
== Make searching and ordering data in your application trivial
|
103
165
|
|
104
166
|
The above is great, but what about tying all of this in with a search form in your application? What would be really nice is if we could use an object that represented a single search. Like this...
|
@@ -178,16 +240,6 @@ Now just throw it in your form:
|
|
178
240
|
|
179
241
|
This really allows Searchlogic to extend beyond what it provides internally. If Searchlogic doesn't provide a named scope for that crazy edge case that you need, just create your own named scope and use it. The sky is the limit.
|
180
242
|
|
181
|
-
== Use any or all
|
182
|
-
|
183
|
-
Every condition you've seen in this readme also has 2 related conditions that you can use. Example:
|
184
|
-
|
185
|
-
User.username_like_any("bjohnson", "thunt") # will return any users that have either of the strings in their username
|
186
|
-
User.username_like_all("bjohnson", "thunt") # will return any users that have all of the strings in their username
|
187
|
-
User.username_like_any(["bjohnson", "thunt"]) # also accepts an array
|
188
|
-
|
189
|
-
This is great for checkbox filters, etc. Where you can pass an array right from your form to this condition.
|
190
|
-
|
191
243
|
== Pagination (leverage will_paginate)
|
192
244
|
|
193
245
|
Instead of recreating the wheel with pagination, Searchlogic works great with will_paginate. All that Searchlogic is doing is creating named scopes, and will_paginate works great with named scopes:
|
data/Rakefile
CHANGED
@@ -5,14 +5,15 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "searchlogic"
|
8
|
-
gem.summary = "Searchlogic provides
|
9
|
-
gem.description = "Searchlogic provides
|
8
|
+
gem.summary = "Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive."
|
9
|
+
gem.description = "Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive."
|
10
10
|
gem.email = "bjohnson@binarylogic.com"
|
11
11
|
gem.homepage = "http://github.com/binarylogic/searchlogic"
|
12
12
|
gem.authors = ["Ben Johnson of Binary Logic"]
|
13
13
|
gem.rubyforge_project = "searchlogic"
|
14
14
|
gem.add_dependency "activerecord", ">= 2.0.0"
|
15
15
|
end
|
16
|
+
Jeweler::RubyforgeTasks.new
|
16
17
|
rescue LoadError
|
17
18
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
19
|
end
|
@@ -29,15 +30,6 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
29
30
|
spec.rcov = true
|
30
31
|
end
|
31
32
|
|
33
|
+
task :spec => :check_dependencies
|
32
34
|
|
33
35
|
task :default => :spec
|
34
|
-
|
35
|
-
begin
|
36
|
-
require 'rake/contrib/sshpublisher'
|
37
|
-
namespace :rubyforge do
|
38
|
-
desc "Release gem to RubyForge"
|
39
|
-
task :release => ["rubyforge:release:gem"]
|
40
|
-
end
|
41
|
-
rescue LoadError
|
42
|
-
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
43
|
-
end
|
data/VERSION.yml
CHANGED
data/lib/searchlogic.rb
CHANGED
@@ -7,6 +7,7 @@ require "searchlogic/named_scopes/ordering"
|
|
7
7
|
require "searchlogic/named_scopes/association_conditions"
|
8
8
|
require "searchlogic/named_scopes/association_ordering"
|
9
9
|
require "searchlogic/named_scopes/alias_scope"
|
10
|
+
require "searchlogic/named_scopes/or_conditions"
|
10
11
|
require "searchlogic/search"
|
11
12
|
|
12
13
|
Proc.send(:include, Searchlogic::CoreExt::Proc)
|
@@ -14,29 +15,23 @@ Object.send(:include, Searchlogic::CoreExt::Object)
|
|
14
15
|
|
15
16
|
module ActiveRecord # :nodoc: all
|
16
17
|
class Base
|
17
|
-
class << self
|
18
|
-
include Searchlogic::ActiveRecord::Consistency
|
19
|
-
end
|
18
|
+
class << self; include Searchlogic::ActiveRecord::Consistency; end
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
22
|
ActiveRecord::Base.extend(Searchlogic::ActiveRecord::NamedScopes)
|
24
|
-
|
25
23
|
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Conditions)
|
26
24
|
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationConditions)
|
27
25
|
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationOrdering)
|
28
26
|
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
|
29
27
|
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AliasScope)
|
28
|
+
ActiveRecord::Base.extend(Searchlogic::NamedScopes::OrConditions)
|
30
29
|
ActiveRecord::Base.extend(Searchlogic::Search::Implementation)
|
31
30
|
|
32
31
|
# Try to use the search method, if it's available. Thinking sphinx and other plugins
|
33
32
|
# like to use that method as well.
|
34
33
|
if !ActiveRecord::Base.respond_to?(:search)
|
35
|
-
ActiveRecord::Base.class_eval
|
36
|
-
class << self
|
37
|
-
alias_method :search, :searchlogic
|
38
|
-
end
|
39
|
-
end
|
34
|
+
ActiveRecord::Base.class_eval { class << self; alias_method :search, :searchlogic; end }
|
40
35
|
end
|
41
36
|
|
42
37
|
if defined?(ActionController)
|
@@ -8,7 +8,7 @@ module Searchlogic
|
|
8
8
|
alias_method_chain :merge_joins, :searchlogic
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
# In AR multiple joins are sometimes in a single join query, and other times they
|
13
13
|
# are not. The merge_joins method in AR should account for this, but it doesn't.
|
14
14
|
# This fixes that problem. This way there is one join per string, which allows
|
@@ -43,9 +43,18 @@ module Searchlogic
|
|
43
43
|
# you to use when writing your own named scopes. This way you know for sure
|
44
44
|
# that duplicate joins will be removed when chaining scopes together that
|
45
45
|
# use the same join.
|
46
|
+
#
|
47
|
+
# Also, don't worry about breaking up the joins or retriving multiple joins.
|
48
|
+
# ActiveRecord will remove dupilicate joins and Searchlogic assists ActiveRecord in
|
49
|
+
# breaking up your joins so that they are unique.
|
46
50
|
def inner_joins(association_name)
|
47
51
|
::ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, association_name, nil).join_associations.collect { |assoc| assoc.association_join }
|
48
52
|
end
|
53
|
+
|
54
|
+
# See inner_joins, except this creates LEFT OUTER joins.
|
55
|
+
def left_outer_joins(association_name)
|
56
|
+
::ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, association_name, nil).join_associations.collect { |assoc| assoc.association_join }
|
57
|
+
end
|
49
58
|
end
|
50
59
|
end
|
51
60
|
end
|
@@ -1,20 +1,11 @@
|
|
1
1
|
module Searchlogic
|
2
2
|
module NamedScopes
|
3
3
|
# Adds the ability to create alias scopes that allow you to alias a named
|
4
|
-
# scope or create a named scope procedure
|
5
|
-
#
|
4
|
+
# scope or create a named scope procedure. See the alias_scope method for a more
|
5
|
+
# detailed explanation.
|
6
6
|
module AliasScope
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# You can not pass the name of a class method and expect that to be called. In some instances
|
10
|
-
# you might create a class method that essentially aliases a named scope or represents a
|
11
|
-
# named scope procedure. Ex:
|
12
|
-
#
|
13
|
-
# User.named_scope :teenager, :conditions => ["age >= ? AND age <= ?", 13, 19]
|
14
|
-
#
|
15
|
-
# This is obviously a very basic example, but there is logic that is duplicated here. For
|
16
|
-
# more complicated named scopes this might make more sense, but to make my point you could
|
17
|
-
# do something like this instead
|
7
|
+
# In some instances you might create a class method that essentially aliases a named scope
|
8
|
+
# or represents a named scope procedure. Ex:
|
18
9
|
#
|
19
10
|
# class User
|
20
11
|
# def teenager
|
@@ -22,13 +13,24 @@ module Searchlogic
|
|
22
13
|
# end
|
23
14
|
# end
|
24
15
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
16
|
+
# This is obviously a very basic example, but notice how we are utilizing already existing named
|
17
|
+
# scopes so that we do not have to repeat ourself. This method makes a lot more sense when you are
|
18
|
+
# dealing with complicated named scope.
|
19
|
+
#
|
20
|
+
# There is a problem though. What if you want to use this in your controller's via the 'search' method:
|
21
|
+
#
|
22
|
+
# User.search(:teenager => true)
|
23
|
+
#
|
24
|
+
# You would expect that to work, but how does Searchlogic::Search tell the difference between your
|
25
|
+
# 'teenager' method and the 'destroy_all' method. It can't, there is no way to tell unless we actually
|
26
|
+
# call the method, which we obviously can not do.
|
27
|
+
#
|
28
|
+
# The being said, we need a way to tell searchlogic that this is method is safe. Here's how you do that:
|
28
29
|
#
|
29
30
|
# User.alias_scope :teenager, lambda { age_gte(13).age_lte(19) }
|
30
31
|
#
|
31
|
-
#
|
32
|
+
# This feels better, it feels like our other scopes, and it provides a way to tell Searchlogic that this
|
33
|
+
# is a safe method.
|
32
34
|
def alias_scope(name, options = nil)
|
33
35
|
alias_scopes[name.to_sym] = options
|
34
36
|
(class << self; self end).instance_eval do
|
@@ -1,21 +1,11 @@
|
|
1
1
|
module Searchlogic
|
2
2
|
module NamedScopes
|
3
|
-
# Handles dynamically creating named scopes for associations.
|
3
|
+
# Handles dynamically creating named scopes for associations. See the README for a detailed explanation.
|
4
4
|
module AssociationConditions
|
5
5
|
def condition?(name) # :nodoc:
|
6
6
|
super || association_condition?(name)
|
7
7
|
end
|
8
8
|
|
9
|
-
def primary_condition_name(name) # :nodoc:
|
10
|
-
if result = super
|
11
|
-
result
|
12
|
-
elsif association_condition?(name)
|
13
|
-
name.to_sym
|
14
|
-
else
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
9
|
private
|
20
10
|
def association_condition?(name)
|
21
11
|
!association_condition_details(name).nil?
|
@@ -31,7 +21,7 @@ module Searchlogic
|
|
31
21
|
end
|
32
22
|
|
33
23
|
def association_condition_details(name)
|
34
|
-
assocs = reflect_on_all_associations.reject { |assoc| assoc.options[:polymorphic] }
|
24
|
+
assocs = reflect_on_all_associations.reject { |assoc| assoc.options[:polymorphic] }.sort { |a, b| b.name.to_s.size <=> a.name.to_s.size }
|
35
25
|
return nil if assocs.empty?
|
36
26
|
|
37
27
|
if name.to_s =~ /^(#{assocs.collect(&:name).join("|")})_(\w+)$/
|
@@ -64,23 +54,7 @@ module Searchlogic
|
|
64
54
|
prepare_named_scope_options(options, association)
|
65
55
|
options
|
66
56
|
else
|
67
|
-
|
68
|
-
# and pass those onto the named scope. We can't use proxy_options because that returns the
|
69
|
-
# result after a value has been passed.
|
70
|
-
proc_args = []
|
71
|
-
if arity > 0
|
72
|
-
arity.times { |i| proc_args << "arg#{i}"}
|
73
|
-
else
|
74
|
-
positive_arity = arity * -1
|
75
|
-
positive_arity.times do |i|
|
76
|
-
if i == (positive_arity - 1)
|
77
|
-
proc_args << "*arg#{i}"
|
78
|
-
else
|
79
|
-
proc_args << "arg#{i}"
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
57
|
+
proc_args = arity_args(arity)
|
84
58
|
arg_type = (scope_options.respond_to?(:searchlogic_arg_type) && scope_options.searchlogic_arg_type) || :string
|
85
59
|
|
86
60
|
eval <<-"end_eval"
|
@@ -94,8 +68,27 @@ module Searchlogic
|
|
94
68
|
end
|
95
69
|
end
|
96
70
|
|
71
|
+
# Used to match the new scopes parameters to the underlying scope. This way we can disguise the
|
72
|
+
# new scope as best as possible instead of taking the easy way out and using *args.
|
73
|
+
def arity_args(arity)
|
74
|
+
args = []
|
75
|
+
if arity > 0
|
76
|
+
arity.times { |i| args << "arg#{i}" }
|
77
|
+
else
|
78
|
+
positive_arity = arity * -1
|
79
|
+
positive_arity.times do |i|
|
80
|
+
if i == (positive_arity - 1)
|
81
|
+
args << "*arg#{i}"
|
82
|
+
else
|
83
|
+
args << "arg#{i}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
args
|
88
|
+
end
|
89
|
+
|
97
90
|
def prepare_named_scope_options(options, association)
|
98
|
-
options.delete(:readonly)
|
91
|
+
options.delete(:readonly) # AR likes to set :readonly to true when using the :joins option, we don't want that
|
99
92
|
|
100
93
|
if options[:joins].is_a?(String) || array_of_strings?(options[:joins])
|
101
94
|
options[:joins] = [inner_joins(association.name), options[:joins]].flatten
|
@@ -1,21 +1,19 @@
|
|
1
1
|
module Searchlogic
|
2
2
|
module NamedScopes
|
3
|
-
# Handles dynamically creating named scopes for associations
|
3
|
+
# Handles dynamically creating order named scopes for associations:
|
4
|
+
#
|
5
|
+
# User.has_many :orders
|
6
|
+
# Order.has_many :line_items
|
7
|
+
# LineItem
|
8
|
+
#
|
9
|
+
# User.ascend_by_orders_line_items_id
|
10
|
+
#
|
11
|
+
# See the README for a more detailed explanation.
|
4
12
|
module AssociationOrdering
|
5
13
|
def condition?(name) # :nodoc:
|
6
14
|
super || association_ordering_condition?(name)
|
7
15
|
end
|
8
16
|
|
9
|
-
def primary_condition_name(name) # :nodoc
|
10
|
-
if result = super
|
11
|
-
result
|
12
|
-
elsif association_ordering_condition?(name)
|
13
|
-
name.to_sym
|
14
|
-
else
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
17
|
private
|
20
18
|
def association_ordering_condition?(name)
|
21
19
|
!association_ordering_condition_details(name).nil?
|
@@ -1,6 +1,13 @@
|
|
1
1
|
module Searchlogic
|
2
2
|
module NamedScopes
|
3
|
-
# Handles dynamically creating named scopes for columns.
|
3
|
+
# Handles dynamically creating named scopes for columns. It allows you to do things like:
|
4
|
+
#
|
5
|
+
# User.first_name_like("ben")
|
6
|
+
# User.id_lt(10)
|
7
|
+
#
|
8
|
+
# Notice the constants in this class, they define which conditions Searchlogic provides.
|
9
|
+
#
|
10
|
+
# See the README for a more detailed explanation.
|
4
11
|
module Conditions
|
5
12
|
COMPARISON_CONDITIONS = {
|
6
13
|
:equals => [:is, :eq],
|
@@ -40,31 +47,6 @@ module Searchlogic
|
|
40
47
|
PRIMARY_CONDITIONS = CONDITIONS.keys
|
41
48
|
ALIAS_CONDITIONS = CONDITIONS.values.flatten
|
42
49
|
|
43
|
-
# Returns the primary condition for the given alias. Ex:
|
44
|
-
#
|
45
|
-
# primary_condition(:gt) => :greater_than
|
46
|
-
def primary_condition(alias_condition)
|
47
|
-
CONDITIONS.find { |k, v| k == alias_condition.to_sym || v.include?(alias_condition.to_sym) }.first
|
48
|
-
end
|
49
|
-
|
50
|
-
# Returns the primary name for any condition on a column. You can pass it
|
51
|
-
# a primary condition, alias condition, etc, and it will return the proper
|
52
|
-
# primary condition name. This helps simply logic throughout Searchlogic. Ex:
|
53
|
-
#
|
54
|
-
# primary_condition_name(:id_gt) => :id_greater_than
|
55
|
-
# primary_condition_name(:id_greater_than) => :id_greater_than
|
56
|
-
def primary_condition_name(name)
|
57
|
-
if details = condition_details(name)
|
58
|
-
if PRIMARY_CONDITIONS.include?(name.to_sym)
|
59
|
-
name
|
60
|
-
else
|
61
|
-
"#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
|
62
|
-
end
|
63
|
-
else
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
50
|
# Is the name of the method a valid condition that can be dynamically created?
|
69
51
|
def condition?(name)
|
70
52
|
local_condition?(name)
|
@@ -106,7 +88,7 @@ module Searchlogic
|
|
106
88
|
|
107
89
|
scope_options = case condition.to_s
|
108
90
|
when /^equals/
|
109
|
-
scope_options(condition, column_type, "#{table_name}.#{column}
|
91
|
+
scope_options(condition, column_type, lambda { |a| attribute_condition("#{table_name}.#{column}", a) })
|
110
92
|
when /^does_not_equal/
|
111
93
|
scope_options(condition, column_type, "#{table_name}.#{column} != ?")
|
112
94
|
when /^less_than_or_equal_to/
|
@@ -148,20 +130,22 @@ module Searchlogic
|
|
148
130
|
when /_(any|all)$/
|
149
131
|
searchlogic_lambda(column_type) { |*values|
|
150
132
|
return {} if values.empty?
|
151
|
-
values = values.flatten
|
152
133
|
|
153
|
-
|
154
|
-
if value_modifier.nil?
|
155
|
-
values_to_sub = values
|
156
|
-
else
|
157
|
-
values_to_sub = values.collect { |value| value_with_modifier(value, value_modifier) }
|
158
|
-
end
|
134
|
+
values.collect! { |value| value_with_modifier(value, value_modifier) }
|
159
135
|
|
160
136
|
join = $1 == "any" ? " OR " : " AND "
|
161
|
-
|
137
|
+
scope_sql = values.collect { |value| sql.is_a?(Proc) ? sql.call(value) : sql }.join(join)
|
138
|
+
|
139
|
+
{:conditions => [scope_sql, *expand_range_bind_variables(values)]}
|
162
140
|
}
|
163
141
|
else
|
164
|
-
searchlogic_lambda(column_type) { |
|
142
|
+
searchlogic_lambda(column_type) { |*values|
|
143
|
+
values.collect! { |value| value_with_modifier(value, value_modifier) }
|
144
|
+
|
145
|
+
scope_sql = sql.is_a?(Proc) ? sql.call(*values) : sql
|
146
|
+
|
147
|
+
{:conditions => [scope_sql, *expand_range_bind_variables(values)]}
|
148
|
+
}
|
165
149
|
end
|
166
150
|
end
|
167
151
|
|
@@ -185,6 +169,31 @@ module Searchlogic
|
|
185
169
|
send(primary_name, *args) # go back to method_missing and make sure we create the method
|
186
170
|
(class << self; self; end).class_eval { alias_method alias_name, primary_name }
|
187
171
|
end
|
172
|
+
|
173
|
+
# Returns the primary condition for the given alias. Ex:
|
174
|
+
#
|
175
|
+
# primary_condition(:gt) => :greater_than
|
176
|
+
def primary_condition(alias_condition)
|
177
|
+
CONDITIONS.find { |k, v| k == alias_condition.to_sym || v.include?(alias_condition.to_sym) }.first
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns the primary name for any condition on a column. You can pass it
|
181
|
+
# a primary condition, alias condition, etc, and it will return the proper
|
182
|
+
# primary condition name. This helps simply logic throughout Searchlogic. Ex:
|
183
|
+
#
|
184
|
+
# primary_condition_name(:id_gt) => :id_greater_than
|
185
|
+
# primary_condition_name(:id_greater_than) => :id_greater_than
|
186
|
+
def primary_condition_name(name)
|
187
|
+
if details = condition_details(name)
|
188
|
+
if PRIMARY_CONDITIONS.include?(name.to_sym)
|
189
|
+
name
|
190
|
+
else
|
191
|
+
"#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
|
192
|
+
end
|
193
|
+
else
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
188
197
|
end
|
189
198
|
end
|
190
199
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
module NamedScopes
|
3
|
+
# Handles dynamically creating named scopes for 'OR' conditions. Please see the README for a more
|
4
|
+
# detailed explanation.
|
5
|
+
module OrConditions
|
6
|
+
class NoConditionSpecifiedError < StandardError; end
|
7
|
+
class UnknownConditionError < StandardError; end
|
8
|
+
|
9
|
+
def condition?(name) # :nodoc:
|
10
|
+
super || or_condition?(name)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def or_condition?(name)
|
15
|
+
!or_conditions(name).nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *args, &block)
|
19
|
+
if conditions = or_conditions(name)
|
20
|
+
create_or_condition(conditions, args)
|
21
|
+
(class << self; self; end).class_eval { alias_method name, conditions.join("_or_") } if !respond_to?(name)
|
22
|
+
send(name, *args)
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def or_conditions(name)
|
29
|
+
# First determine if we should even work on the name, we want to be as quick as possible
|
30
|
+
# with this.
|
31
|
+
if (parts = split_or_condition(name)).size > 1
|
32
|
+
conditions = interpolate_or_conditions(parts)
|
33
|
+
if conditions.any?
|
34
|
+
conditions
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def split_or_condition(name)
|
42
|
+
parts = name.to_s.split("_or_")
|
43
|
+
new_parts = []
|
44
|
+
parts.each do |part|
|
45
|
+
if part =~ /^equal_to(_any|_all)?$/
|
46
|
+
new_parts << new_parts.pop + "_or_equal_to"
|
47
|
+
else
|
48
|
+
new_parts << part
|
49
|
+
end
|
50
|
+
end
|
51
|
+
new_parts
|
52
|
+
end
|
53
|
+
|
54
|
+
# The purpose of this method is to convert the method name parts into actual condition names.
|
55
|
+
#
|
56
|
+
# Example:
|
57
|
+
#
|
58
|
+
# ["first_name", "last_name_like"]
|
59
|
+
# => ["first_name_like", "last_name_like"]
|
60
|
+
#
|
61
|
+
# ["id_gt", "first_name_begins_with", "last_name", "middle_name_like"]
|
62
|
+
# => ["id_gt", "first_name_begins_with", "last_name_like", "middle_name_like"]
|
63
|
+
#
|
64
|
+
# Basically if a column is specified without a condition the next condition in the list
|
65
|
+
# is what will be used. Once we are able to get a consistent list of conditions we can easily
|
66
|
+
# create a scope for it.
|
67
|
+
def interpolate_or_conditions(parts)
|
68
|
+
conditions = []
|
69
|
+
last_condition = nil
|
70
|
+
|
71
|
+
parts.reverse.each do |part|
|
72
|
+
if details = condition_details(part)
|
73
|
+
# We are a searchlogic defined scope
|
74
|
+
conditions << "#{details[:column]}_#{details[:condition]}"
|
75
|
+
last_condition = details[:condition]
|
76
|
+
elsif details = association_condition_details(part)
|
77
|
+
# pending, need to find the last condition
|
78
|
+
elsif local_condition?(part)
|
79
|
+
# We are a custom scope
|
80
|
+
conditions << part
|
81
|
+
elsif column_names.include?(part)
|
82
|
+
# we are a column, use the last condition
|
83
|
+
if last_condition.nil?
|
84
|
+
raise NoConditionSpecifiedError.new("The '#{part}' column doesn't know which condition to use, if you use an exact column " +
|
85
|
+
"name you need to specify a condition sometime after (ex: id_or_created_at_lt), where id would use the 'lt' condition.")
|
86
|
+
end
|
87
|
+
|
88
|
+
conditions << "#{part}_#{last_condition}"
|
89
|
+
else
|
90
|
+
raise UnknownConditionError.new("The condition '#{part}' is not a valid condition, we could not find any scopes that match this.")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
conditions
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_or_condition(scopes, args)
|
98
|
+
named_scope scopes.join("_or_"), lambda { |*args|
|
99
|
+
scopes_options = scopes.collect { |scope| send(scope, *args).proxy_options }
|
100
|
+
conditions = scopes_options.reject { |o| o[:conditions].nil? }.collect { |o| sanitize_sql(o[:conditions]) }
|
101
|
+
scopes.inject(scoped({})) { |scope, scope_name| scope.send(scope_name, *args) }.scope(:find).merge(:conditions => "(" + conditions.join(") OR (") + ")")
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -1,21 +1,16 @@
|
|
1
1
|
module Searchlogic
|
2
2
|
module NamedScopes
|
3
|
-
# Handles dynamically creating named scopes for
|
3
|
+
# Handles dynamically creating named scopes for ordering by columns. Example:
|
4
|
+
#
|
5
|
+
# User.ascend_by_id
|
6
|
+
# User.descend_by_username
|
7
|
+
#
|
8
|
+
# See the README for a more detailed explanation.
|
4
9
|
module Ordering
|
5
10
|
def condition?(name) # :nodoc:
|
6
11
|
super || ordering_condition?(name)
|
7
12
|
end
|
8
13
|
|
9
|
-
def primary_condition_name(name) # :nodoc
|
10
|
-
if result = super
|
11
|
-
result
|
12
|
-
elsif ordering_condition?(name)
|
13
|
-
name.to_sym
|
14
|
-
else
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
14
|
private
|
20
15
|
def ordering_condition?(name) # :nodoc:
|
21
16
|
!ordering_condition_details(name).nil?
|
@@ -21,7 +21,10 @@ module Searchlogic
|
|
21
21
|
# * <tt>:params_scope</tt> - the name of the params key to scope the order condition by, defaults to :search
|
22
22
|
def order(search, options = {}, html_options = {})
|
23
23
|
options[:params_scope] ||= :search
|
24
|
-
options[:as]
|
24
|
+
if !options[:as]
|
25
|
+
id = options[:by].to_s.downcase == "id"
|
26
|
+
options[:as] = id ? options[:by].to_s.upcase : options[:by].to_s.humanize
|
27
|
+
end
|
25
28
|
options[:ascend_scope] ||= "ascend_by_#{options[:by]}"
|
26
29
|
options[:descend_scope] ||= "descend_by_#{options[:by]}"
|
27
30
|
ascending = search.order.to_s == options[:ascend_scope]
|
data/lib/searchlogic/search.rb
CHANGED
@@ -79,20 +79,21 @@ module Searchlogic
|
|
79
79
|
|
80
80
|
private
|
81
81
|
def method_missing(name, *args, &block)
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
condition_name = condition_name(name)
|
83
|
+
scope_name = scope_name(condition_name)
|
84
|
+
|
85
|
+
if setter?(name)
|
85
86
|
if scope?(scope_name)
|
86
|
-
conditions[
|
87
|
+
conditions[condition_name] = type_cast(args.first, cast_type(scope_name))
|
87
88
|
else
|
88
|
-
raise UnknownConditionError.new(
|
89
|
+
raise UnknownConditionError.new(condition_name)
|
89
90
|
end
|
90
|
-
elsif scope?(
|
91
|
+
elsif scope?(scope_name)
|
91
92
|
if args.size > 0
|
92
|
-
send("#{
|
93
|
+
send("#{condition_name}=", *args)
|
93
94
|
self
|
94
95
|
else
|
95
|
-
conditions[
|
96
|
+
conditions[condition_name]
|
96
97
|
end
|
97
98
|
else
|
98
99
|
scope = conditions.inject(klass.scoped(current_scope)) do |scope, condition|
|
@@ -119,6 +120,19 @@ module Searchlogic
|
|
119
120
|
klass.column_names.include?(scope_name.to_s) ? "#{scope_name}_equals".to_sym : scope_name.to_sym
|
120
121
|
end
|
121
122
|
|
123
|
+
def setter?(name)
|
124
|
+
!(name.to_s =~ /=$/).nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
def condition_name(name)
|
128
|
+
condition = name.to_s.match(/(\w+)=?$/)[1]
|
129
|
+
condition ? condition.to_sym : nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def scope_name(condition_name)
|
133
|
+
condition_name && normalize_scope_name(condition_name)
|
134
|
+
end
|
135
|
+
|
122
136
|
def scope?(scope_name)
|
123
137
|
klass.scopes.key?(scope_name) || klass.condition?(scope_name)
|
124
138
|
end
|
data/searchlogic.gemspec
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
1
4
|
# -*- encoding: utf-8 -*-
|
2
5
|
|
3
6
|
Gem::Specification.new do |s|
|
4
7
|
s.name = %q{searchlogic}
|
5
|
-
s.version = "2.
|
8
|
+
s.version = "2.3.0"
|
6
9
|
|
7
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
11
|
s.authors = ["Ben Johnson of Binary Logic"]
|
9
|
-
s.date = %q{2009-
|
10
|
-
s.description = %q{Searchlogic provides
|
12
|
+
s.date = %q{2009-08-22}
|
13
|
+
s.description = %q{Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive.}
|
11
14
|
s.email = %q{bjohnson@binarylogic.com}
|
12
15
|
s.extra_rdoc_files = [
|
13
16
|
"LICENSE",
|
@@ -30,6 +33,7 @@ Gem::Specification.new do |s|
|
|
30
33
|
"lib/searchlogic/named_scopes/association_conditions.rb",
|
31
34
|
"lib/searchlogic/named_scopes/association_ordering.rb",
|
32
35
|
"lib/searchlogic/named_scopes/conditions.rb",
|
36
|
+
"lib/searchlogic/named_scopes/or_conditions.rb",
|
33
37
|
"lib/searchlogic/named_scopes/ordering.rb",
|
34
38
|
"lib/searchlogic/rails_helpers.rb",
|
35
39
|
"lib/searchlogic/search.rb",
|
@@ -41,6 +45,7 @@ Gem::Specification.new do |s|
|
|
41
45
|
"spec/named_scopes/association_conditions_spec.rb",
|
42
46
|
"spec/named_scopes/association_ordering_spec.rb",
|
43
47
|
"spec/named_scopes/conditions_spec.rb",
|
48
|
+
"spec/named_scopes/or_conditions_spec.rb",
|
44
49
|
"spec/named_scopes/ordering_spec.rb",
|
45
50
|
"spec/search_spec.rb",
|
46
51
|
"spec/spec_helper.rb"
|
@@ -50,7 +55,7 @@ Gem::Specification.new do |s|
|
|
50
55
|
s.require_paths = ["lib"]
|
51
56
|
s.rubyforge_project = %q{searchlogic}
|
52
57
|
s.rubygems_version = %q{1.3.5}
|
53
|
-
s.summary = %q{Searchlogic provides
|
58
|
+
s.summary = %q{Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive.}
|
54
59
|
s.test_files = [
|
55
60
|
"spec/core_ext/object_spec.rb",
|
56
61
|
"spec/core_ext/proc_spec.rb",
|
@@ -58,6 +63,7 @@ Gem::Specification.new do |s|
|
|
58
63
|
"spec/named_scopes/association_conditions_spec.rb",
|
59
64
|
"spec/named_scopes/association_ordering_spec.rb",
|
60
65
|
"spec/named_scopes/conditions_spec.rb",
|
66
|
+
"spec/named_scopes/or_conditions_spec.rb",
|
61
67
|
"spec/named_scopes/ordering_spec.rb",
|
62
68
|
"spec/search_spec.rb",
|
63
69
|
"spec/spec_helper.rb"
|
@@ -15,6 +15,7 @@ describe "Association Conditions" do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should allow the use of deep foreign pre-existing named scopes" do
|
18
|
+
pending
|
18
19
|
Order.named_scope :big_id, :conditions => "orders.id > 100"
|
19
20
|
Company.users_orders_big_id.proxy_options.should == Order.big_id.proxy_options.merge(:joins => {:users => :orders})
|
20
21
|
end
|
@@ -15,6 +15,9 @@ describe "Conditions" do
|
|
15
15
|
it "should have equals" do
|
16
16
|
(5..7).each { |age| User.create(:age => age) }
|
17
17
|
User.age_equals(6).all.should == User.find_all_by_age(6)
|
18
|
+
User.age_equals(nil).all.should == User.find_all_by_age(nil)
|
19
|
+
User.age_equals(5..6).all.should == User.find_all_by_age(5..6)
|
20
|
+
User.age_equals([5, 7]).all.should == User.find_all_by_age([5, 7])
|
18
21
|
end
|
19
22
|
|
20
23
|
it "should have does not equal" do
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
|
3
|
+
describe "Or conditions" do
|
4
|
+
it "should match username or name" do
|
5
|
+
User.username_or_name_like("ben").proxy_options.should == {:conditions => "(users.name LIKE '%ben%') OR (users.username LIKE '%ben%')"}
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should use the specified condition" do
|
9
|
+
User.username_begins_with_or_name_like("ben").proxy_options.should == {:conditions => "(users.name LIKE '%ben%') OR (users.username LIKE 'ben%')"}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should use the last specified condition" do
|
13
|
+
User.username_or_name_like_or_id_or_age_lt(10).proxy_options.should == {:conditions => "(users.age < 10) OR (users.id < 10) OR (users.name LIKE '%10%') OR (users.username LIKE '%10%')"}
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should raise an error on unknown conditions" do
|
17
|
+
lambda { User.usernme_begins_with_or_name_like("ben") }.should raise_error(Searchlogic::NamedScopes::OrConditions::UnknownConditionError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should play nice with other scopes" do
|
21
|
+
User.username_begins_with("ben").id_gt(10).age_not_nil.username_or_name_ends_with("ben").scope(:find).should ==
|
22
|
+
{:conditions => "((users.name LIKE '%ben') OR (users.username LIKE '%ben')) AND ((users.age IS NOT NULL) AND ((users.id > 10) AND (users.username LIKE 'ben%')))"}
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -71,6 +71,7 @@ Spec::Runner.configure do |config|
|
|
71
71
|
class User < ActiveRecord::Base
|
72
72
|
belongs_to :company, :counter_cache => true
|
73
73
|
has_many :orders, :dependent => :destroy
|
74
|
+
has_many :orders_big, :class_name => 'Order', :conditions => 'total > 100'
|
74
75
|
end
|
75
76
|
|
76
77
|
class Order < ActiveRecord::Base
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchlogic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-22 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 2.0.0
|
24
24
|
version:
|
25
|
-
description: Searchlogic provides
|
25
|
+
description: Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive.
|
26
26
|
email: bjohnson@binarylogic.com
|
27
27
|
executables: []
|
28
28
|
|
@@ -48,6 +48,7 @@ files:
|
|
48
48
|
- lib/searchlogic/named_scopes/association_conditions.rb
|
49
49
|
- lib/searchlogic/named_scopes/association_ordering.rb
|
50
50
|
- lib/searchlogic/named_scopes/conditions.rb
|
51
|
+
- lib/searchlogic/named_scopes/or_conditions.rb
|
51
52
|
- lib/searchlogic/named_scopes/ordering.rb
|
52
53
|
- lib/searchlogic/rails_helpers.rb
|
53
54
|
- lib/searchlogic/search.rb
|
@@ -59,6 +60,7 @@ files:
|
|
59
60
|
- spec/named_scopes/association_conditions_spec.rb
|
60
61
|
- spec/named_scopes/association_ordering_spec.rb
|
61
62
|
- spec/named_scopes/conditions_spec.rb
|
63
|
+
- spec/named_scopes/or_conditions_spec.rb
|
62
64
|
- spec/named_scopes/ordering_spec.rb
|
63
65
|
- spec/search_spec.rb
|
64
66
|
- spec/spec_helper.rb
|
@@ -89,7 +91,7 @@ rubyforge_project: searchlogic
|
|
89
91
|
rubygems_version: 1.3.5
|
90
92
|
signing_key:
|
91
93
|
specification_version: 3
|
92
|
-
summary: Searchlogic provides
|
94
|
+
summary: Searchlogic provides tools that make using ActiveRecord named scopes easier and less repetitive.
|
93
95
|
test_files:
|
94
96
|
- spec/core_ext/object_spec.rb
|
95
97
|
- spec/core_ext/proc_spec.rb
|
@@ -97,6 +99,7 @@ test_files:
|
|
97
99
|
- spec/named_scopes/association_conditions_spec.rb
|
98
100
|
- spec/named_scopes/association_ordering_spec.rb
|
99
101
|
- spec/named_scopes/conditions_spec.rb
|
102
|
+
- spec/named_scopes/or_conditions_spec.rb
|
100
103
|
- spec/named_scopes/ordering_spec.rb
|
101
104
|
- spec/search_spec.rb
|
102
105
|
- spec/spec_helper.rb
|