joost-searchlogic 2.1.7.1 → 2.2.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,52 @@
1
+ <<<<<<< HEAD:CHANGELOG.rdoc
1
2
  == 2.1.7
2
3
 
4
+ =======
5
+ == 2.2.3 released 2009-07-31
6
+
7
+ * Fixed bug when an associations named scope joins is a string or an array of strings, the joins we add in automatically should also be a string, not a symbol.
8
+
9
+ == 2.2.2 released 2009-07-31
10
+
11
+ * Fix bug to give priority to local columns.
12
+
13
+ == 2.2.1 released 2009-07-30
14
+
15
+ * Use ::ActiveRecord instead of ActiveRecord to avoid a name conflict since ActiveRecord is a module within Searchlogic.
16
+
17
+ == 2.2.0 released 2009-07-30
18
+
19
+ * 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.
20
+
21
+ == 2.1.13 released 2009-07-29
22
+
23
+ * Applied bug fix from http://github.com/skanev/searchlogic to make #order work with association ordering.
24
+ * Applied bug fix to allow for custom ordering conditions.
25
+
26
+ == 2.1.12 released 2009-07-28
27
+
28
+ * Fixed bug when dealing with scopes that return nil.
29
+
30
+ == 2.1.11 released 2009-07-28
31
+
32
+ * Reworked how alias conditions are created on the fly, uses scope(:find) instead of proxy_options to create the scope. This allows using association alias named scopes.
33
+
34
+ == 2.1.10 released 2009-07-28
35
+
36
+ * Ignore polymorphic associations when dynamically creating conditions on associations.
37
+
38
+ == 2.1.9 released 2009-07-28
39
+
40
+ * Fixed bug when cloning with no scope
41
+ * Allow the call of foreign pre-existing named scopes instead of those generated by searchlogic. Allows you to call named scopes on associations that you define yourself.
42
+
43
+ == 2.1.8 released 2009-07-15
44
+
45
+ * Added support for not_like, not_begin_with, not_end_with, and not_null
46
+
47
+ == 2.1.7 released 2009-07-14
48
+
49
+ >>>>>>> searchlogic/master:CHANGELOG.rdoc
3
50
  * Add support for time zones in the Search class when type casting to Time objects.
4
51
 
5
52
  == 2.1.6 released 2009-07-13
@@ -9,8 +9,6 @@ Changes made:
9
9
 
10
10
  = Searchlogic
11
11
 
12
- <b>Searchlogic has been <em>completely</em> rewritten for v2. It is much simpler and has taken an entirely new approach. To give you an idea, v1 had ~2300 lines of code, v2 has ~420 lines of code.</b>
13
-
14
12
  Searchlogic provides common named scopes and object based searching for ActiveRecord.
15
13
 
16
14
  == Helpful links
@@ -94,7 +92,7 @@ You also get named scopes for any of your associations:
94
92
  User.ascend_by_order_total
95
93
  User.descend_by_orders_line_items_price
96
94
 
97
- 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:
95
+ 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:
98
96
 
99
97
  Benchmark.bm do |x|
100
98
  x.report { 10.times { Event.tickets_id_gt(10).all(:include => :tickets) } }
@@ -108,7 +106,7 @@ If you want to use the :include option, just specify it:
108
106
 
109
107
  User.orders_line_items_price_greater_than(20).all(:include => {:orders => :line_items})
110
108
 
111
- Obviously, only do this if you want to actually use the included objects.
109
+ 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.
112
110
 
113
111
  == Make searching and ordering data in your application trivial
114
112
 
@@ -201,7 +199,7 @@ Now just throw it in your form:
201
199
  = f.check_box :four_year_olds
202
200
  = f.submit
203
201
 
204
- 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.
202
+ 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.
205
203
 
206
204
  == Use any or all
207
205
 
@@ -224,7 +222,7 @@ If you don't like will_paginate, use another solution, or roll your own. Paginat
224
222
 
225
223
  == Conflicts with other gems
226
224
 
227
- 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
225
+ 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
228
226
 
229
227
  User.search
230
228
 
data/Rakefile CHANGED
@@ -6,6 +6,7 @@ begin
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "searchlogic"
8
8
  gem.summary = "Searchlogic provides common named scopes and object based searching for ActiveRecord."
9
+ gem.description = "Searchlogic provides common named scopes and object based searching for ActiveRecord."
9
10
  gem.email = "bjohnson@binarylogic.com"
10
11
  gem.homepage = "http://github.com/binarylogic/searchlogic"
11
12
  gem.authors = ["Ben Johnson of Binary Logic"]
@@ -1,4 +1,4 @@
1
1
  ---
2
+ :patch: 3
2
3
  :major: 2
3
- :minor: 1
4
4
  :patch: 7
@@ -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,6 +11,17 @@ 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
26
  ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
15
27
  ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationConditions)
@@ -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
@@ -27,8 +27,10 @@ module Searchlogic
27
27
  #
28
28
  # named_scope :id_gt, searchlogic_lambda(:integer) { |value| {:conditions => ["id > ?", value]} }
29
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.
30
+ # If you are wanting a string, you don't have to do anything, because Searchlogic assumes you want a string.
31
+ # If you want something else, you need to specify it as I did in the above example. Comments are appreciated
32
+ # on this, if you know of a better solution please let me know. But this is the best I could come up with,
33
+ # without being intrusive and altering default behavior.
32
34
  def searchlogic_lambda(type = :string, &block)
33
35
  proc = lambda(&block)
34
36
  proc.searchlogic_arg_type = type
@@ -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
 
@@ -13,17 +13,22 @@ module Searchlogic
13
13
 
14
14
  WILDCARD_CONDITIONS = {
15
15
  :like => [:contains, :includes],
16
+ :not_like => [],
16
17
  :begins_with => [:bw],
18
+ :not_begin_with => [:does_not_begin_with],
17
19
  :ends_with => [:ew],
20
+ :not_end_with => [:does_not_end_with]
18
21
  }
19
22
 
20
23
  BOOLEAN_CONDITIONS = {
21
24
  :null => [:nil],
25
+ :not_null => [:not_nil],
22
26
  :empty => []
23
27
  }
24
28
 
25
29
  CONDITIONS = {}
26
30
 
31
+ # Add any / all variations to every comparison and wildcard condition
27
32
  COMPARISON_CONDITIONS.merge(WILDCARD_CONDITIONS).each do |condition, aliases|
28
33
  CONDITIONS[condition] = aliases
29
34
  CONDITIONS["#{condition}_any".to_sym] = aliases.collect { |a| "#{a}_any".to_sym }
@@ -35,41 +40,6 @@ module Searchlogic
35
40
  PRIMARY_CONDITIONS = CONDITIONS.keys
36
41
  ALIAS_CONDITIONS = CONDITIONS.values.flatten
37
42
 
38
- # Retrieves the options passed when creating the respective named scope. Ex:
39
- #
40
- # named_scope :whatever, :conditions => {:column => value}
41
- #
42
- # This method will return:
43
- #
44
- # :conditions => {:column => value}
45
- #
46
- # ActiveRecord hides this internally, so we have to try and pull it out with this
47
- # method.
48
- def named_scope_options(name)
49
- key = scopes.key?(name.to_sym) ? name.to_sym : primary_condition_name(name)
50
-
51
- if key
52
- eval("options", scopes[key])
53
- else
54
- nil
55
- end
56
- end
57
-
58
- # The arity for a named scope's proc is important, because we use the arity
59
- # to determine if the condition should be ignored when calling the search method.
60
- # If the condition is false and the arity is 0, then we skip it all together. Ex:
61
- #
62
- # User.named_scope :age_is_4, :conditions => {:age => 4}
63
- # User.search(:age_is_4 => false) == User.all
64
- # User.search(:age_is_4 => true) == User.all(:conditions => {:age => 4})
65
- #
66
- # We also use it when trying to "copy" the underlying named scope for association
67
- # conditions.
68
- def named_scope_arity(name)
69
- options = named_scope_options(name)
70
- options.respond_to?(:arity) ? options.arity : nil
71
- end
72
-
73
43
  # Returns the primary condition for the given alias. Ex:
74
44
  #
75
45
  # primary_condition(:gt) => :greater_than
@@ -84,10 +54,12 @@ module Searchlogic
84
54
  # primary_condition_name(:id_gt) => :id_greater_than
85
55
  # primary_condition_name(:id_greater_than) => :id_greater_than
86
56
  def primary_condition_name(name)
87
- if primary_condition?(name)
88
- name.to_sym
89
- elsif details = alias_condition_details(name)
90
- "#{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
91
63
  else
92
64
  nil
93
65
  end
@@ -116,26 +88,39 @@ module Searchlogic
116
88
  end
117
89
 
118
90
  private
91
+ def local_condition?(name)
92
+ return false if name.blank?
93
+ scope_names = scopes.keys.reject { |k| k == :scoped }
94
+ scope_names.include?(name.to_sym) || !condition_details(name).nil?
95
+ end
96
+
119
97
  def method_missing(name, *args, &block)
120
- if details = primary_condition_details(name)
121
- create_primary_condition(details[:column], details[:condition])
122
- send(name, *args)
123
- elsif details = alias_condition_details(name)
124
- create_alias_condition(details[:column], details[:condition], args)
98
+ if details = condition_details(name)
99
+ create_condition(details[:column], details[:condition], args)
125
100
  send(name, *args)
126
101
  else
127
102
  super
128
103
  end
129
104
  end
130
105
 
131
- def primary_condition_details(name)
132
- if name.to_s =~ /^(#{column_names.join("|")})_(#{PRIMARY_CONDITIONS.join("|")})$/
106
+ def condition_details(name)
107
+ if name.to_s =~ /^(#{column_names.join("|")})_(#{(PRIMARY_CONDITIONS + ALIAS_CONDITIONS).join("|")})$/
133
108
  {:column => $1, :condition => $2}
134
109
  end
135
110
  end
136
111
 
112
+ def create_condition(column, condition, args)
113
+ if PRIMARY_CONDITIONS.include?(condition.to_sym)
114
+ create_primary_condition(column, condition)
115
+ elsif ALIAS_CONDITIONS.include?(condition.to_sym)
116
+ create_alias_condition(column, condition, args)
117
+ end
118
+ end
119
+
137
120
  def create_primary_condition(column, condition)
138
121
  column_type = columns_hash[column.to_s].type
122
+ match_keyword = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
123
+
139
124
  scope_options = case condition.to_s
140
125
  when /^equals/
141
126
  scope_options(condition, column_type, "#{table_name}.#{column} = ?")
@@ -150,13 +135,21 @@ module Searchlogic
150
135
  when /^greater_than/
151
136
  scope_options(condition, column_type, "#{table_name}.#{column} > ?")
152
137
  when /^like/
153
- scope_options(condition, column_type, "#{table_name}.#{column} LIKE ?", :like)
138
+ scope_options(condition, column_type, "#{table_name}.#{column} #{match_keyword} ?", :like)
139
+ when /^not_like/
140
+ scope_options(condition, column_type, "#{table_name}.#{column} NOT #{match_keyword} ?", :like)
154
141
  when /^begins_with/
155
- scope_options(condition, column_type, "#{table_name}.#{column} LIKE ?", :begins_with)
142
+ scope_options(condition, column_type, "#{table_name}.#{column} #{match_keyword} ?", :begins_with)
143
+ when /^not_begin_with/
144
+ scope_options(condition, column_type, "#{table_name}.#{column} NOT #{match_keyword} ?", :begins_with)
156
145
  when /^ends_with/
157
- scope_options(condition, column_type, "#{table_name}.#{column} LIKE ?", :ends_with)
146
+ scope_options(condition, column_type, "#{table_name}.#{column} #{match_keyword} ?", :ends_with)
147
+ when /^not_end_with/
148
+ scope_options(condition, column_type, "#{table_name}.#{column} NOT #{match_keyword} ?", :ends_with)
158
149
  when "null"
159
150
  {:conditions => "#{table_name}.#{column} IS NULL"}
151
+ when "not_null"
152
+ {:conditions => "#{table_name}.#{column} IS NOT NULL"}
160
153
  when "empty"
161
154
  {:conditions => "#{table_name}.#{column} = ''"}
162
155
  end
@@ -202,12 +195,6 @@ module Searchlogic
202
195
  end
203
196
  end
204
197
 
205
- def alias_condition_details(name)
206
- if name.to_s =~ /^(#{column_names.join("|")})_(#{ALIAS_CONDITIONS.join("|")})$/
207
- {:column => $1, :condition => $2}
208
- end
209
- end
210
-
211
198
  def create_alias_condition(column, condition, args)
212
199
  primary_condition = primary_condition(condition)
213
200
  alias_name = "#{column}_#{condition}"
@@ -9,27 +9,27 @@ module Searchlogic
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
19
  private
20
+ def ordering_condition?(name) # :nodoc:
21
+ !ordering_condition_details(name).nil?
22
+ end
23
+
24
24
  def method_missing(name, *args, &block)
25
25
  if name == :order
26
26
  named_scope name, lambda { |scope_name|
27
- return {} if !order_condition?(scope_name)
27
+ return {} if !condition?(scope_name)
28
28
  send(scope_name).proxy_options
29
29
  }
30
30
  send(name, *args)
31
- elsif details = order_condition_details(name)
32
- create_order_conditions(details[:column])
31
+ elsif details = ordering_condition_details(name)
32
+ create_ordering_conditions(details[:column])
33
33
  send(name, *args)
34
34
  else
35
35
  super
@@ -44,7 +44,7 @@ module Searchlogic
44
44
  end
45
45
  end
46
46
 
47
- def create_order_conditions(column)
47
+ def create_ordering_conditions(column)
48
48
  named_scope("ascend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} ASC"})
49
49
  named_scope("descend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} DESC"})
50
50
  end
@@ -19,6 +19,7 @@ module ActionView
19
19
  # * <tt>:as</tt> - the text used in the link, defaults to whatever is passed to :by
20
20
  # * <tt>:ascend_scope</tt> - what scope to call for ascending the data, defaults to "ascend_by_:by"
21
21
  # * <tt>:descend_scope</tt> - what scope to call for descending the data, defaults to "descend_by_:by"
22
+ # * <tt>:params</tt> - hash with additional params which will be added to generated url
22
23
  # * <tt>:params_scope</tt> - the name of the params key to scope the order condition by, defaults to :search
23
24
  # * <tt>:default_scope</tt> - either :asc or :desc, defaults to ascend_scope
24
25
  # * <tt>:arrow</tt> - set to true if you want an ascii arrow showing the ordering
@@ -45,7 +46,10 @@ module ActionView
45
46
  else
46
47
  new_scope = (options[:default_scope] == :desc) ? options[:descend_scope] : options[:ascend_scope]
47
48
  end
48
- link_to options[:as], url_for(options[:params_scope] => search.conditions.merge( { :order => new_scope } ) ), html_options
49
+ url_options = {
50
+ options[:params_scope] => search.conditions.merge( { :order => new_scope } )
51
+ }.deep_merge(options[:params] || {})
52
+ link_to options[:as], url_for(url_options), html_options
49
53
  end
50
54
 
51
55
  # Automatically makes the form method :get if a Searchlogic::Search and sets
@@ -73,4 +77,4 @@ module ActionView
73
77
  end
74
78
 
75
79
  end
76
- end
80
+ 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.
@@ -51,7 +51,7 @@ module Searchlogic
51
51
  end
52
52
 
53
53
  def clone
54
- self.class.new(klass, current_scope.clone, conditions.clone)
54
+ self.class.new(klass, current_scope && current_scope.clone, conditions.clone)
55
55
  end
56
56
 
57
57
  # Returns a hash of the current conditions set.
@@ -141,11 +141,11 @@ 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
148
148
  end
149
149
  end
150
150
  end
151
- end
151
+ end
@@ -2,11 +2,12 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{searchlogic}
5
- s.version = "2.1.7.1"
5
+ s.version = "2.2.3.1"
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-13}
9
+ s.date = %q{2009-07-31}
10
+ s.description = %q{Searchlogic provides common named scopes and object based searching for ActiveRecord.}
10
11
  s.email = %q{bjohnson@binarylogic.com}
11
12
  s.extra_rdoc_files = [
12
13
  "LICENSE",
@@ -21,7 +22,8 @@ Gem::Specification.new do |s|
21
22
  "VERSION.yml",
22
23
  "init.rb",
23
24
  "lib/searchlogic.rb",
24
- "lib/searchlogic/active_record_consistency.rb",
25
+ "lib/searchlogic/active_record/consistency.rb",
26
+ "lib/searchlogic/active_record/named_scopes.rb",
25
27
  "lib/searchlogic/core_ext/object.rb",
26
28
  "lib/searchlogic/core_ext/proc.rb",
27
29
  "lib/searchlogic/named_scopes/alias_scope.rb",
@@ -47,7 +49,7 @@ Gem::Specification.new do |s|
47
49
  s.rdoc_options = ["--charset=UTF-8"]
48
50
  s.require_paths = ["lib"]
49
51
  s.rubyforge_project = %q{searchlogic}
50
- s.rubygems_version = %q{1.3.4}
52
+ s.rubygems_version = %q{1.3.5}
51
53
  s.summary = %q{Searchlogic provides common named scopes and object based searching for ActiveRecord.}
52
54
  s.test_files = [
53
55
  "spec/core_ext/object_spec.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")
@@ -9,11 +9,35 @@ describe "Association Conditions" do
9
9
  Company.users_orders_total_greater_than(10).proxy_options.should == Order.total_greater_than(10).proxy_options.merge(:joins => {:users => :orders})
10
10
  end
11
11
 
12
- it "should not allowed named scopes on non existent association columns" do
12
+ it "should allow the use of foreign pre-existing named scopes" do
13
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
14
+ Company.users_uname("bjohnson").proxy_options.should == User.uname("bjohnson").proxy_options.merge(:joins => :users)
15
+ end
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
+
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) }
24
+ Company.users_username_has("bjohnson").proxy_options.should == User.username_has("bjohnson").proxy_options.merge(:joins => :users)
25
+ end
26
+
27
+ it "should not raise errors for scopes that don't return anything" do
28
+ User.alias_scope :blank_scope, lambda { |value| }
29
+ Company.users_blank_scope("bjohnson").proxy_options.should == {:joins => :users}
30
+ end
31
+
32
+ it "should ignore polymorphic associations" do
33
+ lambda { Fee.owner_created_at_gt(Time.now) }.should raise_error(NoMethodError)
34
+ end
35
+
36
+ it "should not allow named scopes on non existent association columns" do
13
37
  lambda { User.users_whatever_like("bjohnson") }.should raise_error(NoMethodError)
14
38
  end
15
39
 
16
- it "should not allowed named scopes on non existent deep association columns" do
40
+ it "should not allow named scopes on non existent deep association columns" do
17
41
  lambda { User.users_orders_whatever_like("bjohnson") }.should raise_error(NoMethodError)
18
42
  end
19
43
 
@@ -101,4 +125,9 @@ describe "Association Conditions" do
101
125
  order = user.orders.create(:total => 20, :taxes => 3)
102
126
  Company.users_orders_taxes_lt(50).ascend_by_users_orders_total.all(:include => {:users => :orders}).should == Company.all
103
127
  end
128
+
129
+ it "should automatically add string joins if the association condition is using strings" do
130
+ User.named_scope(:orders_big_id, :joins => User.inner_joins(:orders))
131
+ Company.users_orders_big_id.proxy_options.should == {:joins=>[" INNER JOIN \"users\" ON users.company_id = companies.id ", " INNER JOIN \"orders\" ON orders.user_id = users.id "]}
132
+ end
104
133
  end
@@ -49,15 +49,30 @@ describe "Conditions" do
49
49
  User.username_like("john").all.should == User.find_all_by_username("bjohnson")
50
50
  end
51
51
 
52
+ it "should have not like" do
53
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
54
+ User.username_not_like("john").all.should == User.find_all_by_username("thunt")
55
+ end
56
+
52
57
  it "should have begins with" do
53
58
  %w(bjohnson thunt).each { |username| User.create(:username => username) }
54
59
  User.username_begins_with("bj").all.should == User.find_all_by_username("bjohnson")
55
60
  end
56
61
 
62
+ it "should have not begin with" do
63
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
64
+ User.username_not_begin_with("bj").all.should == User.find_all_by_username("thunt")
65
+ end
66
+
57
67
  it "should have ends with" do
58
68
  %w(bjohnson thunt).each { |username| User.create(:username => username) }
59
69
  User.username_ends_with("son").all.should == User.find_all_by_username("bjohnson")
60
70
  end
71
+
72
+ it "should have not end with" do
73
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
74
+ User.username_not_end_with("son").all.should == User.find_all_by_username("thunt")
75
+ end
61
76
  end
62
77
 
63
78
  context "boolean conditions" do
@@ -66,6 +81,11 @@ describe "Conditions" do
66
81
  User.username_null.all.should == User.find_all_by_username(nil)
67
82
  end
68
83
 
84
+ it "should have not null" do
85
+ ["bjohnson", nil].each { |username| User.create(:username => username) }
86
+ User.username_not_null.all.should == User.find_all_by_username("bjohnson")
87
+ end
88
+
69
89
  it "should have empty" do
70
90
  ["bjohnson", ""].each { |username| User.create(:username => username) }
71
91
  User.username_empty.all.should == User.find_all_by_username("")
@@ -183,9 +203,6 @@ describe "Conditions" do
183
203
  end
184
204
 
185
205
  it "should have is_not" do
186
- # This is matching "not" first. How do you give priority in a regex? Because it's matching the
187
- # 'not' condition and thinking the column is 'age_is'.
188
- pending
189
206
  User.age_is_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
190
207
  end
191
208
 
@@ -40,6 +40,16 @@ describe "Search" do
40
40
  search1.all.should == [user2]
41
41
  end
42
42
 
43
+ it "should clone properly without scope" do
44
+ user1 = User.create(:age => 5)
45
+ user2 = User.create(:age => 25)
46
+ search1 = User.search(:age_gt => 10)
47
+ search2 = search1.clone
48
+ search2.age_gt = 1
49
+ search2.all.should == User.all
50
+ search1.all.should == [user2]
51
+ end
52
+
43
53
  it "should delete the condition" do
44
54
  search = User.search(:username_like => "bjohnson")
45
55
  search.delete("username_like")
@@ -99,6 +109,20 @@ describe "Search" do
99
109
  search.orders_total_gt.should == 10
100
110
  end
101
111
 
112
+ it "should allow setting pre-existing association conditions" do
113
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
114
+ search = Company.search
115
+ search.users_uname = "bjohnson"
116
+ search.users_uname.should == "bjohnson"
117
+ end
118
+
119
+ it "should allow setting pre-existing association alias conditions" do
120
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
121
+ search = Company.search
122
+ search.users_username_has = "bjohnson"
123
+ search.users_username_has.should == "bjohnson"
124
+ end
125
+
102
126
  it "should allow using custom conditions" do
103
127
  User.named_scope(:four_year_olds, { :conditions => { :age => 4 } })
104
128
  search = User.search
@@ -136,6 +160,11 @@ describe "Search" do
136
160
  lambda { search.unknown = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
137
161
  end
138
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
+
139
168
  it "should not use the ruby implementation of the id method" do
140
169
  search = User.search
141
170
  search.id.should be_nil
@@ -292,4 +321,4 @@ describe "Search" do
292
321
  User.search(:order => "ascend_by_username").proxy_options.should == User.ascend_by_username.proxy_options
293
322
  end
294
323
  end
295
- end
324
+ end
@@ -26,6 +26,12 @@ ActiveRecord::Schema.define(:version => 1) do
26
26
  t.integer :age
27
27
  end
28
28
 
29
+ create_table :carts do |t|
30
+ t.datetime :created_at
31
+ t.datetime :updated_at
32
+ t.integer :user_id
33
+ end
34
+
29
35
  create_table :orders do |t|
30
36
  t.datetime :created_at
31
37
  t.datetime :updated_at
@@ -35,6 +41,14 @@ ActiveRecord::Schema.define(:version => 1) do
35
41
  t.float :total
36
42
  end
37
43
 
44
+ create_table :fees do |t|
45
+ t.datetime :created_at
46
+ t.datetime :updated_at
47
+ t.string :owner_type
48
+ t.integer :owner_id
49
+ t.float :cost
50
+ end
51
+
38
52
  create_table :line_items do |t|
39
53
  t.datetime :created_at
40
54
  t.datetime :updated_at
@@ -56,7 +70,6 @@ Spec::Runner.configure do |config|
56
70
  class User < ActiveRecord::Base
57
71
  belongs_to :company, :counter_cache => true
58
72
  has_many :orders, :dependent => :destroy
59
- alias_scope :username_has, lambda { |value| username_like(value) }
60
73
  end
61
74
 
62
75
  class Order < ActiveRecord::Base
@@ -64,6 +77,10 @@ Spec::Runner.configure do |config|
64
77
  has_many :line_items, :dependent => :destroy
65
78
  end
66
79
 
80
+ class Fee < ActiveRecord::Base
81
+ belongs_to :owner, :polymorphic => true
82
+ end
83
+
67
84
  class LineItem < ActiveRecord::Base
68
85
  belongs_to :order
69
86
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: joost-searchlogic
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.7.1
4
+ version: 2.2.3.1
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-13 00:00:00 -07:00
12
+ date: 2009-07-31 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,7 +22,7 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 2.0.0
24
24
  version:
25
- description:
25
+ description: Searchlogic provides common named scopes and object based searching for ActiveRecord.
26
26
  email: bjohnson@binarylogic.com
27
27
  executables: []
28
28
 
@@ -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
@@ -63,6 +64,7 @@ files:
63
64
  - spec/spec_helper.rb
64
65
  has_rdoc: false
65
66
  homepage: http://github.com/binarylogic/searchlogic
67
+ licenses:
66
68
  post_install_message:
67
69
  rdoc_options:
68
70
  - --charset=UTF-8
@@ -83,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
85
  requirements: []
84
86
 
85
87
  rubyforge_project: searchlogic
86
- rubygems_version: 1.2.0
88
+ rubygems_version: 1.3.5
87
89
  signing_key:
88
90
  specification_version: 3
89
91
  summary: Searchlogic provides common named scopes and object based searching for ActiveRecord.
@@ -1,27 +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 time they
12
- # are not. The merge_joins method in AR should account for this, but it doesn't.
13
- # This fixes that problem.
14
- def merge_joins_with_searchlogic(*args)
15
- joins = merge_joins_without_searchlogic(*args)
16
- joins.collect { |j| j.is_a?(String) ? j.split(" ") : j }.flatten.uniq
17
- end
18
- end
19
- end
20
-
21
- module ActiveRecord # :nodoc: all
22
- class Base
23
- class << self
24
- include Searchlogic::ActiveRecordConsistency
25
- end
26
- end
27
- end