joost-searchlogic 2.1.5.2
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/.gitignore +6 -0
- data/CHANGELOG.rdoc +346 -0
- data/LICENSE +20 -0
- data/README.rdoc +229 -0
- data/Rakefile +42 -0
- data/VERSION.yml +4 -0
- data/init.rb +1 -0
- data/lib/searchlogic.rb +31 -0
- data/lib/searchlogic/active_record_consistency.rb +27 -0
- data/lib/searchlogic/core_ext/object.rb +39 -0
- 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 +131 -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 +150 -0
- data/rails/init.rb +1 -0
- data/searchlogic.gemspec +73 -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 +288 -0
- data/spec/spec_helper.rb +79 -0
- metadata +96 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
module NamedScopes
|
3
|
+
# Handles dynamically creating named scopes for orderin by columns.
|
4
|
+
module Ordering
|
5
|
+
def condition?(name) # :nodoc:
|
6
|
+
super || order_condition?(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def primary_condition_name(name) # :nodoc
|
10
|
+
if result = super
|
11
|
+
result
|
12
|
+
elsif order_condition?(name)
|
13
|
+
name.to_sym
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def order_condition?(name) # :nodoc:
|
20
|
+
!order_condition_details(name).nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def method_missing(name, *args, &block)
|
25
|
+
if name == :order
|
26
|
+
named_scope name, lambda { |scope_name|
|
27
|
+
return {} if !order_condition?(scope_name)
|
28
|
+
send(scope_name).proxy_options
|
29
|
+
}
|
30
|
+
send(name, *args)
|
31
|
+
elsif details = order_condition_details(name)
|
32
|
+
create_order_conditions(details[:column])
|
33
|
+
send(name, *args)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def order_condition_details(name)
|
40
|
+
if name.to_s =~ /^(ascend|descend)_by_(\w+)$/
|
41
|
+
{:order_as => $1, :column => $2}
|
42
|
+
elsif name.to_s =~ /^order$/
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_order_conditions(column)
|
48
|
+
named_scope("ascend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} ASC"})
|
49
|
+
named_scope("descend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} DESC"})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
module RailsHelpers
|
3
|
+
# Creates a link that alternates between acending and descending. It basically
|
4
|
+
# alternates between calling 2 named scopes: "ascend_by_*" and "descend_by_*"
|
5
|
+
#
|
6
|
+
# By default Searchlogic gives you these named scopes for all of your columns, but
|
7
|
+
# if you wanted to create your own, it will work with those too.
|
8
|
+
#
|
9
|
+
# Examples:
|
10
|
+
#
|
11
|
+
# order @search, :by => :username
|
12
|
+
# order @search, :by => :created_at, :as => "Created"
|
13
|
+
#
|
14
|
+
# This helper accepts the following options:
|
15
|
+
#
|
16
|
+
# * <tt>:by</tt> - the name of the named scope. This helper will prepend this value with "ascend_by_" and "descend_by_"
|
17
|
+
# * <tt>:as</tt> - the text used in the link, defaults to whatever is passed to :by
|
18
|
+
# * <tt>:ascend_scope</tt> - what scope to call for ascending the data, defaults to "ascend_by_:by"
|
19
|
+
# * <tt>:descend_scope</tt> - what scope to call for descending the data, defaults to "descend_by_:by"
|
20
|
+
# * <tt>:params_scope</tt> - the name of the params key to scope the order condition by, defaults to :search
|
21
|
+
def order(search, options = {}, html_options = {})
|
22
|
+
options[:params_scope] ||= :search
|
23
|
+
options[:as] ||= options[:by].to_s.humanize
|
24
|
+
options[:ascend_scope] ||= "ascend_by_#{options[:by]}"
|
25
|
+
options[:descend_scope] ||= "descend_by_#{options[:by]}"
|
26
|
+
ascending = search.order.to_s == options[:ascend_scope]
|
27
|
+
new_scope = ascending ? options[:descend_scope] : options[:ascend_scope]
|
28
|
+
selected = [options[:ascend_scope], options[:descend_scope]].include?(search.order.to_s)
|
29
|
+
if selected
|
30
|
+
css_classes = html_options[:class] ? html_options[:class].split(" ") : []
|
31
|
+
if ascending
|
32
|
+
options[:as] = "▲ #{options[:as]}"
|
33
|
+
css_classes << "ascending"
|
34
|
+
else
|
35
|
+
options[:as] = "▼ #{options[:as]}"
|
36
|
+
css_classes << "descending"
|
37
|
+
end
|
38
|
+
html_options[:class] = css_classes.join(" ")
|
39
|
+
end
|
40
|
+
link_to options[:as], url_for(options[:params_scope] => search.conditions.merge( { :order => new_scope } ) ), html_options
|
41
|
+
end
|
42
|
+
|
43
|
+
# Automatically makes the form method :get if a Searchlogic::Search and sets
|
44
|
+
# the params scope to :search
|
45
|
+
def form_for(*args, &block)
|
46
|
+
if search_obj = args.find { |arg| arg.is_a?(Searchlogic::Search) }
|
47
|
+
options = args.extract_options!
|
48
|
+
options[:html] ||= {}
|
49
|
+
options[:html][:method] ||= :get
|
50
|
+
options[:url] ||= url_for
|
51
|
+
args.unshift(:search) if args.first == search_obj
|
52
|
+
args << options
|
53
|
+
end
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Automatically adds an "order" hidden field in your form to preserve how the data
|
58
|
+
# is being ordered.
|
59
|
+
def fields_for(*args, &block)
|
60
|
+
if search_obj = args.find { |arg| arg.is_a?(Searchlogic::Search) }
|
61
|
+
args.unshift(:search) if args.first == search_obj
|
62
|
+
concat(content_tag("div", hidden_field_tag("#{args.first}[order]", search_obj.order)) + "\n")
|
63
|
+
super
|
64
|
+
else
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Searchlogic
|
2
|
+
# A class that acts like a model, creates attr_accessors for named_scopes, and then
|
3
|
+
# chains together everything when an "action" method is called. It basically makes
|
4
|
+
# implementing search forms in your application effortless:
|
5
|
+
#
|
6
|
+
# search = User.search
|
7
|
+
# search.username_like = "bjohnson"
|
8
|
+
# search.all
|
9
|
+
#
|
10
|
+
# Is equivalent to:
|
11
|
+
#
|
12
|
+
# User.search(:username_like => "bjohnson").all
|
13
|
+
#
|
14
|
+
# Is equivalent to:
|
15
|
+
#
|
16
|
+
# User.username_like("bjohnson").all
|
17
|
+
class Search
|
18
|
+
# Responsible for adding a "search" method into your models.
|
19
|
+
module Implementation
|
20
|
+
# Additional method, gets aliases as "search" if that method
|
21
|
+
# is available. A lot of other libraries like to use "search"
|
22
|
+
# as well, so if you have a conflict like this, you can use
|
23
|
+
# this method directly.
|
24
|
+
def searchlogic(conditions = {})
|
25
|
+
Search.new(self, scope(:find), conditions)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Is an invalid condition is used this error will be raised. Ex:
|
30
|
+
#
|
31
|
+
# User.search(:unkown => true)
|
32
|
+
#
|
33
|
+
# Where unknown is not a valid named scope for the User model.
|
34
|
+
class UnknownConditionError < StandardError
|
35
|
+
def initialize(condition)
|
36
|
+
msg = "The #{condition} is not a valid condition. You may only use conditions that map to a named scope"
|
37
|
+
super(msg)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_accessor :klass, :current_scope, :conditions
|
42
|
+
undef :id if respond_to?(:id)
|
43
|
+
|
44
|
+
# Creates a new search object for the given class. Ex:
|
45
|
+
#
|
46
|
+
# Searchlogic::Search.new(User, {}, {:username_like => "bjohnson"})
|
47
|
+
def initialize(klass, current_scope, conditions = {})
|
48
|
+
self.klass = klass
|
49
|
+
self.current_scope = current_scope
|
50
|
+
self.conditions = conditions if conditions.is_a?(Hash)
|
51
|
+
end
|
52
|
+
|
53
|
+
def clone
|
54
|
+
self.class.new(klass, current_scope.clone, conditions.clone)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a hash of the current conditions set.
|
58
|
+
def conditions
|
59
|
+
@conditions ||= {}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Accepts a hash of conditions.
|
63
|
+
def conditions=(values)
|
64
|
+
values.each do |condition, value|
|
65
|
+
value.delete_if { |v| v.blank? } if value.is_a?(Array)
|
66
|
+
next if value.blank?
|
67
|
+
send("#{condition}=", value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Delete a condition from the search. Since conditions map to named scopes,
|
72
|
+
# if a named scope accepts a parameter there is no way to actually delete
|
73
|
+
# the scope if you do not want it anymore. A nil value might be meaningful
|
74
|
+
# to that scope.
|
75
|
+
def delete(*names)
|
76
|
+
names.each { |name| @conditions.delete(name.to_sym) }
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def method_missing(name, *args, &block)
|
82
|
+
if name.to_s =~ /(\w+)=$/
|
83
|
+
condition = $1.to_sym
|
84
|
+
scope_name = normalize_scope_name($1)
|
85
|
+
if scope?(scope_name)
|
86
|
+
conditions[condition] = type_cast(args.first, cast_type(scope_name))
|
87
|
+
else
|
88
|
+
raise UnknownConditionError.new(name)
|
89
|
+
end
|
90
|
+
elsif scope?(normalize_scope_name(name))
|
91
|
+
if args.size > 0
|
92
|
+
send("#{name}=", *args)
|
93
|
+
self
|
94
|
+
else
|
95
|
+
conditions[name]
|
96
|
+
end
|
97
|
+
else
|
98
|
+
scope = conditions.inject(klass.scoped(current_scope)) do |scope, condition|
|
99
|
+
scope_name, value = condition
|
100
|
+
scope_name = normalize_scope_name(scope_name)
|
101
|
+
klass.send(scope_name, value) if !klass.respond_to?(scope_name)
|
102
|
+
arity = klass.named_scope_arity(scope_name)
|
103
|
+
|
104
|
+
if !arity || arity == 0
|
105
|
+
if value == true
|
106
|
+
scope.send(scope_name)
|
107
|
+
else
|
108
|
+
scope
|
109
|
+
end
|
110
|
+
else
|
111
|
+
scope.send(scope_name, value)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
scope.send(name, *args, &block)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def normalize_scope_name(scope_name)
|
119
|
+
klass.column_names.include?(scope_name.to_s) ? "#{scope_name}_equals".to_sym : scope_name.to_sym
|
120
|
+
end
|
121
|
+
|
122
|
+
def scope?(scope_name)
|
123
|
+
klass.scopes.key?(scope_name) || klass.condition?(scope_name)
|
124
|
+
end
|
125
|
+
|
126
|
+
def cast_type(name)
|
127
|
+
klass.send(name, nil) if !klass.respond_to?(name) # We need to set up the named scope if it doesn't exist, so we can get a value for named_ssope_options
|
128
|
+
named_scope_options = klass.named_scope_options(name)
|
129
|
+
arity = klass.named_scope_arity(name)
|
130
|
+
if !arity || arity == 0
|
131
|
+
:boolean
|
132
|
+
else
|
133
|
+
named_scope_options.respond_to?(:searchlogic_arg_type) ? named_scope_options.searchlogic_arg_type : :string
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def type_cast(value, type)
|
138
|
+
case value
|
139
|
+
when Array
|
140
|
+
value.collect { |v| type_cast(v, type) }
|
141
|
+
else
|
142
|
+
# Let's leverage ActiveRecord's type casting, so that casting is consistent
|
143
|
+
# with the other models.
|
144
|
+
column_for_type_cast = ActiveRecord::ConnectionAdapters::Column.new("", nil)
|
145
|
+
column_for_type_cast.instance_variable_set(:@type, type)
|
146
|
+
column_for_type_cast.type_cast(value)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "searchlogic"
|
data/searchlogic.gemspec
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{searchlogic}
|
5
|
+
s.version = "2.1.5.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Ben Johnson of Binary Logic"]
|
9
|
+
s.date = %q{2009-07-12}
|
10
|
+
s.email = %q{bjohnson@binarylogic.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"CHANGELOG.rdoc",
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION.yml",
|
22
|
+
"init.rb",
|
23
|
+
"lib/searchlogic.rb",
|
24
|
+
"lib/searchlogic/active_record_consistency.rb",
|
25
|
+
"lib/searchlogic/core_ext/object.rb",
|
26
|
+
"lib/searchlogic/core_ext/proc.rb",
|
27
|
+
"lib/searchlogic/named_scopes/alias_scope.rb",
|
28
|
+
"lib/searchlogic/named_scopes/associations.rb",
|
29
|
+
"lib/searchlogic/named_scopes/conditions.rb",
|
30
|
+
"lib/searchlogic/named_scopes/ordering.rb",
|
31
|
+
"lib/searchlogic/rails_helpers.rb",
|
32
|
+
"lib/searchlogic/search.rb",
|
33
|
+
"rails/init.rb",
|
34
|
+
"searchlogic.gemspec",
|
35
|
+
"spec/core_ext/object_spec.rb",
|
36
|
+
"spec/core_ext/proc_spec.rb",
|
37
|
+
"spec/named_scopes/alias_scope_spec.rb",
|
38
|
+
"spec/named_scopes/associations_spec.rb",
|
39
|
+
"spec/named_scopes/conditions_spec.rb",
|
40
|
+
"spec/named_scopes/ordering_spec.rb",
|
41
|
+
"spec/search_spec.rb",
|
42
|
+
"spec/spec_helper.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/binarylogic/searchlogic}
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubyforge_project = %q{searchlogic}
|
48
|
+
s.rubygems_version = %q{1.3.4}
|
49
|
+
s.summary = %q{Searchlogic provides common named scopes and object based searching for ActiveRecord.}
|
50
|
+
s.test_files = [
|
51
|
+
"spec/core_ext/object_spec.rb",
|
52
|
+
"spec/core_ext/proc_spec.rb",
|
53
|
+
"spec/named_scopes/alias_scope_spec.rb",
|
54
|
+
"spec/named_scopes/associations_spec.rb",
|
55
|
+
"spec/named_scopes/conditions_spec.rb",
|
56
|
+
"spec/named_scopes/ordering_spec.rb",
|
57
|
+
"spec/search_spec.rb",
|
58
|
+
"spec/spec_helper.rb"
|
59
|
+
]
|
60
|
+
|
61
|
+
if s.respond_to? :specification_version then
|
62
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
63
|
+
s.specification_version = 3
|
64
|
+
|
65
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
66
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.0.0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<activerecord>, [">= 2.0.0"])
|
69
|
+
end
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<activerecord>, [">= 2.0.0"])
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
|
3
|
+
describe "AliasScope" do
|
4
|
+
it "should allow alias scopes" do
|
5
|
+
User.create(:username => "bjohnson")
|
6
|
+
User.create(:username => "thunt")
|
7
|
+
User.username_has("bjohnson").all.should == User.find_all_by_username("bjohnson")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should allow alias scopes from the search object" do
|
11
|
+
search = User.search
|
12
|
+
search.username_has = "bjohnson"
|
13
|
+
search.username_has.should == "bjohnson"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
|
3
|
+
describe "Associations" do
|
4
|
+
it "should create a named scope" do
|
5
|
+
Company.users_username_like("bjohnson").proxy_options.should == User.username_like("bjohnson").proxy_options.merge(:joins => :users)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should create a deep named scope" do
|
9
|
+
Company.users_orders_total_greater_than(10).proxy_options.should == Order.total_greater_than(10).proxy_options.merge(:joins => {:users => :orders})
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not allowed named scopes on non existent association columns" do
|
13
|
+
lambda { User.users_whatever_like("bjohnson") }.should raise_error(NoMethodError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not allowed named scopes on non existent deep association columns" do
|
17
|
+
lambda { User.users_orders_whatever_like("bjohnson") }.should raise_error(NoMethodError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow named scopes to be called multiple times and reflect the value passed" do
|
21
|
+
Company.users_username_like("bjohnson").proxy_options.should == User.username_like("bjohnson").proxy_options.merge(:joins => :users)
|
22
|
+
Company.users_username_like("thunt").proxy_options.should == User.username_like("thunt").proxy_options.merge(:joins => :users)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should allow deep named scopes to be called multiple times and reflect the value passed" do
|
26
|
+
Company.users_orders_total_greater_than(10).proxy_options.should == Order.total_greater_than(10).proxy_options.merge(:joins => {:users => :orders})
|
27
|
+
Company.users_orders_total_greater_than(20).proxy_options.should == Order.total_greater_than(20).proxy_options.merge(:joins => {:users => :orders})
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have an arity of 1 if the underlying scope has an arity of 1" do
|
31
|
+
Company.users_orders_total_greater_than(10)
|
32
|
+
Company.named_scope_arity("users_orders_total_greater_than").should == Order.named_scope_arity("total_greater_than")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have an arity of nil if the underlying scope has an arity of nil" do
|
36
|
+
Company.users_orders_total_null
|
37
|
+
Company.named_scope_arity("users_orders_total_null").should == Order.named_scope_arity("total_null")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have an arity of -1 if the underlying scope has an arity of -1" do
|
41
|
+
Company.users_id_equals_any
|
42
|
+
Company.named_scope_arity("users_id_equals_any").should == User.named_scope_arity("id_equals_any")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow aliases" do
|
46
|
+
Company.users_username_contains("bjohnson").proxy_options.should == User.username_contains("bjohnson").proxy_options.merge(:joins => :users)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should allow deep aliases" do
|
50
|
+
Company.users_orders_total_gt(10).proxy_options.should == Order.total_gt(10).proxy_options.merge(:joins => {:users => :orders})
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should allow ascending" do
|
54
|
+
Company.ascend_by_users_username.proxy_options.should == User.ascend_by_username.proxy_options.merge(:joins => :users)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should allow descending" do
|
58
|
+
Company.descend_by_users_username.proxy_options.should == User.descend_by_username.proxy_options.merge(:joins => :users)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should allow deep ascending" do
|
62
|
+
Company.ascend_by_users_orders_total.proxy_options.should == Order.ascend_by_total.proxy_options.merge(:joins => {:users => :orders})
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should allow deep descending" do
|
66
|
+
Company.descend_by_users_orders_total.proxy_options.should == Order.descend_by_total.proxy_options.merge(:joins => {:users => :orders})
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should include optional associations" do
|
70
|
+
pending # this is a problem with using inner joins and left outer joins
|
71
|
+
Company.create
|
72
|
+
company = Company.create
|
73
|
+
user = company.users.create
|
74
|
+
order = user.orders.create(:total => 20, :taxes => 3)
|
75
|
+
Company.ascend_by_users_orders_total.all.should == Company.all
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should not create the same join twice" do
|
79
|
+
company = Company.create
|
80
|
+
user = company.users.create
|
81
|
+
order = user.orders.create(:total => 20, :taxes => 3)
|
82
|
+
Company.users_orders_total_gt(10).users_orders_taxes_lt(5).ascend_by_users_orders_total.all.should == Company.all
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not create the same join twice when traveling through the duplicate join" do
|
86
|
+
Company.users_username_like("bjohnson").users_orders_total_gt(100).all.should == Company.all
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not create the same join twice when traveling through the duplicate join 2" do
|
90
|
+
Company.users_orders_total_gt(100).users_orders_line_items_price_gt(20).all.should == Company.all
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should allow the use of :include when a join was created" do
|
94
|
+
company = Company.create
|
95
|
+
user = company.users.create
|
96
|
+
order = user.orders.create(:total => 20, :taxes => 3)
|
97
|
+
Company.users_orders_total_gt(10).users_orders_taxes_lt(5).ascend_by_users_orders_total.all(:include => :users).should == Company.all
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should allow the use of deep :include when a join was created" do
|
101
|
+
company = Company.create
|
102
|
+
user = company.users.create
|
103
|
+
order = user.orders.create(:total => 20, :taxes => 3)
|
104
|
+
Company.users_orders_total_gt(10).users_orders_taxes_lt(5).ascend_by_users_orders_total.all(:include => {:users => :orders}).should == Company.all
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should allow the use of :include when traveling through the duplicate join" do
|
108
|
+
company = Company.create
|
109
|
+
user = company.users.create(:username => "bjohnson")
|
110
|
+
order = user.orders.create(:total => 20, :taxes => 3)
|
111
|
+
Company.users_username_like("bjohnson").users_orders_taxes_lt(5).ascend_by_users_orders_total.all(:include => :users).should == Company.all
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should allow the use of deep :include when traveling through the duplicate join" do
|
115
|
+
company = Company.create
|
116
|
+
user = company.users.create(:username => "bjohnson")
|
117
|
+
order = user.orders.create(:total => 20, :taxes => 3)
|
118
|
+
Company.users_orders_taxes_lt(50).ascend_by_users_orders_total.all(:include => {:users => :orders}).should == Company.all
|
119
|
+
end
|
120
|
+
end
|