searchlogic 1.5.3
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.rdoc +228 -0
- data/MIT-LICENSE +20 -0
- data/Manifest +123 -0
- data/README.rdoc +383 -0
- data/Rakefile +15 -0
- data/TODO.rdoc +6 -0
- data/examples/README.rdoc +4 -0
- data/init.rb +1 -0
- data/lib/searchlogic.rb +89 -0
- data/lib/searchlogic/active_record/associations.rb +52 -0
- data/lib/searchlogic/active_record/base.rb +218 -0
- data/lib/searchlogic/active_record/connection_adapters/mysql_adapter.rb +172 -0
- data/lib/searchlogic/active_record/connection_adapters/postgresql_adapter.rb +168 -0
- data/lib/searchlogic/active_record/connection_adapters/sqlite_adapter.rb +75 -0
- data/lib/searchlogic/condition/base.rb +159 -0
- data/lib/searchlogic/condition/begins_with.rb +17 -0
- data/lib/searchlogic/condition/blank.rb +21 -0
- data/lib/searchlogic/condition/child_of.rb +11 -0
- data/lib/searchlogic/condition/descendant_of.rb +24 -0
- data/lib/searchlogic/condition/ends_with.rb +17 -0
- data/lib/searchlogic/condition/equals.rb +27 -0
- data/lib/searchlogic/condition/greater_than.rb +15 -0
- data/lib/searchlogic/condition/greater_than_or_equal_to.rb +15 -0
- data/lib/searchlogic/condition/inclusive_descendant_of.rb +11 -0
- data/lib/searchlogic/condition/keywords.rb +47 -0
- data/lib/searchlogic/condition/less_than.rb +15 -0
- data/lib/searchlogic/condition/less_than_or_equal_to.rb +15 -0
- data/lib/searchlogic/condition/like.rb +15 -0
- data/lib/searchlogic/condition/nil.rb +21 -0
- data/lib/searchlogic/condition/not_begin_with.rb +20 -0
- data/lib/searchlogic/condition/not_blank.rb +19 -0
- data/lib/searchlogic/condition/not_end_with.rb +20 -0
- data/lib/searchlogic/condition/not_equal.rb +26 -0
- data/lib/searchlogic/condition/not_have_keywords.rb +20 -0
- data/lib/searchlogic/condition/not_like.rb +20 -0
- data/lib/searchlogic/condition/not_nil.rb +19 -0
- data/lib/searchlogic/condition/sibling_of.rb +14 -0
- data/lib/searchlogic/condition/tree.rb +17 -0
- data/lib/searchlogic/conditions/base.rb +484 -0
- data/lib/searchlogic/conditions/protection.rb +36 -0
- data/lib/searchlogic/config.rb +31 -0
- data/lib/searchlogic/config/helpers.rb +289 -0
- data/lib/searchlogic/config/search.rb +53 -0
- data/lib/searchlogic/core_ext/hash.rb +75 -0
- data/lib/searchlogic/helpers/control_types/link.rb +310 -0
- data/lib/searchlogic/helpers/control_types/links.rb +241 -0
- data/lib/searchlogic/helpers/control_types/remote_link.rb +87 -0
- data/lib/searchlogic/helpers/control_types/remote_links.rb +72 -0
- data/lib/searchlogic/helpers/control_types/remote_select.rb +36 -0
- data/lib/searchlogic/helpers/control_types/select.rb +82 -0
- data/lib/searchlogic/helpers/form.rb +208 -0
- data/lib/searchlogic/helpers/utilities.rb +197 -0
- data/lib/searchlogic/modifiers/absolute.rb +15 -0
- data/lib/searchlogic/modifiers/acos.rb +11 -0
- data/lib/searchlogic/modifiers/asin.rb +11 -0
- data/lib/searchlogic/modifiers/atan.rb +11 -0
- data/lib/searchlogic/modifiers/base.rb +27 -0
- data/lib/searchlogic/modifiers/ceil.rb +15 -0
- data/lib/searchlogic/modifiers/char_length.rb +15 -0
- data/lib/searchlogic/modifiers/cos.rb +15 -0
- data/lib/searchlogic/modifiers/cot.rb +15 -0
- data/lib/searchlogic/modifiers/day_of_month.rb +15 -0
- data/lib/searchlogic/modifiers/day_of_week.rb +15 -0
- data/lib/searchlogic/modifiers/day_of_year.rb +15 -0
- data/lib/searchlogic/modifiers/degrees.rb +11 -0
- data/lib/searchlogic/modifiers/exp.rb +15 -0
- data/lib/searchlogic/modifiers/floor.rb +15 -0
- data/lib/searchlogic/modifiers/hex.rb +11 -0
- data/lib/searchlogic/modifiers/hour.rb +11 -0
- data/lib/searchlogic/modifiers/log.rb +15 -0
- data/lib/searchlogic/modifiers/log10.rb +11 -0
- data/lib/searchlogic/modifiers/log2.rb +11 -0
- data/lib/searchlogic/modifiers/lower.rb +15 -0
- data/lib/searchlogic/modifiers/ltrim.rb +15 -0
- data/lib/searchlogic/modifiers/md5.rb +11 -0
- data/lib/searchlogic/modifiers/microseconds.rb +11 -0
- data/lib/searchlogic/modifiers/milliseconds.rb +11 -0
- data/lib/searchlogic/modifiers/minute.rb +15 -0
- data/lib/searchlogic/modifiers/month.rb +15 -0
- data/lib/searchlogic/modifiers/octal.rb +15 -0
- data/lib/searchlogic/modifiers/radians.rb +11 -0
- data/lib/searchlogic/modifiers/round.rb +11 -0
- data/lib/searchlogic/modifiers/rtrim.rb +15 -0
- data/lib/searchlogic/modifiers/second.rb +15 -0
- data/lib/searchlogic/modifiers/sign.rb +11 -0
- data/lib/searchlogic/modifiers/sin.rb +11 -0
- data/lib/searchlogic/modifiers/square_root.rb +15 -0
- data/lib/searchlogic/modifiers/tan.rb +15 -0
- data/lib/searchlogic/modifiers/trim.rb +15 -0
- data/lib/searchlogic/modifiers/upper.rb +15 -0
- data/lib/searchlogic/modifiers/week.rb +11 -0
- data/lib/searchlogic/modifiers/year.rb +11 -0
- data/lib/searchlogic/search/base.rb +148 -0
- data/lib/searchlogic/search/conditions.rb +53 -0
- data/lib/searchlogic/search/ordering.rb +244 -0
- data/lib/searchlogic/search/pagination.rb +121 -0
- data/lib/searchlogic/search/protection.rb +89 -0
- data/lib/searchlogic/search/searching.rb +31 -0
- data/lib/searchlogic/shared/utilities.rb +50 -0
- data/lib/searchlogic/shared/virtual_classes.rb +39 -0
- data/lib/searchlogic/version.rb +79 -0
- data/searchlogic.gemspec +39 -0
- data/test/fixtures/accounts.yml +15 -0
- data/test/fixtures/cats.yml +3 -0
- data/test/fixtures/dogs.yml +3 -0
- data/test/fixtures/orders.yml +14 -0
- data/test/fixtures/user_groups.yml +13 -0
- data/test/fixtures/users.yml +36 -0
- data/test/test_active_record_associations.rb +81 -0
- data/test/test_active_record_base.rb +93 -0
- data/test/test_condition_base.rb +52 -0
- data/test/test_condition_types.rb +143 -0
- data/test/test_conditions_base.rb +242 -0
- data/test/test_conditions_protection.rb +16 -0
- data/test/test_config.rb +23 -0
- data/test/test_helper.rb +134 -0
- data/test/test_search_base.rb +227 -0
- data/test/test_search_conditions.rb +19 -0
- data/test/test_search_ordering.rb +165 -0
- data/test/test_search_pagination.rb +72 -0
- data/test/test_search_protection.rb +24 -0
- data/test_libs/acts_as_tree.rb +98 -0
- data/test_libs/ordered_hash.rb +9 -0
- data/test_libs/rexml_fix.rb +14 -0
- metadata +317 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'echoe'
|
|
3
|
+
|
|
4
|
+
require File.dirname(__FILE__) << "/lib/searchlogic/version"
|
|
5
|
+
|
|
6
|
+
Echoe.new 'searchlogic' do |p|
|
|
7
|
+
p.version = Searchlogic::Version::STRING
|
|
8
|
+
p.author = "Ben Johnson of Binary Logic"
|
|
9
|
+
p.email = 'bjohnson@binarylogic.com'
|
|
10
|
+
p.project = 'searchlogic'
|
|
11
|
+
p.summary = "Object based ActiveRecord searching, ordering, pagination, and more!"
|
|
12
|
+
p.url = "http://github.com/binarylogic/searchlogic"
|
|
13
|
+
p.dependencies = %w(activerecord activesupport)
|
|
14
|
+
p.include_rakefile = true
|
|
15
|
+
end
|
data/TODO.rdoc
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
= To Do
|
|
2
|
+
|
|
3
|
+
1. Perform "more efficient" checks: year_of_created_at = 2008 and month_of_created_at = 8. Should result in a "BETWEEN" statement utilizing the column indexes. Thanks Georg for letting me know about this.
|
|
4
|
+
2. Solve conflicts between scope joins and joins in the search (for old versions of AR). Also solve conflicts between joins and includes.
|
|
5
|
+
3. Add configuration to change the "english" words
|
|
6
|
+
4. Re-add the distinct option
|
data/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require File.dirname(__FILE__) << "/lib/searchlogic"
|
data/lib/searchlogic.rb
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
|
2
|
+
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "active_record"
|
|
5
|
+
require "active_record/version"
|
|
6
|
+
|
|
7
|
+
["mysql", "postgresql", "sqlite"].each do |adapter_name|
|
|
8
|
+
begin
|
|
9
|
+
require "active_record/connection_adapters/#{adapter_name}_adapter"
|
|
10
|
+
require "searchlogic/active_record/connection_adapters/#{adapter_name}_adapter"
|
|
11
|
+
rescue Exception
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Core Ext
|
|
16
|
+
require "searchlogic/core_ext/hash"
|
|
17
|
+
|
|
18
|
+
# Shared
|
|
19
|
+
require "searchlogic/shared/utilities"
|
|
20
|
+
require "searchlogic/shared/virtual_classes"
|
|
21
|
+
|
|
22
|
+
# Base classes
|
|
23
|
+
require "searchlogic/version"
|
|
24
|
+
require "searchlogic/config/helpers"
|
|
25
|
+
require "searchlogic/config/search"
|
|
26
|
+
require "searchlogic/config"
|
|
27
|
+
|
|
28
|
+
# ActiveRecord
|
|
29
|
+
require "searchlogic/active_record/base"
|
|
30
|
+
require "searchlogic/active_record/associations"
|
|
31
|
+
|
|
32
|
+
# Search
|
|
33
|
+
require "searchlogic/search/ordering"
|
|
34
|
+
require "searchlogic/search/pagination"
|
|
35
|
+
require "searchlogic/search/conditions"
|
|
36
|
+
require "searchlogic/search/searching"
|
|
37
|
+
require "searchlogic/search/base"
|
|
38
|
+
require "searchlogic/search/protection"
|
|
39
|
+
|
|
40
|
+
# Conditions
|
|
41
|
+
require "searchlogic/conditions/protection"
|
|
42
|
+
require "searchlogic/conditions/base"
|
|
43
|
+
|
|
44
|
+
# Condition
|
|
45
|
+
require "searchlogic/condition/base"
|
|
46
|
+
require "searchlogic/condition/tree"
|
|
47
|
+
SEARCHGASM_CONDITIONS = [:begins_with, :blank, :child_of, :descendant_of, :ends_with, :equals, :greater_than, :greater_than_or_equal_to, :inclusive_descendant_of, :like, :nil, :not_begin_with, :not_blank, :not_end_with, :not_equal, :not_have_keywords, :not_nil, :keywords, :less_than, :less_than_or_equal_to, :sibling_of]
|
|
48
|
+
SEARCHGASM_CONDITIONS.each { |condition| require "searchlogic/condition/#{condition}" }
|
|
49
|
+
|
|
50
|
+
# Modifiers
|
|
51
|
+
require "searchlogic/modifiers/base"
|
|
52
|
+
SEARCHGASM_MODIFIERS = [:absolute, :acos, :asin, :atan, :ceil, :char_length, :cos, :cot, :day_of_month, :day_of_week, :day_of_year, :degrees, :exp, :floor, :hex, :hour, :log, :log10, :log2, :lower, :ltrim, :md5, :microseconds, :milliseconds, :minute, :month, :octal, :radians, :round, :rtrim, :second, :sign, :sin, :square_root, :tan, :trim, :upper, :week, :year]
|
|
53
|
+
SEARCHGASM_MODIFIERS.each { |modifier| require "searchlogic/modifiers/#{modifier}" }
|
|
54
|
+
|
|
55
|
+
# Helpers
|
|
56
|
+
require "searchlogic/helpers/utilities"
|
|
57
|
+
require "searchlogic/helpers/form"
|
|
58
|
+
require "searchlogic/helpers/control_types/link"
|
|
59
|
+
require "searchlogic/helpers/control_types/links"
|
|
60
|
+
require "searchlogic/helpers/control_types/select"
|
|
61
|
+
require "searchlogic/helpers/control_types/remote_link"
|
|
62
|
+
require "searchlogic/helpers/control_types/remote_links"
|
|
63
|
+
require "searchlogic/helpers/control_types/remote_select"
|
|
64
|
+
|
|
65
|
+
# Lets do it!
|
|
66
|
+
module Searchlogic
|
|
67
|
+
module Search
|
|
68
|
+
class Base
|
|
69
|
+
include Conditions
|
|
70
|
+
include Ordering
|
|
71
|
+
include Protection
|
|
72
|
+
include Pagination
|
|
73
|
+
include Searching
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
module Conditions
|
|
78
|
+
class Base
|
|
79
|
+
include Protection
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
SEARCHGASM_CONDITIONS.each { |condition| Base.register_condition("Searchlogic::Condition::#{condition.to_s.camelize}".constantize) }
|
|
83
|
+
SEARCHGASM_MODIFIERS.each { |modifier| Base.register_modifier("Searchlogic::Modifiers::#{modifier.to_s.camelize}".constantize) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The namespace I put all cached search classes.
|
|
87
|
+
module Cache
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Searchlogic
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
# = Searchlogic ActiveRecord Associations
|
|
4
|
+
#
|
|
5
|
+
# These methods hook into ActiveRecords association methods and add in searchlogic functionality.
|
|
6
|
+
module Associations
|
|
7
|
+
module AssociationCollection
|
|
8
|
+
# This needs to be implemented because AR doesn't leverage scopes with this method like it probably should
|
|
9
|
+
def find_with_searchlogic(*args)
|
|
10
|
+
options = args.extract_options!
|
|
11
|
+
args << filter_options_with_searchlogic(options)
|
|
12
|
+
find_without_searchlogic(*args)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module HasManyAssociation
|
|
17
|
+
def count_with_searchlogic(*args)
|
|
18
|
+
options = args.extract_options!
|
|
19
|
+
args << filter_options_with_searchlogic(options)
|
|
20
|
+
count_without_searchlogic(*args)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
ActiveRecord::Associations::AssociationCollection.class_eval do
|
|
28
|
+
if respond_to?(:find)
|
|
29
|
+
include Searchlogic::ActiveRecord::Associations::AssociationCollection
|
|
30
|
+
alias_method_chain :find, :searchlogic
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
35
|
+
include Searchlogic::ActiveRecord::Associations::HasManyAssociation
|
|
36
|
+
alias_method_chain :count, :searchlogic
|
|
37
|
+
|
|
38
|
+
# Older versions of AR have find in here, not in AssociationCollection
|
|
39
|
+
include Searchlogic::ActiveRecord::Associations::AssociationCollection
|
|
40
|
+
alias_method_chain :find, :searchlogic
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
ActiveRecord::Associations::ClassMethods::InnerJoinDependency::InnerJoinAssociation.class_eval do
|
|
44
|
+
private
|
|
45
|
+
# Inner joins impose limitations on queries. They can be quicker but you can't do OR conditions when conditions
|
|
46
|
+
# overlap from the base model to any of its associations. Also, inner joins won't allow you to order by an association
|
|
47
|
+
# attribute. What if the association is optional? All of those records are ommitted. It just doesn't make sense to default
|
|
48
|
+
# to inner joins when providing this as a "convenience" when searching. So let's change it.
|
|
49
|
+
def join_type
|
|
50
|
+
"LEFT OUTER JOIN"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
module Searchlogic
|
|
2
|
+
# == Searchlogic ActiveRecord
|
|
3
|
+
#
|
|
4
|
+
# Hooks into ActiveRecord to add all of the searchlogic functionality into your models. Only uses what is publically available, doesn't dig into internals, and
|
|
5
|
+
# searchlogic only gets involved when needed.
|
|
6
|
+
module ActiveRecord
|
|
7
|
+
# = Searchlogic ActiveRecord Base
|
|
8
|
+
# Adds in base level functionality to ActiveRecord
|
|
9
|
+
module Base
|
|
10
|
+
# This is an alias method chain. It hook into ActiveRecord's "calculate" method and checks to see if Searchlogic should get involved.
|
|
11
|
+
def calculate_with_searchlogic(*args)
|
|
12
|
+
options = args.extract_options!
|
|
13
|
+
options = filter_options_with_searchlogic(options, false)
|
|
14
|
+
args << options
|
|
15
|
+
calculate_without_searchlogic(*args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# This is an alias method chain. It hooks into ActiveRecord's "find" method and checks to see if Searchlogic should get involved.
|
|
19
|
+
def find_with_searchlogic(*args)
|
|
20
|
+
options = args.extract_options!
|
|
21
|
+
options = filter_options_with_searchlogic(options)
|
|
22
|
+
args << options
|
|
23
|
+
find_without_searchlogic(*args)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# This is an alias method chain. It hooks into ActiveRecord's scopes and checks to see if Searchlogic should get involved. Allowing you to use all of Searchlogics conditions and tools
|
|
27
|
+
# in scopes as well.
|
|
28
|
+
#
|
|
29
|
+
# === Examples
|
|
30
|
+
#
|
|
31
|
+
# Named scopes:
|
|
32
|
+
#
|
|
33
|
+
# named_scope :top_expensive, :conditions => {:total_gt => 1_000_000}, :per_page => 10
|
|
34
|
+
# named_scope :top_expensive_ordered, :conditions => {:total_gt => 1_000_000}, :per_page => 10, :order_by => {:user => :first_name}
|
|
35
|
+
#
|
|
36
|
+
# Good ole' regular scopes:
|
|
37
|
+
#
|
|
38
|
+
# with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
|
|
39
|
+
# find(:all)
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
|
|
43
|
+
# build_search
|
|
44
|
+
# end
|
|
45
|
+
def with_scope_with_searchlogic(method_scoping = {}, action = :merge, &block)
|
|
46
|
+
method_scoping[:find] = filter_options_with_searchlogic(method_scoping[:find]) if method_scoping[:find]
|
|
47
|
+
with_scope_without_searchlogic(method_scoping, action, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# This is a special method that Searchlogic adds in. It returns a new search object on the model. So you can search via an object.
|
|
51
|
+
#
|
|
52
|
+
# <b>This method is "protected". Meaning it checks the passed options for SQL injections. So trying to write raw SQL in *any* of the option will result in a raised exception. It's safe to pass a params object when instantiating.</b>
|
|
53
|
+
#
|
|
54
|
+
# This method has an alias "new_search"
|
|
55
|
+
#
|
|
56
|
+
# === Examples
|
|
57
|
+
#
|
|
58
|
+
# search = User.new_search
|
|
59
|
+
# search.conditions.first_name_contains = "Ben"
|
|
60
|
+
# search.per_page = 20
|
|
61
|
+
# search.page = 2
|
|
62
|
+
# search.order_by = {:user_group => :name}
|
|
63
|
+
# search.all # can call any search method: first, find(:all), find(:first), sum("id"), etc...
|
|
64
|
+
def build_search(options = {}, &block)
|
|
65
|
+
search = searchlogic_search
|
|
66
|
+
search.protect = true
|
|
67
|
+
search.options = options
|
|
68
|
+
yield search if block_given?
|
|
69
|
+
search
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# See build_search. This is the same method but *without* protection. Do *NOT* pass in a params object to this method.
|
|
73
|
+
#
|
|
74
|
+
# This also has an alias "new_search!"
|
|
75
|
+
def build_search!(options = {}, &block)
|
|
76
|
+
search = searchlogic_search(options)
|
|
77
|
+
yield search if block_given?
|
|
78
|
+
search
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Similar to ActiveRecord's attr_protected, but for conditions. It will block any conditions in this array that are being mass assigned. Mass assignments are:
|
|
82
|
+
#
|
|
83
|
+
# === Examples
|
|
84
|
+
#
|
|
85
|
+
# search = User.new_search(:conditions => {:first_name_like => "Ben", :email_contains => "binarylogic.com"})
|
|
86
|
+
# search.options = {:conditions => {:first_name_like => "Ben", :email_contains => "binarylogic.com"}}
|
|
87
|
+
#
|
|
88
|
+
# If first_name_like is in the list of conditions_protected then it will be removed from the hash.
|
|
89
|
+
def conditions_protected(*conditions)
|
|
90
|
+
write_inheritable_attribute(:conditions_protected, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def protected_conditions # :nodoc:
|
|
94
|
+
read_inheritable_attribute(:conditions_protected)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# This is the reverse of conditions_protected. You can specify conditions here and *only* these conditions will be allowed in mass assignment. Any condition not specified here will be blocked.
|
|
98
|
+
def conditions_accessible(*conditions)
|
|
99
|
+
write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (accessible_conditions || []))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def accessible_conditions # :nodoc:
|
|
103
|
+
read_inheritable_attribute(:conditions_accessible)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
def filter_options_with_searchlogic(options = {}, searching = true)
|
|
108
|
+
return options unless Searchlogic::Search::Base.needed?(self, options)
|
|
109
|
+
search = Searchlogic::Search::Base.create_virtual_class(self).new # call explicitly to avoid merging the scopes into the search
|
|
110
|
+
search.acting_as_filter = true
|
|
111
|
+
search.scope = scope(:find)
|
|
112
|
+
conditions = options.delete(:conditions) || options.delete("conditions") || {}
|
|
113
|
+
if conditions
|
|
114
|
+
case conditions
|
|
115
|
+
when Hash
|
|
116
|
+
conditions.each { |condition, value| search.conditions.send("#{condition}=", value) } # explicitly call to enforce blanks
|
|
117
|
+
else
|
|
118
|
+
search.conditions = conditions
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
search.options = options
|
|
122
|
+
search.sanitize(searching)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def searchlogic_search(options = {})
|
|
126
|
+
scope = {}
|
|
127
|
+
current_scope = scope(:find) && scope(:find).deep_dup
|
|
128
|
+
if current_scope
|
|
129
|
+
[:conditions, :include, :joins].each do |option|
|
|
130
|
+
value = current_scope.delete(option)
|
|
131
|
+
next if value.blank?
|
|
132
|
+
scope[option] = value
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Delete nil values in the scope, for some reason habtm relationships like to pass :limit => nil
|
|
136
|
+
new_scope = {}
|
|
137
|
+
current_scope.each { |k, v| new_scope[k] = v unless v.nil? }
|
|
138
|
+
current_scope = new_scope
|
|
139
|
+
end
|
|
140
|
+
search = Searchlogic::Search::Base.create_virtual_class(self).new
|
|
141
|
+
search.scope = scope
|
|
142
|
+
search.options = current_scope
|
|
143
|
+
search.options = options
|
|
144
|
+
search
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
ActiveRecord::Base.send(:extend, Searchlogic::ActiveRecord::Base)
|
|
151
|
+
|
|
152
|
+
module ActiveRecord #:nodoc: all
|
|
153
|
+
class Base
|
|
154
|
+
class << self
|
|
155
|
+
alias_method_chain :calculate, :searchlogic
|
|
156
|
+
alias_method_chain :find, :searchlogic
|
|
157
|
+
alias_method_chain :with_scope, :searchlogic
|
|
158
|
+
alias_method :new_search, :build_search
|
|
159
|
+
alias_method :new_search!, :build_search!
|
|
160
|
+
|
|
161
|
+
def valid_find_options
|
|
162
|
+
VALID_FIND_OPTIONS
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def valid_calculations_options
|
|
166
|
+
Calculations::CALCULATIONS_OPTIONS
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
# This is copied over from 2 different versions of ActiveRecord. I have to do this in order to preserve the "auto joins"
|
|
171
|
+
# as symbols. Keeping them as symbols allows ActiveRecord to merge them properly. The problem is when they conflict with includes.
|
|
172
|
+
# Includes add joins also, and they add them before joins do. So if they already added them skip them. Now you can do queries like:
|
|
173
|
+
#
|
|
174
|
+
# User.all(:joins => {:orders => :line_items}, :include => :orders)
|
|
175
|
+
#
|
|
176
|
+
# Where as before, the only way to get the above query to work would be to include line_items also, which is not neccessarily what you want.
|
|
177
|
+
def add_joins!(sql, options_or_joins, scope = :auto) # :nodoc:
|
|
178
|
+
code_type = (respond_to?(:array_of_strings?, true) && :array_of_strings) || (respond_to?(:merge_joins, true) && :merge_joins)
|
|
179
|
+
|
|
180
|
+
case code_type
|
|
181
|
+
when :array_of_strings, :merge_joins
|
|
182
|
+
joins = options_or_joins
|
|
183
|
+
scope = scope(:find) if :auto == scope
|
|
184
|
+
merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
|
|
185
|
+
case merged_joins
|
|
186
|
+
when Symbol, Hash, Array
|
|
187
|
+
if code_type == :array_of_strings && array_of_strings?(merged_joins)
|
|
188
|
+
merged_joins.each { |merged_join| sql << " #{merged_join} " unless sql.include?(merged_join) }
|
|
189
|
+
else
|
|
190
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
|
|
191
|
+
join_dependency.join_associations.each do |assoc|
|
|
192
|
+
join_sql = assoc.association_join
|
|
193
|
+
sql << " #{join_sql} " unless sql.include?(join_sql)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
when String
|
|
197
|
+
sql << " #{merged_joins} " if merged_joins && !sql.include?(merged_joins)
|
|
198
|
+
end
|
|
199
|
+
else
|
|
200
|
+
options = options_or_joins
|
|
201
|
+
scope = scope(:find) if :auto == scope
|
|
202
|
+
[(scope && scope[:joins]), options[:joins]].each do |join|
|
|
203
|
+
case join
|
|
204
|
+
when Symbol, Hash, Array
|
|
205
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil)
|
|
206
|
+
join_dependency.join_associations.each do |assoc|
|
|
207
|
+
join_sql = assoc.association_join
|
|
208
|
+
sql << " #{join_sql} " unless sql.include?(join_sql)
|
|
209
|
+
end
|
|
210
|
+
else
|
|
211
|
+
sql << " #{join} " if join && !sql.include?(join)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
module Searchlogic
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module ConnectionAdapters # :nodoc: all
|
|
4
|
+
module MysqlAdapter
|
|
5
|
+
# Date / time functions
|
|
6
|
+
def microseconds_sql(column_name)
|
|
7
|
+
"MICROSECOND(#{column_name})"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def milliseconds_sql(column_name)
|
|
11
|
+
"(MICROSECOND(#{column_name}) / 1000)"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def second_sql(column_name)
|
|
15
|
+
"SECOND(#{column_name})"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def minute_sql(column_name)
|
|
19
|
+
"MINUTE(#{column_name})"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def hour_sql(column_name)
|
|
23
|
+
"HOUR(#{column_name})"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def day_of_week_sql(column_name)
|
|
27
|
+
"DAYOFWEEK(#{column_name})"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def day_of_month_sql(column_name)
|
|
31
|
+
"DAYOFMONTH(#{column_name})"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def day_of_year_sql(column_name)
|
|
35
|
+
"DAYOFYEAR(#{column_name})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def week_sql(column_name)
|
|
39
|
+
"WEEK(#{column_name}, 2)"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def month_sql(column_name)
|
|
43
|
+
"MONTH(#{column_name})"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def year_sql(column_name)
|
|
47
|
+
"YEAR(#{column_name})"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# String functions
|
|
51
|
+
def char_length_sql(column_name)
|
|
52
|
+
"CHAR_LENGTH(#{column_name})"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def lower_sql(column_name)
|
|
56
|
+
"LOWER(#{column_name})"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ltrim_sql(column_name)
|
|
60
|
+
"LTRIM(#{column_name})"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def md5_sql(column_name)
|
|
64
|
+
"MD5(#{column_name})"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def rtrim_sql(column_name)
|
|
68
|
+
"RTRIM(#{column_name})"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def trim_sql(column_name)
|
|
72
|
+
"TRIM(#{column_name})"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def upper_sql(column_name)
|
|
76
|
+
"UPPER(#{column_name})"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Number functions
|
|
80
|
+
def absolute_sql(column_name)
|
|
81
|
+
"ABS(#{column_name})"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def acos_sql(column_name)
|
|
85
|
+
"ACOS(#{column_name})"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def asin_sql(column_name)
|
|
89
|
+
"ASIN(#{column_name})"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def atan_sql(column_name)
|
|
93
|
+
"ATAN(#{column_name})"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ceil_sql(column_name)
|
|
97
|
+
"CEIL(#{column_name})"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def cos_sql(column_name)
|
|
101
|
+
"COS(#{column_name})"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def cot_sql(column_name)
|
|
105
|
+
"COT(#{column_name})"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def degrees_sql(column_name)
|
|
109
|
+
"DEGREES(#{column_name})"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def exp_sql(column_name)
|
|
113
|
+
"EXP(#{column_name})"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def floor_sql(column_name)
|
|
117
|
+
"FLOOR(#{column_name})"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def hex_sql(column_name)
|
|
121
|
+
"HEX(#{column_name})"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def ln_sql(column_name)
|
|
125
|
+
"LN(#{column_name})"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def log_sql(column_name)
|
|
129
|
+
"LOG(#{column_name})"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def log2_sql(column_name)
|
|
133
|
+
"LOG2(#{column_name})"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def log10_sql(column_name)
|
|
137
|
+
"LOG10(#{column_name})"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def octal_sql(column_name)
|
|
141
|
+
"OCT(#{column_name})"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def radians_sql(column_name)
|
|
145
|
+
"RADIANS(#{column_name})"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def round_sql(column_name)
|
|
149
|
+
"ROUND(#{column_name})"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def sign_sql(column_name)
|
|
153
|
+
"SIGN(#{column_name})"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def sin_sql(column_name)
|
|
157
|
+
"SIN(#{column_name})"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def square_root_sql(column_name)
|
|
161
|
+
"SQRT(#{column_name})"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def tan_sql(column_name)
|
|
165
|
+
"TAN(#{column_name})"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
::ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, Searchlogic::ActiveRecord::ConnectionAdapters::MysqlAdapter)
|