rd_searchlogic 3.0.0.rc

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.
Files changed (34) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +308 -0
  4. data/Rakefile +42 -0
  5. data/VERSION.yml +5 -0
  6. data/init.rb +1 -0
  7. data/lib/searchlogic/active_record/association_proxy.rb +19 -0
  8. data/lib/searchlogic/active_record/consistency.rb +49 -0
  9. data/lib/searchlogic/active_record/named_scope_tools.rb +102 -0
  10. data/lib/searchlogic/core_ext/object.rb +43 -0
  11. data/lib/searchlogic/core_ext/proc.rb +17 -0
  12. data/lib/searchlogic/named_scopes/alias_scope.rb +67 -0
  13. data/lib/searchlogic/named_scopes/association_conditions.rb +163 -0
  14. data/lib/searchlogic/named_scopes/association_ordering.rb +44 -0
  15. data/lib/searchlogic/named_scopes/conditions.rb +232 -0
  16. data/lib/searchlogic/named_scopes/or_conditions.rb +141 -0
  17. data/lib/searchlogic/named_scopes/ordering.rb +74 -0
  18. data/lib/searchlogic/rails_helpers.rb +79 -0
  19. data/lib/searchlogic/search.rb +259 -0
  20. data/lib/searchlogic.rb +89 -0
  21. data/rails/init.rb +1 -0
  22. data/spec/searchlogic/active_record/association_proxy_spec.rb +23 -0
  23. data/spec/searchlogic/active_record/consistency_spec.rb +28 -0
  24. data/spec/searchlogic/core_ext/object_spec.rb +9 -0
  25. data/spec/searchlogic/core_ext/proc_spec.rb +8 -0
  26. data/spec/searchlogic/named_scopes/alias_scope_spec.rb +23 -0
  27. data/spec/searchlogic/named_scopes/association_conditions_spec.rb +221 -0
  28. data/spec/searchlogic/named_scopes/association_ordering_spec.rb +29 -0
  29. data/spec/searchlogic/named_scopes/conditions_spec.rb +321 -0
  30. data/spec/searchlogic/named_scopes/or_conditions_spec.rb +66 -0
  31. data/spec/searchlogic/named_scopes/ordering_spec.rb +34 -0
  32. data/spec/searchlogic/search_spec.rb +459 -0
  33. data/spec/spec_helper.rb +146 -0
  34. metadata +123 -0
@@ -0,0 +1,74 @@
1
+ module Searchlogic
2
+ module NamedScopes
3
+ # Handles dynamically creating named scopes for ordering by columns. Example:
4
+ #
5
+ # User.ascend_by_id
6
+ # User.descend_by_username
7
+ #
8
+ # See the README for a more detailed explanation.
9
+ module Ordering
10
+ def condition?(name) # :nodoc:
11
+ super || ordering_condition?(name)
12
+ end
13
+
14
+ def self.included(base)
15
+ #this overrides the 'new' method in any class including Ordering -- then when called, new overrides the 'order' method
16
+ #overriding is not a simple matter in Ruby because includes put the modules below the class in the superclass sequence
17
+ #this keeps the code cleaner by avoiding alias_method_chain (monkey patching)
18
+ (class << base; self; end).send(:include, ClassOverrideMethods)
19
+ end
20
+
21
+ module ClassOverrideMethods
22
+ def new(*args)
23
+ _inst = super
24
+ #this overrides the 'order' method
25
+ (class << _inst; self; end).send(:include, Searchlogic::NamedScopes::Ordering::InstanceOverrideMethods)
26
+ _inst
27
+ end
28
+ end
29
+ module InstanceOverrideMethods
30
+ def order(*args)
31
+ if condition?(args[0])
32
+ send(args[0])
33
+ else
34
+ super
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+ def ordering_condition?(name) # :nodoc:
41
+ !ordering_condition_details(name).nil?
42
+ end
43
+
44
+ def method_missing(name, *args, &block)
45
+ if name == :order
46
+ scope name, lambda { |scope_name|
47
+ return {} if !condition?(scope_name)
48
+ send(scope_name).proxy_options
49
+ }
50
+ send(name, *args)
51
+ end
52
+ if details = ordering_condition_details(name)
53
+ create_ordering_conditions(details[:column])
54
+ send(name, *args)
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ def ordering_condition_details(name)
61
+ if name.to_s =~ /^(ascend|descend)_by_(#{column_names.join("|")})$/
62
+ {:order_as => $1, :column => $2}
63
+ elsif name.to_s =~ /^order$/
64
+ {}
65
+ end
66
+ end
67
+
68
+ def create_ordering_conditions(column)
69
+ scope("ascend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} ASC"})
70
+ scope("descend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} DESC"})
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,79 @@
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</tt> - hash with additional params which will be added to generated url
21
+ # * <tt>:params_scope</tt> - the name of the params key to scope the order condition by, defaults to :search
22
+ def order(search, options = {}, html_options = {})
23
+ options[:params_scope] ||= :search
24
+ if !options[:as]
25
+ id = options[:by].to_s.downcase == "id"
26
+ options[:as] = id ? options[:by].to_s.upcase : options[:by].to_s.humanize
27
+ end
28
+ options[:ascend_scope] ||= "ascend_by_#{options[:by]}"
29
+ options[:descend_scope] ||= "descend_by_#{options[:by]}"
30
+ ascending = search.order.to_s == options[:ascend_scope]
31
+ new_scope = ascending ? options[:descend_scope] : options[:ascend_scope]
32
+ selected = [options[:ascend_scope], options[:descend_scope]].include?(search.order.to_s)
33
+ if selected
34
+ css_classes = html_options[:class] ? html_options[:class].split(" ") : []
35
+ if ascending
36
+ options[:as] = raw("&#9650;&nbsp;#{options[:as]}")
37
+ css_classes << "ascending"
38
+ else
39
+ options[:as] = raw("&#9660;&nbsp;#{options[:as]}")
40
+ css_classes << "descending"
41
+ end
42
+ html_options[:class] = css_classes.join(" ")
43
+ end
44
+ url_options = {
45
+ options[:params_scope] => search.conditions.merge( { :order => new_scope } )
46
+ }.deep_merge(options[:params] || {})
47
+
48
+ options[:as] = raw(options[:as]) if defined?(RailsXss)
49
+
50
+ link_to options[:as], url_for(url_options), html_options
51
+ end
52
+
53
+ # Automatically makes the form method :get if a Searchlogic::Search and sets
54
+ # the params scope to :search
55
+ def form_for(*args, &block)
56
+ if search_obj = args.find { |arg| arg.is_a?(Searchlogic::Search) }
57
+ options = args.extract_options!
58
+ options[:html] ||= {}
59
+ options[:html][:method] ||= :get
60
+ options[:url] ||= url_for
61
+ args.unshift(:search) if args.first == search_obj
62
+ args << options
63
+ end
64
+ super
65
+ end
66
+
67
+ # Automatically adds an "order" hidden field in your form to preserve how the data
68
+ # is being ordered.
69
+ def fields_for(*args, &block)
70
+ if search_obj = args.find { |arg| arg.is_a?(Searchlogic::Search) }
71
+ args.unshift(:search) if args.first == search_obj
72
+ concat(content_tag("div", hidden_field_tag("#{args.first}[order]", search_obj.order)))
73
+ super
74
+ else
75
+ super
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,259 @@
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 aliased 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, self, conditions) #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
+ def do_search
42
+ @current_scope
43
+ end
44
+
45
+ attr_accessor :klass, :current_scope, :conditions
46
+ undef :id if respond_to?(:id)
47
+
48
+ # Creates a new search object for the given class. Ex:
49
+ #
50
+ # Searchlogic::Search.new(User, {}, {:username_like => "bjohnson"})
51
+ def initialize(klass, current_scope, conditions = {})
52
+ self.klass = klass
53
+ self.current_scope = current_scope
54
+ @conditions ||= {}
55
+ self.conditions = conditions if conditions.is_a?(Hash)
56
+ end
57
+
58
+ def clone
59
+ self.class.new(klass, current_scope && current_scope.clone, conditions.clone)
60
+ end
61
+
62
+ # Returns a hash of the current conditions set.
63
+ def conditions
64
+ mass_conditions.clone.merge(@conditions)
65
+ end
66
+
67
+ # Accepts a hash of conditions.
68
+ def conditions=(values)
69
+ values.each do |condition, value|
70
+ # if a condition name ends with "(1i)", assume it's date / datetime
71
+ if condition =~ /(.*)\(1i\)$/
72
+ date_scope_name = $1
73
+ date_parts = (1..6).to_a.map do |idx|
74
+ values.delete("#{ date_scope_name }(#{ idx }i)")
75
+ end.reject{|s| s.blank? }.map{|s| s.to_i }
76
+
77
+ # did we get enough info to build a time?
78
+ if date_parts.length >= 3
79
+ values[date_scope_name] = Time.zone.local(*date_parts)
80
+ end
81
+ end
82
+ end
83
+
84
+ values.each do |condition, value|
85
+ mass_conditions[condition.to_sym] = value
86
+ value.delete_if { |v| ignore_value?(v) } if value.is_a?(Array)
87
+ next if ignore_value?(value)
88
+ # value = Time.zone.parse(value) if value =~ /[\d\/.-]{10}/
89
+ @current_scope = @current_scope.send(condition, value)
90
+ send("#{condition}=", value)
91
+ @conditions[condition.to_sym] = value
92
+ end
93
+ end
94
+
95
+ # Delete a condition from the search. Since conditions map to named scopes,
96
+ # if a named scope accepts a parameter there is no way to actually delete
97
+ # the scope if you do not want it anymore. A nil value might be meaningful
98
+ # to that scope.
99
+ def delete(*names)
100
+ names.each do |name|
101
+ @conditions.delete(name.to_sym)
102
+ mass_conditions.delete(name)
103
+ end
104
+ self
105
+ end
106
+
107
+ # Returns the column we are currently ordering by
108
+ def ordering_by
109
+ order && order.to_s.gsub(/^(ascend|descend)_by_/, '')
110
+ end
111
+
112
+ private
113
+ def method_missing(name, *args, &block)
114
+ condition_name = condition_name(name)
115
+ scope_name = scope_name(condition_name)
116
+
117
+
118
+ if setter?(name)
119
+ if scope?(scope_name)
120
+ if args.size == 1
121
+ write_condition(
122
+ condition_name,
123
+ type_cast(
124
+ args.first,
125
+ cast_type(scope_name),
126
+ scope_options(scope_name).respond_to?(:searchlogic_options) ? scope_options(scope_name).searchlogic_options : {}
127
+ )
128
+ )
129
+ else
130
+ write_condition(condition_name, args)
131
+ end
132
+ else
133
+ raise UnknownConditionError.new(condition_name)
134
+ end
135
+ elsif scope?(scope_name) && args.size <= 1
136
+ if args.size == 0
137
+ read_condition(condition_name)
138
+ else
139
+ send("#{condition_name}=", *args)
140
+ self
141
+ end
142
+ else
143
+ scope = conditions_array.inject(klass.scoped(current_scope) || {}) do |scope, condition|
144
+ scope_name, value = condition
145
+ scope_name = normalize_scope_name(scope_name)
146
+ klass.send(scope_name, value) if !klass.respond_to?(scope_name)
147
+ arity = klass.named_scope_arity(scope_name)
148
+
149
+ if !arity || arity == 0
150
+ if value == true
151
+ scope.send(scope_name)
152
+ else
153
+ scope
154
+ end
155
+ elsif arity == -1
156
+ scope.send(scope_name, *(value.is_a?(Array) ? value : [value]))
157
+ else
158
+ scope.send(scope_name, value)
159
+ end
160
+ end
161
+ scope.send(name, *args, &block)
162
+ end
163
+ end
164
+
165
+ # This is here as a hook to allow people to modify the order in which the conditions are called, for whatever reason.
166
+ def conditions_array
167
+ @conditions.to_a
168
+ end
169
+
170
+ def normalize_scope_name(scope_name)
171
+ case
172
+ when klass.scopes.key?(scope_name.to_sym) then scope_name.to_sym
173
+ when klass.column_names.include?(scope_name.to_s) then "#{scope_name}_equals".to_sym
174
+ else scope_name.to_sym
175
+ end
176
+ end
177
+
178
+ def setter?(name)
179
+ !(name.to_s =~ /=$/).nil?
180
+ end
181
+
182
+ def condition_name(name)
183
+ condition = name.to_s.match(/(\w+)=?$/)
184
+ condition ? condition[1].to_sym : nil
185
+ end
186
+
187
+ def write_condition(name, value)
188
+ @conditions[name] = value
189
+ end
190
+
191
+ def read_condition(name)
192
+ @conditions[name]
193
+ end
194
+
195
+ def mass_conditions
196
+ @mass_conditions ||= {}
197
+ end
198
+
199
+ def scope_name(condition_name)
200
+ condition_name && normalize_scope_name(condition_name)
201
+ end
202
+
203
+ def scope?(scope_name)
204
+ klass.scopes.key?(scope_name) || klass.condition?(scope_name)
205
+ end
206
+
207
+ def scope_options(name)
208
+ 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_scope_options
209
+ klass.named_scope_options(name)
210
+ end
211
+
212
+ def cast_type(name)
213
+ named_scope_options = scope_options(name)
214
+ arity = klass.named_scope_arity(name)
215
+ if !arity || arity == 0
216
+ :boolean
217
+ else
218
+ named_scope_options.respond_to?(:searchlogic_options) ? named_scope_options.searchlogic_options[:type] : :string
219
+ end
220
+ end
221
+
222
+ def type_cast(value, type, options = {})
223
+ case value
224
+ when Array
225
+ value.collect { |v| type_cast(v, type) }
226
+ when Range
227
+ Range.new(type_cast(value.first, type), type_cast(value.last, type))
228
+ else
229
+ # Let's leverage ActiveRecord's type casting, so that casting is consistent
230
+ # with the other models.
231
+ column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
232
+ column_for_type_cast.instance_variable_set(:@type, type)
233
+ casted_value = column_for_type_cast.type_cast(value)
234
+
235
+ if Time.zone && casted_value.is_a?(Time)
236
+ if value.is_a?(String)
237
+ if options[:skip_conversion]
238
+ casted_value.utc
239
+ else
240
+ (casted_value + (Time.zone.utc_offset * -1)).in_time_zone
241
+ end
242
+ else
243
+ if options[:skip_conversion]
244
+ casted_value.utc
245
+ else
246
+ casted_value.in_time_zone
247
+ end
248
+ end
249
+ else
250
+ casted_value
251
+ end
252
+ end
253
+ end
254
+
255
+ def ignore_value?(value)
256
+ (value.is_a?(String) && value.blank?) || (value.is_a?(Array) && value.empty?)
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,89 @@
1
+ require "searchlogic/core_ext/proc"
2
+ require "searchlogic/core_ext/object"
3
+ require "searchlogic/active_record/association_proxy"
4
+ require "searchlogic/active_record/consistency"
5
+ require "searchlogic/active_record/named_scope_tools"
6
+ require "searchlogic/named_scopes/conditions"
7
+ require "searchlogic/named_scopes/ordering"
8
+ require "searchlogic/named_scopes/association_conditions"
9
+ require "searchlogic/named_scopes/association_ordering"
10
+ require "searchlogic/named_scopes/alias_scope"
11
+ require "searchlogic/named_scopes/or_conditions"
12
+ require "searchlogic/search"
13
+
14
+ ActiveRecord::Relation.send(:include, Searchlogic::CoreExt::Proc)
15
+ Proc.send(:include, Searchlogic::CoreExt::Proc)
16
+ Object.send(:include, Searchlogic::CoreExt::Object)
17
+
18
+ module ActiveRecord # :nodoc: all
19
+ module Associations
20
+ class AssociationProxy
21
+ include Searchlogic::ActiveRecord::AssociationProxy
22
+ end
23
+ end
24
+
25
+ # FIX: removed this to get tests running
26
+ # class Base
27
+ # class << self; include Searchlogic::ActiveRecord::Consistency; end
28
+ # end
29
+ end
30
+
31
+ m = Searchlogic::NamedScopes
32
+ [
33
+ Searchlogic::ActiveRecord::NamedScopeTools,
34
+ m::Conditions,
35
+ m::AssociationConditions,
36
+ m::AssociationOrdering,
37
+ m::Ordering,
38
+ # m::AliasScope
39
+ # m::OrConditions
40
+ ].each do |_module|
41
+ ActiveRecord::Base.extend(_module)
42
+ ActiveRecord::Relation.send(:include, _module)
43
+ end
44
+ #ActiveRecord::Base.extend(Searchlogic::ActiveRecord::NamedScopeTools)
45
+ #ActiveRecord::Base.extend(Searchlogic::NamedScopes::Conditions)
46
+
47
+ #ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationConditions)
48
+ #ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationOrdering)
49
+ #ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
50
+ #ActiveRecord::Base.extend(Searchlogic::NamedScopes::AliasScope)
51
+ #ActiveRecord::Base.extend(Searchlogic::NamedScopes::OrConditions)
52
+ ActiveRecord::Base.extend(Searchlogic::Search::Implementation)
53
+
54
+
55
+ # Try to use the search method, if it's available. Thinking sphinx and other plugins
56
+ # like to use that method as well.
57
+ if !ActiveRecord::Base.respond_to?(:search)
58
+ ActiveRecord::Base.class_eval { class << self; alias_method :search, :searchlogic; end }
59
+ end
60
+
61
+ if defined?(ActionController)
62
+ require "searchlogic/rails_helpers"
63
+ ActionController::Base.helper(Searchlogic::RailsHelpers)
64
+ end
65
+
66
+ #FIX: for bug in active_record 3.0.0.beta3 -- left over from Rails 2.3.x, and wasn't removed from attribute_condition method in ActiveRecord::Base
67
+ module ActiveRecord
68
+ module NamedScope
69
+ class Scope; end
70
+ end
71
+ end
72
+
73
+ #FIX: proxy_options is used extensively throughout SL testing to determine that no conditions, ordering, etc. were set
74
+ #module ActiveRecord
75
+ # class Relation
76
+ # def proxy_options
77
+ # [:where_clauses, :order_clauses].inject({}) do |hash, method|
78
+ # clauses = send(method)
79
+ # unless clauses.empty?
80
+ # case method
81
+ # when :where_clauses then hash[:conditions] = clauses
82
+ # when :order_clauses then hash[:order] = clauses
83
+ # end
84
+ # end
85
+ # hash
86
+ # end
87
+ # end
88
+ # end
89
+ #end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "searchlogic"
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe "Searchlogic::ActiveRecord::AssociationProxy" do
4
+ it "should call location conditions" do
5
+ company = Company.create
6
+ user = company.users.create(:username => "bjohnson")
7
+ company.users.send(:username_like, "bjohnson").should == [user]
8
+ end
9
+
10
+ it "should call ordering conditions" do
11
+ company = Company.create
12
+ user = company.users.create(:username => "bjohnson")
13
+ company.users.send(:ascend_by_username).should == [user]
14
+ end
15
+
16
+ it "should call 'or' conditions" do
17
+ company = Company.create
18
+ user = company.users.create(:username => "bjohnson")
19
+ company.users.send(:username_or_some_type_id_like, "bjohnson").should == [user]
20
+ end
21
+
22
+ end
23
+
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::ActiveRecord::Consistency do
4
+ it "should merge joins with consistent conditions" do
5
+ user_group = UserGroup.create
6
+ user_group.users.user_groups_name_like("name").user_groups_id_gt(10).scope(:find)[:joins].should == [
7
+ "INNER JOIN \"user_groups_users\" ON \"user_groups_users\".user_id = \"users\".id",
8
+ "INNER JOIN \"user_groups\" ON \"user_groups\".id = \"user_groups_users\".user_group_id"
9
+ ]
10
+ end
11
+
12
+ it "should respect parenthesis when reordering conditions" do
13
+ joins = [
14
+ "INNER JOIN \"table\" ON (\"b\".user_id = \"a\".id)",
15
+ "INNER JOIN \"table\" ON (\"b\".id = \"a\".user_group_id)"
16
+ ]
17
+ ActiveRecord::Base.send(:merge_joins, joins).should == [
18
+ "INNER JOIN \"table\" ON \"a\".id = \"b\".user_id",
19
+ "INNER JOIN \"table\" ON \"a\".user_group_id = \"b\".id"
20
+ ]
21
+ end
22
+
23
+ it "shuold not convert joins to strings when delegating via associations" do
24
+ User.alias_scope :has_id_gt, lambda { User.id_gt(10).has_name.orders_id_gt(10) }
25
+ User.alias_scope :has_name, lambda { User.orders_created_at_after(Time.now).name_equals("ben").username_equals("ben") }
26
+ Company.users_has_id_gt.proxy_options[:joins].should == {:users=>[:orders]}
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::CoreExt::Object do
4
+ it "should accept and pass the argument to the searchlogic_options" do
5
+ proc = searchlogic_lambda(:integer, :test => :value) {}
6
+ proc.searchlogic_options[:type].should == :integer
7
+ proc.searchlogic_options[:test].should == :value
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::CoreExt::Proc do
4
+ it "should have a searchlogic_options accessor" do
5
+ p = Proc.new {}
6
+ p.searchlogic_options[:type] = :integer
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::NamedScopes::AliasScope do
4
+ before(:each) do
5
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
6
+ end
7
+
8
+ it "should allow alias scopes" do
9
+ User.create(:username => "bjohnson")
10
+ User.create(:username => "thunt")
11
+ User.username_has("bjohnson").all.should == User.find_all_by_username("bjohnson")
12
+ end
13
+
14
+ it "should allow alias scopes from the search object" do
15
+ search = User.search
16
+ search.username_has = "bjohnson"
17
+ search.username_has.should == "bjohnson"
18
+ end
19
+
20
+ it "should inherit alias scopes from superclasses" do
21
+ Class.new(User).alias_scope?("username_has").should be_true
22
+ end
23
+ end