searchlogic 2.1.13 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ == 2.2.0 released 2009-07-30
2
+
3
+ * Refactored association code to be much simpler and rely on recursion. This allows the underlying class to do most of the work. This also allows calling any named scopes through any level of associations.
4
+
1
5
  == 2.1.13 released 2009-07-29
2
6
 
3
7
  * Applied bug fix from http://github.com/skanev/searchlogic to make #order work with association ordering.
@@ -83,7 +83,7 @@ You also get named scopes for any of your associations:
83
83
  User.ascend_by_order_total
84
84
  User.descend_by_orders_line_items_price
85
85
 
86
- Again these are just named scopes. You can chain them together, call methods off of them, etc. What's great about these named scopes is that they do NOT use the :include option, making them <em>much</em> faster. Instead they create a INNER JOIN and pass it to the :joins option, which is great for performance. To prove my point here is a quick benchmark from an application I am working on:
86
+ Again these are just named scopes. You can chain them together, call methods off of them, etc. What's great about these named scopes is that they 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
87
 
88
88
  Benchmark.bm do |x|
89
89
  x.report { 10.times { Event.tickets_id_gt(10).all(:include => :tickets) } }
@@ -97,7 +97,7 @@ If you want to use the :include option, just specify it:
97
97
 
98
98
  User.orders_line_items_price_greater_than(20).all(:include => {:orders => :line_items})
99
99
 
100
- Obviously, only do this if you want to actually use the included objects.
100
+ 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
101
 
102
102
  == Make searching and ordering data in your application trivial
103
103
 
@@ -176,7 +176,7 @@ Now just throw it in your form:
176
176
  = f.check_box :four_year_olds
177
177
  = f.submit
178
178
 
179
- What's great about this is that you can do just about anything you want. If Searchlogic doesn't provide a named scope for that crazy edge case that you need, just create your own named scope. The sky is the limit.
179
+ 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
180
 
181
181
  == Use any or all
182
182
 
@@ -199,7 +199,7 @@ If you don't like will_paginate, use another solution, or roll your own. Paginat
199
199
 
200
200
  == Conflicts with other gems
201
201
 
202
- You will notice searchlogic wants to create a method called "search". So do other libraries like thinking sphinx, etc. So searchlogic has a no conflict resolution. If the "search" method is already taken the method will be called "searchlogic" instead. So instead of
202
+ You will notice searchlogic wants to create a method called "search". So do other libraries like thinking-sphinx, etc. So searchlogic has a no conflict resolution. If the "search" method is already taken the method will be called "searchlogic" instead. So instead of
203
203
 
204
204
  User.search
205
205
 
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 13
2
+ :patch: 0
3
3
  :major: 2
4
- :minor: 1
4
+ :minor: 2
@@ -1,6 +1,7 @@
1
1
  require "searchlogic/core_ext/proc"
2
2
  require "searchlogic/core_ext/object"
3
- require "searchlogic/active_record_consistency"
3
+ require "searchlogic/active_record/consistency"
4
+ require "searchlogic/active_record/named_scopes"
4
5
  require "searchlogic/named_scopes/conditions"
5
6
  require "searchlogic/named_scopes/ordering"
6
7
  require "searchlogic/named_scopes/association_conditions"
@@ -10,10 +11,21 @@ require "searchlogic/search"
10
11
 
11
12
  Proc.send(:include, Searchlogic::CoreExt::Proc)
12
13
  Object.send(:include, Searchlogic::CoreExt::Object)
14
+
15
+ module ActiveRecord # :nodoc: all
16
+ class Base
17
+ class << self
18
+ include Searchlogic::ActiveRecord::Consistency
19
+ end
20
+ end
21
+ end
22
+
23
+ ActiveRecord::Base.extend(Searchlogic::ActiveRecord::NamedScopes)
24
+
13
25
  ActiveRecord::Base.extend(Searchlogic::NamedScopes::Conditions)
14
- ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
15
26
  ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationConditions)
16
27
  ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationOrdering)
28
+ ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
17
29
  ActiveRecord::Base.extend(Searchlogic::NamedScopes::AliasScope)
18
30
  ActiveRecord::Base.extend(Searchlogic::Search::Implementation)
19
31
 
@@ -0,0 +1,22 @@
1
+ module Searchlogic
2
+ module ActiveRecord
3
+ # Active Record is pretty inconsistent with how their SQL is constructed. This
4
+ # method attempts to close the gap between the various inconsistencies.
5
+ module Consistency
6
+ def self.included(klass)
7
+ klass.class_eval do
8
+ alias_method_chain :merge_joins, :searchlogic
9
+ end
10
+ end
11
+
12
+ # In AR multiple joins are sometimes in a single join query, and other times they
13
+ # are not. The merge_joins method in AR should account for this, but it doesn't.
14
+ # This fixes that problem. This way there is one join per string, which allows
15
+ # the merge_joins method to delete duplicates.
16
+ def merge_joins_with_searchlogic(*args)
17
+ joins = merge_joins_without_searchlogic(*args)
18
+ joins.collect { |j| j.is_a?(String) ? j.split(" ") : j }.flatten.uniq
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ module Searchlogic
2
+ module ActiveRecord
3
+ # Adds methods that give extra information about a classes named scopes.
4
+ module NamedScopes
5
+ # Retrieves the options passed when creating the respective named scope. Ex:
6
+ #
7
+ # named_scope :whatever, :conditions => {:column => value}
8
+ #
9
+ # This method will return:
10
+ #
11
+ # :conditions => {:column => value}
12
+ #
13
+ # ActiveRecord hides this internally in a Proc, so we have to try and pull it out with this
14
+ # method.
15
+ def named_scope_options(name)
16
+ key = scopes.key?(name.to_sym) ? name.to_sym : primary_condition_name(name)
17
+
18
+ if key
19
+ eval("options", scopes[key].binding)
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ # The arity for a named scope's proc is important, because we use the arity
26
+ # to determine if the condition should be ignored when calling the search method.
27
+ # If the condition is false and the arity is 0, then we skip it all together. Ex:
28
+ #
29
+ # User.named_scope :age_is_4, :conditions => {:age => 4}
30
+ # User.search(:age_is_4 => false) == User.all
31
+ # User.search(:age_is_4 => true) == User.all(:conditions => {:age => 4})
32
+ #
33
+ # We also use it when trying to "copy" the underlying named scope for association
34
+ # conditions. This way our aliased scope accepts the same number of parameters for
35
+ # the underlying scope.
36
+ def named_scope_arity(name)
37
+ options = named_scope_options(name)
38
+ options.respond_to?(:arity) ? options.arity : nil
39
+ end
40
+
41
+ # A convenience method for creating inner join sql to that your inner joins
42
+ # are consistent with how Active Record creates them. Basically a tool for
43
+ # you to use when writing your own named scopes. This way you know for sure
44
+ # that duplicate joins will be removed when chaining scopes together that
45
+ # use the same join.
46
+ def inner_joins(association_name)
47
+ ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, association_name, nil).join_associations.collect { |assoc| assoc.association_join }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -48,6 +48,7 @@ module Searchlogic
48
48
  end
49
49
 
50
50
  def alias_scope?(name) # :nodoc:
51
+ return false if name.blank?
51
52
  alias_scopes.key?(name.to_sym)
52
53
  end
53
54
 
@@ -3,7 +3,7 @@ module Searchlogic
3
3
  # Handles dynamically creating named scopes for associations.
4
4
  module AssociationConditions
5
5
  def condition?(name) # :nodoc:
6
- super || association_condition?(name) || association_alias_condition?(name)
6
+ super || association_condition?(name)
7
7
  end
8
8
 
9
9
  def primary_condition_name(name) # :nodoc:
@@ -11,40 +11,19 @@ module Searchlogic
11
11
  result
12
12
  elsif association_condition?(name)
13
13
  name.to_sym
14
- elsif details = association_alias_condition_details(name)
15
- "#{details[:association]}_#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
16
14
  else
17
15
  nil
18
16
  end
19
17
  end
20
18
 
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 named of the method a valid 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
- # A convenience method for creating inner join sql to that your inner joins
33
- # are consistent with how Active Record creates them. Basically a tool for
34
- # you to use when writing your own named scopes. This way you know for sure
35
- # that duplicate joins will be removed when chaining scopes together that
36
- # use the same join.
37
- def inner_joins(association_name)
38
- ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, association_name, nil).join_associations.collect { |assoc| assoc.association_join }
39
- end
40
-
41
19
  private
20
+ def association_condition?(name)
21
+ !association_condition_details(name).nil?
22
+ end
23
+
42
24
  def method_missing(name, *args, &block)
43
25
  if details = association_condition_details(name)
44
- create_association_condition(details[:association], details[:column], details[:condition], args)
45
- send(name, *args)
46
- elsif details = association_alias_condition_details(name)
47
- create_association_alias_condition(details[:association], details[:column], details[:condition], args)
26
+ create_association_condition(details[:association], details[:condition], args)
48
27
  send(name, *args)
49
28
  else
50
29
  super
@@ -52,51 +31,24 @@ module Searchlogic
52
31
  end
53
32
 
54
33
  def association_condition_details(name)
55
- assocs = non_polymorphic_associations
34
+ assocs = reflect_on_all_associations.reject { |assoc| assoc.options[:polymorphic] }
56
35
  return nil if assocs.empty?
57
- regexes = [association_searchlogic_regex(assocs, Conditions::PRIMARY_CONDITIONS)]
58
- assocs.each do |assoc|
59
- scope_names = assoc.klass.scopes.keys + assoc.klass.alias_scopes.keys
60
- scope_names.uniq!
61
- scope_names.delete(:scoped)
62
- next if scope_names.empty?
63
- regexes << /^(#{assoc.name})_(#{scope_names.join("|")})$/
64
- end
65
36
 
66
- if !local_condition?(name) && regexes.any? { |regex| name.to_s =~ regex }
67
- {:association => $1, :column => $2, :condition => $3}
68
- end
69
- end
70
-
71
- def create_association_condition(association_name, column, condition, args)
72
- name_parts = [column, condition].compact
73
- condition_name = name_parts.join("_")
74
- named_scope("#{association_name}_#{condition_name}", association_condition_options(association_name, condition_name, args))
75
- end
76
-
77
- def association_alias_condition_details(name)
78
- assocs = non_polymorphic_associations
79
- return nil if assocs.empty?
80
-
81
- if !local_condition?(name) && name.to_s =~ association_searchlogic_regex(assocs, Conditions::ALIAS_CONDITIONS)
82
- {:association => $1, :column => $2, :condition => $3}
37
+ if name.to_s =~ /^(#{assocs.collect(&:name).join("|")})_(\w+)$/
38
+ association_name = $1
39
+ condition = $2
40
+ association = reflect_on_association(association_name.to_sym)
41
+ klass = association.klass
42
+ if klass.condition?(condition)
43
+ {:association => $1, :condition => $2}
44
+ else
45
+ nil
46
+ end
83
47
  end
84
48
  end
85
49
 
86
- def non_polymorphic_associations
87
- reflect_on_all_associations.reject { |assoc| assoc.options[:polymorphic] }
88
- end
89
-
90
- def association_searchlogic_regex(assocs, condition_names)
91
- /^(#{assocs.collect(&:name).join("|")})_(\w+)_(#{condition_names.join("|")})$/
92
- end
93
-
94
- def create_association_alias_condition(association, column, condition, args)
95
- primary_condition = primary_condition(condition)
96
- alias_name = "#{association}_#{column}_#{condition}"
97
- primary_name = "#{association}_#{column}_#{primary_condition}"
98
- send(primary_name, *args) # go back to method_missing and make sure we create the method
99
- (class << self; self; end).class_eval { alias_method alias_name, primary_name }
50
+ def create_association_condition(association, condition, args)
51
+ named_scope("#{association}_#{condition}", association_condition_options(association, condition, args))
100
52
  end
101
53
 
102
54
  def association_condition_options(association_name, association_condition, args)
@@ -2,14 +2,28 @@ module Searchlogic
2
2
  module NamedScopes
3
3
  # Handles dynamically creating named scopes for associations.
4
4
  module AssociationOrdering
5
- def association_ordering_condition?(name)
6
- !association_ordering_condition_details(name).nil?
5
+ def condition?(name) # :nodoc:
6
+ super || association_ordering_condition?(name)
7
+ end
8
+
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
7
17
  end
8
18
 
9
19
  private
20
+ def association_ordering_condition?(name)
21
+ !association_ordering_condition_details(name).nil?
22
+ end
23
+
10
24
  def method_missing(name, *args, &block)
11
25
  if details = association_ordering_condition_details(name)
12
- create_association_ordering_condition(details[:association], details[:order_as], details[:column], args)
26
+ create_association_ordering_condition(details[:association], details[:order_as], details[:condition], args)
13
27
  send(name, *args)
14
28
  else
15
29
  super
@@ -18,13 +32,13 @@ module Searchlogic
18
32
 
19
33
  def association_ordering_condition_details(name)
20
34
  associations = reflect_on_all_associations.collect { |assoc| assoc.name }
21
- if !local_condition?(name) && name.to_s =~ /^(ascend|descend)_by_(#{associations.join("|")})_(\w+)$/
22
- {:order_as => $1, :association => $2, :column => $3}
35
+ if name.to_s =~ /^(ascend|descend)_by_(#{associations.join("|")})_(\w+)$/
36
+ {:order_as => $1, :association => $2, :condition => $3}
23
37
  end
24
38
  end
25
39
 
26
- def create_association_ordering_condition(association_name, order_as, column, args)
27
- named_scope("#{order_as}_by_#{association_name}_#{column}", association_condition_options(association_name, "#{order_as}_by_#{column}", args))
40
+ def create_association_ordering_condition(association, order_as, condition, args)
41
+ named_scope("#{order_as}_by_#{association}_#{condition}", association_condition_options(association, "#{order_as}_by_#{condition}", args))
28
42
  end
29
43
  end
30
44
  end
@@ -40,42 +40,6 @@ module Searchlogic
40
40
  PRIMARY_CONDITIONS = CONDITIONS.keys
41
41
  ALIAS_CONDITIONS = CONDITIONS.values.flatten
42
42
 
43
- # Retrieves the options passed when creating the respective named scope. Ex:
44
- #
45
- # named_scope :whatever, :conditions => {:column => value}
46
- #
47
- # This method will return:
48
- #
49
- # :conditions => {:column => value}
50
- #
51
- # ActiveRecord hides this internally in a Proc, so we have to try and pull it out with this
52
- # method.
53
- def named_scope_options(name)
54
- key = scopes.key?(name.to_sym) ? name.to_sym : primary_condition_name(name)
55
-
56
- if key
57
- eval("options", scopes[key])
58
- else
59
- nil
60
- end
61
- end
62
-
63
- # The arity for a named scope's proc is important, because we use the arity
64
- # to determine if the condition should be ignored when calling the search method.
65
- # If the condition is false and the arity is 0, then we skip it all together. Ex:
66
- #
67
- # User.named_scope :age_is_4, :conditions => {:age => 4}
68
- # User.search(:age_is_4 => false) == User.all
69
- # User.search(:age_is_4 => true) == User.all(:conditions => {:age => 4})
70
- #
71
- # We also use it when trying to "copy" the underlying named scope for association
72
- # conditions. This way our aliased scope accepts the same number of parameters for
73
- # the underlying scope.
74
- def named_scope_arity(name)
75
- options = named_scope_options(name)
76
- options.respond_to?(:arity) ? options.arity : nil
77
- end
78
-
79
43
  # Returns the primary condition for the given alias. Ex:
80
44
  #
81
45
  # primary_condition(:gt) => :greater_than
@@ -90,10 +54,12 @@ module Searchlogic
90
54
  # primary_condition_name(:id_gt) => :id_greater_than
91
55
  # primary_condition_name(:id_greater_than) => :id_greater_than
92
56
  def primary_condition_name(name)
93
- if primary_condition?(name)
94
- name.to_sym
95
- elsif details = alias_condition_details(name)
96
- "#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
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
97
63
  else
98
64
  nil
99
65
  end
@@ -101,48 +67,38 @@ module Searchlogic
101
67
 
102
68
  # Is the name of the method a valid condition that can be dynamically created?
103
69
  def condition?(name)
104
- local_condition?(name)
105
- end
106
-
107
- # Is the condition for a local column, not an association
108
- def local_condition?(name)
109
- primary_condition?(name) || alias_condition?(name)
110
- end
111
-
112
- # Is the name of the method a valid condition that can be dynamically created,
113
- # AND is it a primary condition (not an alias). "greater_than" not "gt".
114
- def primary_condition?(name)
115
- !primary_condition_details(name).nil?
116
- end
117
-
118
- # Is the name of the method a valid condition that can be dynamically created,
119
- # AND is it an alias condition. "gt" not "greater_than".
120
- def alias_condition?(name)
121
- !alias_condition_details(name).nil?
70
+ return false if name.blank?
71
+ scope_names = scopes.keys.reject { |k| k == :scoped }
72
+ scope_names.include?(name.to_sym) || !condition_details(name).nil?
122
73
  end
123
74
 
124
75
  private
125
76
  def method_missing(name, *args, &block)
126
- if details = primary_condition_details(name)
127
- create_primary_condition(details[:column], details[:condition])
128
- send(name, *args)
129
- elsif details = alias_condition_details(name)
130
- create_alias_condition(details[:column], details[:condition], args)
77
+ if details = condition_details(name)
78
+ create_condition(details[:column], details[:condition], args)
131
79
  send(name, *args)
132
80
  else
133
81
  super
134
82
  end
135
83
  end
136
84
 
137
- def primary_condition_details(name)
138
- if name.to_s =~ /^(#{column_names.join("|")})_(#{PRIMARY_CONDITIONS.join("|")})$/
85
+ def condition_details(name)
86
+ if name.to_s =~ /^(#{column_names.join("|")})_(#{(PRIMARY_CONDITIONS + ALIAS_CONDITIONS).join("|")})$/
139
87
  {:column => $1, :condition => $2}
140
88
  end
141
89
  end
142
90
 
91
+ def create_condition(column, condition, args)
92
+ if PRIMARY_CONDITIONS.include?(condition.to_sym)
93
+ create_primary_condition(column, condition)
94
+ elsif ALIAS_CONDITIONS.include?(condition.to_sym)
95
+ create_alias_condition(column, condition, args)
96
+ end
97
+ end
98
+
143
99
  def create_primary_condition(column, condition)
144
100
  column_type = columns_hash[column.to_s].type
145
- match_keyword = ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
101
+ match_keyword = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
146
102
 
147
103
  scope_options = case condition.to_s
148
104
  when /^equals/
@@ -218,12 +174,6 @@ module Searchlogic
218
174
  end
219
175
  end
220
176
 
221
- def alias_condition_details(name)
222
- if name.to_s =~ /^(#{column_names.join("|")})_(#{ALIAS_CONDITIONS.join("|")})$/
223
- {:column => $1, :condition => $2}
224
- end
225
- end
226
-
227
177
  def create_alias_condition(column, condition, args)
228
178
  primary_condition = primary_condition(condition)
229
179
  alias_name = "#{column}_#{condition}"
@@ -2,59 +2,49 @@ module Searchlogic
2
2
  module NamedScopes
3
3
  # Handles dynamically creating named scopes for orderin by columns.
4
4
  module Ordering
5
- def local_condition?(name) # :nodoc:
6
- super || order_condition?(name)
5
+ def condition?(name) # :nodoc:
6
+ super || ordering_condition?(name)
7
7
  end
8
8
 
9
9
  def primary_condition_name(name) # :nodoc
10
10
  if result = super
11
11
  result
12
- elsif order_condition?(name)
12
+ elsif ordering_condition?(name)
13
13
  name.to_sym
14
14
  else
15
15
  nil
16
16
  end
17
17
  end
18
18
 
19
- def order_condition?(name) # :nodoc:
20
- !order_condition_details(name).nil?
21
- end
22
-
23
- def custom_order_condition?(name) # :nodoc:
24
- !custom_order_condition_details(name).nil?
25
- end
26
-
27
19
  private
20
+ def ordering_condition?(name) # :nodoc:
21
+ !ordering_condition_details(name).nil?
22
+ end
23
+
28
24
  def method_missing(name, *args, &block)
29
25
  if name == :order
30
26
  named_scope name, lambda { |scope_name|
31
- return {} if !order_condition?(scope_name) && !custom_order_condition?(scope_name) && !association_ordering_condition?(scope_name)
27
+ return {} if !condition?(scope_name)
32
28
  send(scope_name).proxy_options
33
29
  }
34
30
  send(name, *args)
35
- elsif details = order_condition_details(name)
36
- create_order_conditions(details[:column])
31
+ elsif details = ordering_condition_details(name)
32
+ create_ordering_conditions(details[:column])
37
33
  send(name, *args)
38
34
  else
39
35
  super
40
36
  end
41
37
  end
42
38
 
43
- def order_condition_details(name)
39
+ def ordering_condition_details(name)
44
40
  if name.to_s =~ /^(ascend|descend)_by_(#{column_names.join("|")})$/
45
41
  {:order_as => $1, :column => $2}
46
42
  elsif name.to_s =~ /^order$/
47
43
  {}
48
44
  end
49
45
  end
50
-
51
- def custom_order_condition_details(name)
52
- if name.to_s =~ /^(ascend|descend)_by_(.+)$/
53
- {:order_as => $1, :scope => name.to_sym}
54
- end
55
- end
56
46
 
57
- def create_order_conditions(column)
47
+ def create_ordering_conditions(column)
58
48
  named_scope("ascend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} ASC"})
59
49
  named_scope("descend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} DESC"})
60
50
  end
@@ -17,7 +17,7 @@ module Searchlogic
17
17
  class Search
18
18
  # Responsible for adding a "search" method into your models.
19
19
  module Implementation
20
- # Additional method, gets aliases as "search" if that method
20
+ # Additional method, gets aliased as "search" if that method
21
21
  # is available. A lot of other libraries like to use "search"
22
22
  # as well, so if you have a conflict like this, you can use
23
23
  # this method directly.
@@ -141,7 +141,7 @@ module Searchlogic
141
141
  else
142
142
  # Let's leverage ActiveRecord's type casting, so that casting is consistent
143
143
  # with the other models.
144
- column_for_type_cast = ActiveRecord::ConnectionAdapters::Column.new("", nil)
144
+ column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
145
145
  column_for_type_cast.instance_variable_set(:@type, type)
146
146
  value = column_for_type_cast.type_cast(value)
147
147
  Time.zone && value.is_a?(Time) ? value.in_time_zone : value
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{searchlogic}
5
- s.version = "2.1.13"
5
+ s.version = "2.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Ben Johnson of Binary Logic"]
9
- s.date = %q{2009-07-29}
9
+ s.date = %q{2009-07-30}
10
10
  s.description = %q{Searchlogic provides common named scopes and object based searching for ActiveRecord.}
11
11
  s.email = %q{bjohnson@binarylogic.com}
12
12
  s.extra_rdoc_files = [
@@ -22,7 +22,8 @@ Gem::Specification.new do |s|
22
22
  "VERSION.yml",
23
23
  "init.rb",
24
24
  "lib/searchlogic.rb",
25
- "lib/searchlogic/active_record_consistency.rb",
25
+ "lib/searchlogic/active_record/consistency.rb",
26
+ "lib/searchlogic/active_record/named_scopes.rb",
26
27
  "lib/searchlogic/core_ext/object.rb",
27
28
  "lib/searchlogic/core_ext/proc.rb",
28
29
  "lib/searchlogic/named_scopes/alias_scope.rb",
@@ -1,6 +1,10 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
2
 
3
3
  describe "AliasScope" do
4
+ before(:each) do
5
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
6
+ end
7
+
4
8
  it "should allow alias scopes" do
5
9
  User.create(:username => "bjohnson")
6
10
  User.create(:username => "thunt")
@@ -10,14 +10,22 @@ describe "Association Conditions" do
10
10
  end
11
11
 
12
12
  it "should allow the use of foreign pre-existing named scopes" do
13
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
13
14
  Company.users_uname("bjohnson").proxy_options.should == User.uname("bjohnson").proxy_options.merge(:joins => :users)
14
15
  end
15
16
 
17
+ it "should allow the use of deep foreign pre-existing named scopes" do
18
+ Order.named_scope :big_id, :conditions => "orders.id > 100"
19
+ Company.users_orders_big_id.proxy_options.should == Order.big_id.proxy_options.merge(:joins => {:users => :orders})
20
+ end
21
+
16
22
  it "should allow the use of foreign pre-existing alias scopes" do
23
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
17
24
  Company.users_username_has("bjohnson").proxy_options.should == User.username_has("bjohnson").proxy_options.merge(:joins => :users)
18
25
  end
19
26
 
20
27
  it "should not raise errors for scopes that don't return anything" do
28
+ User.alias_scope :blank_scope, lambda { |value| }
21
29
  Company.users_blank_scope("bjohnson").proxy_options.should == {:joins => :users}
22
30
  end
23
31
 
@@ -203,9 +203,6 @@ describe "Conditions" do
203
203
  end
204
204
 
205
205
  it "should have is_not" do
206
- # This is matching "not" first. How do you give priority in a regex? Because it's matching the
207
- # 'not' condition and thinking the column is 'age_is'.
208
- pending
209
206
  User.age_is_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
210
207
  end
211
208
 
@@ -110,12 +110,14 @@ describe "Search" do
110
110
  end
111
111
 
112
112
  it "should allow setting pre-existing association conditions" do
113
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
113
114
  search = Company.search
114
115
  search.users_uname = "bjohnson"
115
116
  search.users_uname.should == "bjohnson"
116
117
  end
117
118
 
118
119
  it "should allow setting pre-existing association alias conditions" do
120
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
119
121
  search = Company.search
120
122
  search.users_username_has = "bjohnson"
121
123
  search.users_username_has.should == "bjohnson"
@@ -158,6 +160,11 @@ describe "Search" do
158
160
  lambda { search.unknown = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
159
161
  end
160
162
 
163
+ it "should not allow setting conditions on sensitive methods" do
164
+ search = User.search
165
+ lambda { search.destroy = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
166
+ end
167
+
161
168
  it "should not use the ruby implementation of the id method" do
162
169
  search = User.search
163
170
  search.id.should be_nil
@@ -71,9 +71,6 @@ 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
- named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
75
- alias_scope :username_has, lambda { |value| username_like(value) }
76
- alias_scope :blank_scope, lambda { |value| }
77
74
  end
78
75
 
79
76
  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.1.13
4
+ version: 2.2.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-07-29 00:00:00 -04:00
12
+ date: 2009-07-30 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,8 @@ files:
40
40
  - VERSION.yml
41
41
  - init.rb
42
42
  - lib/searchlogic.rb
43
- - lib/searchlogic/active_record_consistency.rb
43
+ - lib/searchlogic/active_record/consistency.rb
44
+ - lib/searchlogic/active_record/named_scopes.rb
44
45
  - lib/searchlogic/core_ext/object.rb
45
46
  - lib/searchlogic/core_ext/proc.rb
46
47
  - lib/searchlogic/named_scopes/alias_scope.rb
@@ -1,28 +0,0 @@
1
- module Searchlogic
2
- # Active Record is pretty inconsistent with how their SQL is constructed. This
3
- # method attempts to close the gap between the various inconsistencies.
4
- module ActiveRecordConsistency
5
- def self.included(klass)
6
- klass.class_eval do
7
- alias_method_chain :merge_joins, :searchlogic
8
- end
9
- end
10
-
11
- # In AR multiple joins are sometimes in a single join query, and other times they
12
- # are not. The merge_joins method in AR should account for this, but it doesn't.
13
- # This fixes that problem. This way there is one join per string, which allows
14
- # the merge_joins method to delete duplicates.
15
- def merge_joins_with_searchlogic(*args)
16
- joins = merge_joins_without_searchlogic(*args)
17
- joins.collect { |j| j.is_a?(String) ? j.split(" ") : j }.flatten.uniq
18
- end
19
- end
20
- end
21
-
22
- module ActiveRecord # :nodoc: all
23
- class Base
24
- class << self
25
- include Searchlogic::ActiveRecordConsistency
26
- end
27
- end
28
- end