rd_searchlogic 3.0.0.rc

Sign up to get free protection for your applications and to get access to all the features.
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