searchlogic 1.6.6 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/CHANGELOG.rdoc +17 -0
- data/{MIT-LICENSE → LICENSE} +2 -2
- data/README.rdoc +128 -379
- data/Rakefile +56 -20
- data/VERSION.yml +4 -0
- data/init.rb +1 -1
- data/lib/searchlogic.rb +18 -98
- data/lib/searchlogic/core_ext/object.rb +33 -13
- data/lib/searchlogic/core_ext/proc.rb +11 -0
- data/lib/searchlogic/named_scopes/alias_scope.rb +63 -0
- data/lib/searchlogic/named_scopes/associations.rb +126 -0
- data/lib/searchlogic/named_scopes/conditions.rb +215 -0
- data/lib/searchlogic/named_scopes/ordering.rb +53 -0
- data/lib/searchlogic/rails_helpers.rb +69 -0
- data/lib/searchlogic/search.rb +146 -0
- data/rails/init.rb +1 -0
- data/searchlogic.gemspec +69 -0
- data/spec/core_ext/object_spec.rb +7 -0
- data/spec/core_ext/proc_spec.rb +9 -0
- data/spec/named_scopes/alias_scope_spec.rb +15 -0
- data/spec/named_scopes/associations_spec.rb +120 -0
- data/spec/named_scopes/conditions_spec.rb +253 -0
- data/spec/named_scopes/ordering_spec.rb +23 -0
- data/spec/search_spec.rb +283 -0
- data/spec/spec_helper.rb +78 -0
- metadata +40 -231
- data/Manifest.txt +0 -158
- data/TODO.rdoc +0 -4
- data/lib/searchlogic/active_record/associations.rb +0 -52
- data/lib/searchlogic/active_record/base.rb +0 -224
- data/lib/searchlogic/active_record/connection_adapters/mysql_adapter.rb +0 -176
- data/lib/searchlogic/active_record/connection_adapters/postgresql_adapter.rb +0 -172
- data/lib/searchlogic/active_record/connection_adapters/sqlite_adapter.rb +0 -80
- data/lib/searchlogic/condition/base.rb +0 -165
- data/lib/searchlogic/condition/begins_with.rb +0 -17
- data/lib/searchlogic/condition/blank.rb +0 -24
- data/lib/searchlogic/condition/child_of.rb +0 -11
- data/lib/searchlogic/condition/descendant_of.rb +0 -11
- data/lib/searchlogic/condition/ends_with.rb +0 -17
- data/lib/searchlogic/condition/equals.rb +0 -33
- data/lib/searchlogic/condition/greater_than.rb +0 -15
- data/lib/searchlogic/condition/greater_than_or_equal_to.rb +0 -15
- data/lib/searchlogic/condition/inclusive_descendant_of.rb +0 -10
- data/lib/searchlogic/condition/keywords.rb +0 -52
- data/lib/searchlogic/condition/less_than.rb +0 -15
- data/lib/searchlogic/condition/less_than_or_equal_to.rb +0 -15
- data/lib/searchlogic/condition/like.rb +0 -15
- data/lib/searchlogic/condition/nested_set.rb +0 -17
- data/lib/searchlogic/condition/nil.rb +0 -21
- data/lib/searchlogic/condition/not_begin_with.rb +0 -20
- data/lib/searchlogic/condition/not_blank.rb +0 -19
- data/lib/searchlogic/condition/not_end_with.rb +0 -20
- data/lib/searchlogic/condition/not_equal.rb +0 -27
- data/lib/searchlogic/condition/not_have_keywords.rb +0 -20
- data/lib/searchlogic/condition/not_like.rb +0 -20
- data/lib/searchlogic/condition/not_nil.rb +0 -19
- data/lib/searchlogic/condition/sibling_of.rb +0 -14
- data/lib/searchlogic/conditions/any_or_all.rb +0 -42
- data/lib/searchlogic/conditions/base.rb +0 -244
- data/lib/searchlogic/conditions/groups.rb +0 -74
- data/lib/searchlogic/conditions/magic_methods.rb +0 -286
- data/lib/searchlogic/conditions/multiparameter_attributes.rb +0 -105
- data/lib/searchlogic/conditions/protection.rb +0 -36
- data/lib/searchlogic/config.rb +0 -31
- data/lib/searchlogic/config/helpers.rb +0 -338
- data/lib/searchlogic/config/search.rb +0 -53
- data/lib/searchlogic/core_ext/hash.rb +0 -75
- data/lib/searchlogic/helpers/control_types/link.rb +0 -310
- data/lib/searchlogic/helpers/control_types/links.rb +0 -242
- data/lib/searchlogic/helpers/control_types/remote_link.rb +0 -87
- data/lib/searchlogic/helpers/control_types/remote_links.rb +0 -72
- data/lib/searchlogic/helpers/control_types/remote_select.rb +0 -36
- data/lib/searchlogic/helpers/control_types/select.rb +0 -82
- data/lib/searchlogic/helpers/form.rb +0 -208
- data/lib/searchlogic/helpers/utilities.rb +0 -197
- data/lib/searchlogic/modifiers/absolute.rb +0 -15
- data/lib/searchlogic/modifiers/acos.rb +0 -11
- data/lib/searchlogic/modifiers/asin.rb +0 -11
- data/lib/searchlogic/modifiers/atan.rb +0 -11
- data/lib/searchlogic/modifiers/avg.rb +0 -15
- data/lib/searchlogic/modifiers/base.rb +0 -27
- data/lib/searchlogic/modifiers/ceil.rb +0 -15
- data/lib/searchlogic/modifiers/char_length.rb +0 -15
- data/lib/searchlogic/modifiers/cos.rb +0 -15
- data/lib/searchlogic/modifiers/cot.rb +0 -15
- data/lib/searchlogic/modifiers/count.rb +0 -11
- data/lib/searchlogic/modifiers/day_of_month.rb +0 -15
- data/lib/searchlogic/modifiers/day_of_week.rb +0 -15
- data/lib/searchlogic/modifiers/day_of_year.rb +0 -15
- data/lib/searchlogic/modifiers/degrees.rb +0 -11
- data/lib/searchlogic/modifiers/exp.rb +0 -15
- data/lib/searchlogic/modifiers/floor.rb +0 -15
- data/lib/searchlogic/modifiers/hex.rb +0 -11
- data/lib/searchlogic/modifiers/hour.rb +0 -11
- data/lib/searchlogic/modifiers/log.rb +0 -15
- data/lib/searchlogic/modifiers/log10.rb +0 -11
- data/lib/searchlogic/modifiers/log2.rb +0 -11
- data/lib/searchlogic/modifiers/lower.rb +0 -15
- data/lib/searchlogic/modifiers/ltrim.rb +0 -15
- data/lib/searchlogic/modifiers/md5.rb +0 -11
- data/lib/searchlogic/modifiers/microseconds.rb +0 -11
- data/lib/searchlogic/modifiers/milliseconds.rb +0 -11
- data/lib/searchlogic/modifiers/minute.rb +0 -15
- data/lib/searchlogic/modifiers/month.rb +0 -15
- data/lib/searchlogic/modifiers/octal.rb +0 -15
- data/lib/searchlogic/modifiers/radians.rb +0 -11
- data/lib/searchlogic/modifiers/round.rb +0 -11
- data/lib/searchlogic/modifiers/rtrim.rb +0 -15
- data/lib/searchlogic/modifiers/second.rb +0 -15
- data/lib/searchlogic/modifiers/sign.rb +0 -11
- data/lib/searchlogic/modifiers/sin.rb +0 -11
- data/lib/searchlogic/modifiers/square_root.rb +0 -15
- data/lib/searchlogic/modifiers/sum.rb +0 -11
- data/lib/searchlogic/modifiers/tan.rb +0 -15
- data/lib/searchlogic/modifiers/trim.rb +0 -15
- data/lib/searchlogic/modifiers/upper.rb +0 -15
- data/lib/searchlogic/modifiers/week.rb +0 -11
- data/lib/searchlogic/modifiers/year.rb +0 -11
- data/lib/searchlogic/search/base.rb +0 -148
- data/lib/searchlogic/search/conditions.rb +0 -53
- data/lib/searchlogic/search/ordering.rb +0 -244
- data/lib/searchlogic/search/pagination.rb +0 -121
- data/lib/searchlogic/search/protection.rb +0 -89
- data/lib/searchlogic/search/searching.rb +0 -32
- data/lib/searchlogic/shared/utilities.rb +0 -57
- data/lib/searchlogic/shared/virtual_classes.rb +0 -39
- data/lib/searchlogic/version.rb +0 -79
- data/test/active_record_tests/associations_test.rb +0 -94
- data/test/active_record_tests/base_test.rb +0 -115
- data/test/condition_tests/base_test.rb +0 -62
- data/test/condition_tests/begins_with_test.rb +0 -11
- data/test/condition_tests/blank_test.rb +0 -31
- data/test/condition_tests/child_of_test.rb +0 -17
- data/test/condition_tests/descendant_of_test.rb +0 -12
- data/test/condition_tests/ends_with_test.rb +0 -11
- data/test/condition_tests/equals_test.rb +0 -28
- data/test/condition_tests/greater_than_or_equal_to_test.rb +0 -11
- data/test/condition_tests/greater_than_test.rb +0 -11
- data/test/condition_tests/inclusive_descendant_of_test.rb +0 -12
- data/test/condition_tests/keywords_test.rb +0 -23
- data/test/condition_tests/less_than_or_equal_to_test.rb +0 -11
- data/test/condition_tests/less_than_test.rb +0 -11
- data/test/condition_tests/like_test.rb +0 -11
- data/test/condition_tests/nil_test.rb +0 -31
- data/test/condition_tests/not_begin_with_test.rb +0 -8
- data/test/condition_tests/not_blank_test.rb +0 -8
- data/test/condition_tests/not_end_with_test.rb +0 -8
- data/test/condition_tests/not_equal_test.rb +0 -19
- data/test/condition_tests/not_have_keywords_test.rb +0 -8
- data/test/condition_tests/not_like_test.rb +0 -8
- data/test/condition_tests/not_nil_test.rb +0 -13
- data/test/condition_tests/sibling_of_test.rb +0 -15
- data/test/conditions_tests/any_or_all_test.rb +0 -23
- data/test/conditions_tests/base_test.rb +0 -185
- data/test/conditions_tests/groups_test.rb +0 -68
- data/test/conditions_tests/magic_methods_test.rb +0 -36
- data/test/conditions_tests/multiparameter_attributes_test.rb +0 -15
- data/test/conditions_tests/protection_test.rb +0 -18
- data/test/config_test.rb +0 -23
- data/test/fixtures/accounts.yml +0 -12
- data/test/fixtures/animals.yml +0 -7
- data/test/fixtures/orders.yml +0 -12
- data/test/fixtures/user_groups.yml +0 -5
- data/test/fixtures/users.yml +0 -45
- data/test/libs/awesome_nested_set.rb +0 -545
- data/test/libs/awesome_nested_set/.autotest +0 -13
- data/test/libs/awesome_nested_set/compatability.rb +0 -29
- data/test/libs/awesome_nested_set/helper.rb +0 -40
- data/test/libs/awesome_nested_set/named_scope.rb +0 -140
- data/test/libs/rexml_fix.rb +0 -14
- data/test/modifier_tests/day_of_month_test.rb +0 -16
- data/test/search_tests/base_test.rb +0 -241
- data/test/search_tests/conditions_test.rb +0 -21
- data/test/search_tests/ordering_test.rb +0 -167
- data/test/search_tests/pagination_test.rb +0 -74
- data/test/search_tests/protection_test.rb +0 -26
- data/test/test_helper.rb +0 -122
data/Rakefile
CHANGED
@@ -1,20 +1,56 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "searchlogic"
|
8
|
+
gem.summary = "Searchlogic provides common named scopes and object based searching for ActiveRecord."
|
9
|
+
gem.email = "bjohnson@binarylogic.com"
|
10
|
+
gem.homepage = "http://github.com/binarylogic/searchlogic"
|
11
|
+
gem.authors = ["Ben Johnson of Binary Logic"]
|
12
|
+
gem.rubyforge_project = "searchlogic"
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'spec/rake/spectask'
|
19
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
20
|
+
spec.libs << 'lib' << 'spec'
|
21
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
22
|
+
end
|
23
|
+
|
24
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
25
|
+
spec.libs << 'lib' << 'spec'
|
26
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
27
|
+
spec.rcov = true
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
task :default => :spec
|
32
|
+
|
33
|
+
require 'rake/rdoctask'
|
34
|
+
Rake::RDocTask.new do |rdoc|
|
35
|
+
if File.exist?('VERSION.yml')
|
36
|
+
config = YAML.load(File.read('VERSION.yml'))
|
37
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
38
|
+
else
|
39
|
+
version = ""
|
40
|
+
end
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "search #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
require 'rake/contrib/sshpublisher'
|
50
|
+
namespace :rubyforge do
|
51
|
+
desc "Release gem to RubyForge"
|
52
|
+
task :release => ["rubyforge:release:gem"]
|
53
|
+
end
|
54
|
+
rescue LoadError
|
55
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
56
|
+
end
|
data/VERSION.yml
ADDED
data/init.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require "searchlogic"
|
data/lib/searchlogic.rb
CHANGED
@@ -1,100 +1,20 @@
|
|
1
|
-
|
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"
|
1
|
+
require "searchlogic/core_ext/proc"
|
17
2
|
require "searchlogic/core_ext/object"
|
18
|
-
|
19
|
-
|
20
|
-
require "searchlogic/
|
21
|
-
require "searchlogic/
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
require "searchlogic/search/pagination"
|
36
|
-
require "searchlogic/search/conditions"
|
37
|
-
require "searchlogic/search/searching"
|
38
|
-
require "searchlogic/search/base"
|
39
|
-
require "searchlogic/search/protection"
|
40
|
-
|
41
|
-
# Conditions
|
42
|
-
require "searchlogic/conditions/any_or_all"
|
43
|
-
require "searchlogic/conditions/groups"
|
44
|
-
require "searchlogic/conditions/magic_methods"
|
45
|
-
require "searchlogic/conditions/multiparameter_attributes"
|
46
|
-
require "searchlogic/conditions/protection"
|
47
|
-
require "searchlogic/conditions/base"
|
48
|
-
|
49
|
-
# Condition
|
50
|
-
require "searchlogic/condition/base"
|
51
|
-
require "searchlogic/condition/nested_set"
|
52
|
-
SEARCHLOGIC_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,
|
53
|
-
:not_blank, :not_end_with, :not_equal, :not_have_keywords, :not_like, :not_nil, :keywords, :less_than, :less_than_or_equal_to, :sibling_of]
|
54
|
-
SEARCHLOGIC_CONDITIONS.each { |condition| require "searchlogic/condition/#{condition}" }
|
55
|
-
|
56
|
-
# Modifiers
|
57
|
-
require "searchlogic/modifiers/base"
|
58
|
-
SEARCHLOGIC_MODIFIERS = [:absolute, :acos, :asin, :atan, :avg, :ceil, :char_length, :cos, :cot, :count, :day_of_month, :day_of_week, :day_of_year, :degrees, :exp, :floor, :hex, :hour, :log, :log10,
|
59
|
-
:log2, :lower, :ltrim, :md5, :microseconds, :milliseconds, :minute, :month, :octal, :radians, :round, :rtrim, :second, :sign, :sin, :square_root, :sum, :tan, :trim, :upper, :week, :year]
|
60
|
-
SEARCHLOGIC_MODIFIERS.each { |modifier| require "searchlogic/modifiers/#{modifier}" }
|
61
|
-
|
62
|
-
# Helpers
|
63
|
-
require "searchlogic/helpers/utilities"
|
64
|
-
require "searchlogic/helpers/form"
|
65
|
-
require "searchlogic/helpers/control_types/link"
|
66
|
-
require "searchlogic/helpers/control_types/links"
|
67
|
-
require "searchlogic/helpers/control_types/select"
|
68
|
-
require "searchlogic/helpers/control_types/remote_link"
|
69
|
-
require "searchlogic/helpers/control_types/remote_links"
|
70
|
-
require "searchlogic/helpers/control_types/remote_select"
|
71
|
-
|
72
|
-
# Lets do it!
|
73
|
-
module Searchlogic
|
74
|
-
module Search
|
75
|
-
class Base
|
76
|
-
include Conditions
|
77
|
-
include Ordering
|
78
|
-
include Protection
|
79
|
-
include Pagination
|
80
|
-
include Searching
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
module Conditions
|
85
|
-
class Base
|
86
|
-
include AnyOrAll
|
87
|
-
include Groups
|
88
|
-
include MagicMethods
|
89
|
-
include MultiparameterAttributes
|
90
|
-
include Protection
|
91
|
-
end
|
92
|
-
|
93
|
-
SEARCHLOGIC_CONDITIONS.each { |condition| Base.register_condition("Searchlogic::Condition::#{condition.to_s.camelize}".constantize) }
|
94
|
-
SEARCHLOGIC_MODIFIERS.each { |modifier| Base.register_modifier("Searchlogic::Modifiers::#{modifier.to_s.camelize}".constantize) }
|
95
|
-
end
|
96
|
-
|
97
|
-
# The namespace I put all cached search classes.
|
98
|
-
module Cache
|
99
|
-
end
|
3
|
+
require "searchlogic/named_scopes/conditions"
|
4
|
+
require "searchlogic/named_scopes/ordering"
|
5
|
+
require "searchlogic/named_scopes/associations"
|
6
|
+
require "searchlogic/named_scopes/alias_scope"
|
7
|
+
require "searchlogic/search"
|
8
|
+
|
9
|
+
Proc.send(:include, Searchlogic::CoreExt::Proc)
|
10
|
+
Object.send(:include, Searchlogic::CoreExt::Object)
|
11
|
+
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Conditions)
|
12
|
+
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
|
13
|
+
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Associations)
|
14
|
+
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AliasScope)
|
15
|
+
ActiveRecord::Base.extend(Searchlogic::Search::Implementation)
|
16
|
+
|
17
|
+
if defined?(ActionController)
|
18
|
+
require "searchlogic/rails_helpers"
|
19
|
+
ActionController::Base.helper(Searchlogic::RailsHelpers)
|
100
20
|
end
|
@@ -1,19 +1,39 @@
|
|
1
1
|
module Searchlogic
|
2
2
|
module CoreExt
|
3
|
+
# Contains extensions for the Object class that Searchlogic uses.
|
3
4
|
module Object
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
|
5
|
+
# Searchlogic needs to know the expected type of the condition value so that it can properly cast
|
6
|
+
# the value in the Searchlogic::Search object. For example:
|
7
|
+
#
|
8
|
+
# search = User.search(:id_gt => "1")
|
9
|
+
#
|
10
|
+
# You would expect this:
|
11
|
+
#
|
12
|
+
# search.id_gt => 1
|
13
|
+
#
|
14
|
+
# Not this:
|
15
|
+
#
|
16
|
+
# search.id_gt => "1"
|
17
|
+
#
|
18
|
+
# Parameter values from forms are ALWAYS strings, so we have to cast them. Just like ActiveRecord
|
19
|
+
# does when you instantiate a new User object.
|
20
|
+
#
|
21
|
+
# The problem is that ruby has no variable types, so Searchlogic needs to know what type you are expecting
|
22
|
+
# for your named scope. So instead of this:
|
23
|
+
#
|
24
|
+
# named_scope :id_gt, lambda { |value| {:conditions => ["id > ?", value]} }
|
25
|
+
#
|
26
|
+
# You need to do this:
|
27
|
+
#
|
28
|
+
# named_scope :id_gt, searchlogic_lambda(:integer) { |value| {:conditions => ["id > ?", value]} }
|
29
|
+
#
|
30
|
+
# If you are wanting a string, you don't have to do anything, because Searchlogic assumes you are want a string.
|
31
|
+
# If you want something else, you need to specify it as I did in the above example.
|
32
|
+
def searchlogic_lambda(type = :string, &block)
|
33
|
+
proc = lambda(&block)
|
34
|
+
proc.searchlogic_arg_type = type
|
35
|
+
proc
|
14
36
|
end
|
15
37
|
end
|
16
38
|
end
|
17
|
-
end
|
18
|
-
|
19
|
-
Object.send(:include, Searchlogic::CoreExt::Object)
|
39
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
module NamedScopes
|
3
|
+
# Adds the ability to create alias scopes that allow you to alias a named
|
4
|
+
# scope or create a named scope procedure, while at the same time letting
|
5
|
+
# Searchlogic know that this is a safe method.
|
6
|
+
module AliasScope
|
7
|
+
# The searchlogic Search class takes a hash and chains the values together as named scopes.
|
8
|
+
# For security reasons the only hash keys that are allowed must be mapped to named scopes.
|
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
|
18
|
+
#
|
19
|
+
# class User
|
20
|
+
# def teenager
|
21
|
+
# age_gte(13).age_lte(19)
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# As I stated above, you could not use this method with the Searchlogic::Search class because
|
26
|
+
# there is no way to tell that this is actually a named scope. Instead, Searchlogic lets you
|
27
|
+
# do something like this:
|
28
|
+
#
|
29
|
+
# User.alias_scope :teenager, lambda { age_gte(13).age_lte(19) }
|
30
|
+
#
|
31
|
+
# It fits in better, at the same time Searchlogic will know this is an acceptable named scope.
|
32
|
+
def alias_scope(name, options = nil)
|
33
|
+
alias_scopes[name.to_sym] = options
|
34
|
+
(class << self; self end).instance_eval do
|
35
|
+
define_method name do |*args|
|
36
|
+
case options
|
37
|
+
when Symbol
|
38
|
+
send(options)
|
39
|
+
else
|
40
|
+
options.call(*args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def alias_scopes # :nodoc:
|
47
|
+
@alias_scopes ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def alias_scope?(name) # :nodoc:
|
51
|
+
alias_scopes.key?(name.to_sym)
|
52
|
+
end
|
53
|
+
|
54
|
+
def condition?(name) # :nodoc:
|
55
|
+
super || alias_scope?(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def named_scope_options(name) # :nodoc:
|
59
|
+
super || alias_scopes[name.to_sym]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
module NamedScopes
|
3
|
+
# Handles dynamically creating named scopes for associations.
|
4
|
+
module Associations
|
5
|
+
def condition?(name) # :nodoc:
|
6
|
+
super || association_condition?(name) || association_alias_condition?(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def primary_condition_name(name) # :nodoc:
|
10
|
+
if result = super
|
11
|
+
result
|
12
|
+
elsif association_condition?(name)
|
13
|
+
name.to_sym
|
14
|
+
elsif details = association_alias_condition_details(name)
|
15
|
+
"#{details[:association]}_#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Is the name of the method a valid name for an association condition?
|
22
|
+
def association_condition?(name)
|
23
|
+
!association_condition_details(name).nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Is the ane of the method a valie name for an association alias condition?
|
27
|
+
# An alias being "gt" for "greater_than", etc.
|
28
|
+
def association_alias_condition?(name)
|
29
|
+
!association_alias_condition_details(name).nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def method_missing(name, *args, &block)
|
34
|
+
if details = association_condition_details(name)
|
35
|
+
create_association_condition(details[:association], details[:column], details[:condition], args)
|
36
|
+
send(name, *args)
|
37
|
+
elsif details = association_alias_condition_details(name)
|
38
|
+
create_association_alias_condition(details[:association], details[:column], details[:condition], args)
|
39
|
+
send(name, *args)
|
40
|
+
elsif details = association_ordering_condition_details(name)
|
41
|
+
create_association_ordering_condition(details[:association], details[:order_as], details[:column], args)
|
42
|
+
send(name, *args)
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def association_condition_details(name)
|
49
|
+
associations = reflect_on_all_associations.collect { |assoc| assoc.name }
|
50
|
+
if name.to_s =~ /^(#{associations.join("|")})_(\w+)_(#{Conditions::PRIMARY_CONDITIONS.join("|")})$/
|
51
|
+
{:association => $1, :column => $2, :condition => $3}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_association_condition(association_name, column, condition, args)
|
56
|
+
named_scope("#{association_name}_#{column}_#{condition}", association_condition_options(association_name, "#{column}_#{condition}", args))
|
57
|
+
end
|
58
|
+
|
59
|
+
def association_alias_condition_details(name)
|
60
|
+
associations = reflect_on_all_associations.collect { |assoc| assoc.name }
|
61
|
+
if name.to_s =~ /^(#{associations.join("|")})_(\w+)_(#{Conditions::ALIAS_CONDITIONS.join("|")})$/
|
62
|
+
{:association => $1, :column => $2, :condition => $3}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_association_alias_condition(association, column, condition, args)
|
67
|
+
primary_condition = primary_condition(condition)
|
68
|
+
alias_name = "#{association}_#{column}_#{condition}"
|
69
|
+
primary_name = "#{association}_#{column}_#{primary_condition}"
|
70
|
+
send(primary_name, *args) # go back to method_missing and make sure we create the method
|
71
|
+
(class << self; self; end).class_eval { alias_method alias_name, primary_name }
|
72
|
+
end
|
73
|
+
|
74
|
+
def association_ordering_condition_details(name)
|
75
|
+
associations = reflect_on_all_associations.collect { |assoc| assoc.name }
|
76
|
+
if name.to_s =~ /^(ascend|descend)_by_(#{associations.join("|")})_(\w+)$/
|
77
|
+
{:order_as => $1, :association => $2, :column => $3}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_association_ordering_condition(association_name, order_as, column, args)
|
82
|
+
named_scope("#{order_as}_by_#{association_name}_#{column}", association_condition_options(association_name, "#{order_as}_by_#{column}", args))
|
83
|
+
end
|
84
|
+
|
85
|
+
def association_condition_options(association_name, association_condition, args)
|
86
|
+
association = reflect_on_association(association_name.to_sym)
|
87
|
+
scope = association.klass.send(association_condition, *args)
|
88
|
+
scope_options = association.klass.named_scope_options(association_condition)
|
89
|
+
arity = association.klass.named_scope_arity(association_condition)
|
90
|
+
|
91
|
+
if !arity || arity == 0
|
92
|
+
# The underlying condition doesn't require any parameters, so let's just create a simple
|
93
|
+
# named scope that is based on a hash.
|
94
|
+
options = scope.proxy_options
|
95
|
+
options[:joins] = options[:joins].blank? ? association.name : {association.name => options[:joins]}
|
96
|
+
#add_left_outer_joins(options, association)
|
97
|
+
options
|
98
|
+
else
|
99
|
+
# The underlying condition requires parameters, let's match the parameters it requires
|
100
|
+
# and pass those onto the named scope. We can't use proxy_options because that returns the
|
101
|
+
# result after a value has been passed.
|
102
|
+
proc_args = []
|
103
|
+
if arity > 0
|
104
|
+
arity.times { |i| proc_args << "arg#{i}"}
|
105
|
+
else
|
106
|
+
positive_arity = arity * -1
|
107
|
+
positive_arity.times do |i|
|
108
|
+
if i == (positive_arity - 1)
|
109
|
+
proc_args << "*arg#{i}"
|
110
|
+
else
|
111
|
+
proc_args << "arg#{i}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
eval <<-"end_eval"
|
116
|
+
searchlogic_lambda(:#{scope_options.searchlogic_arg_type}) { |#{proc_args.join(",")}|
|
117
|
+
options = association.klass.named_scope_options(association_condition).call(#{proc_args.join(",")})
|
118
|
+
options[:joins] = options[:joins].blank? ? association.name : {association.name => options[:joins]}
|
119
|
+
options
|
120
|
+
}
|
121
|
+
end_eval
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|