friendlyfashion-thinking-sphinx 2.0.13

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 (175) hide show
  1. data/HISTORY +244 -0
  2. data/LICENCE +20 -0
  3. data/README.textile +235 -0
  4. data/features/abstract_inheritance.feature +10 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +77 -0
  8. data/features/deleting_instances.feature +67 -0
  9. data/features/direct_attributes.feature +11 -0
  10. data/features/excerpts.feature +21 -0
  11. data/features/extensible_delta_indexing.feature +9 -0
  12. data/features/facets.feature +88 -0
  13. data/features/facets_across_model.feature +29 -0
  14. data/features/field_sorting.feature +18 -0
  15. data/features/handling_edits.feature +94 -0
  16. data/features/retry_stale_indexes.feature +24 -0
  17. data/features/searching_across_models.feature +20 -0
  18. data/features/searching_by_index.feature +40 -0
  19. data/features/searching_by_model.feature +175 -0
  20. data/features/searching_with_find_arguments.feature +56 -0
  21. data/features/sphinx_detection.feature +25 -0
  22. data/features/sphinx_scopes.feature +68 -0
  23. data/features/step_definitions/alpha_steps.rb +16 -0
  24. data/features/step_definitions/beta_steps.rb +7 -0
  25. data/features/step_definitions/common_steps.rb +201 -0
  26. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  27. data/features/step_definitions/facet_steps.rb +96 -0
  28. data/features/step_definitions/find_arguments_steps.rb +36 -0
  29. data/features/step_definitions/gamma_steps.rb +15 -0
  30. data/features/step_definitions/scope_steps.rb +19 -0
  31. data/features/step_definitions/search_steps.rb +94 -0
  32. data/features/step_definitions/sphinx_steps.rb +35 -0
  33. data/features/sti_searching.feature +19 -0
  34. data/features/support/env.rb +27 -0
  35. data/features/support/lib/generic_delta_handler.rb +8 -0
  36. data/features/thinking_sphinx/database.example.yml +3 -0
  37. data/features/thinking_sphinx/db/.gitignore +1 -0
  38. data/features/thinking_sphinx/db/fixtures/alphas.rb +8 -0
  39. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  40. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  41. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  42. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  43. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  44. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  45. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  46. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  47. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  48. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  49. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  50. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  51. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  52. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
  53. data/features/thinking_sphinx/db/fixtures/posts.rb +10 -0
  54. data/features/thinking_sphinx/db/fixtures/robots.rb +8 -0
  55. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  56. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  57. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  58. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  59. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  60. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  61. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  62. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  63. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  64. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  65. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  66. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  67. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  68. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  69. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  70. data/features/thinking_sphinx/db/migrations/create_posts.rb +6 -0
  71. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  72. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  73. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  74. data/features/thinking_sphinx/models/alpha.rb +23 -0
  75. data/features/thinking_sphinx/models/andrew.rb +17 -0
  76. data/features/thinking_sphinx/models/animal.rb +5 -0
  77. data/features/thinking_sphinx/models/author.rb +3 -0
  78. data/features/thinking_sphinx/models/beta.rb +13 -0
  79. data/features/thinking_sphinx/models/box.rb +8 -0
  80. data/features/thinking_sphinx/models/cat.rb +3 -0
  81. data/features/thinking_sphinx/models/category.rb +4 -0
  82. data/features/thinking_sphinx/models/comment.rb +10 -0
  83. data/features/thinking_sphinx/models/developer.rb +21 -0
  84. data/features/thinking_sphinx/models/dog.rb +3 -0
  85. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  86. data/features/thinking_sphinx/models/fox.rb +5 -0
  87. data/features/thinking_sphinx/models/gamma.rb +5 -0
  88. data/features/thinking_sphinx/models/genre.rb +3 -0
  89. data/features/thinking_sphinx/models/medium.rb +5 -0
  90. data/features/thinking_sphinx/models/music.rb +10 -0
  91. data/features/thinking_sphinx/models/person.rb +24 -0
  92. data/features/thinking_sphinx/models/post.rb +22 -0
  93. data/features/thinking_sphinx/models/robot.rb +12 -0
  94. data/features/thinking_sphinx/models/tag.rb +3 -0
  95. data/features/thinking_sphinx/models/tagging.rb +4 -0
  96. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  97. data/lib/cucumber/thinking_sphinx/internal_world.rb +137 -0
  98. data/lib/cucumber/thinking_sphinx/sql_logger.rb +28 -0
  99. data/lib/thinking-sphinx.rb +1 -0
  100. data/lib/thinking_sphinx/action_controller.rb +31 -0
  101. data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
  102. data/lib/thinking_sphinx/active_record/collection_proxy.rb +47 -0
  103. data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
  104. data/lib/thinking_sphinx/active_record/delta.rb +67 -0
  105. data/lib/thinking_sphinx/active_record/has_many_association.rb +44 -0
  106. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  107. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  108. data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
  109. data/lib/thinking_sphinx/active_record.rb +386 -0
  110. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  111. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
  112. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +188 -0
  113. data/lib/thinking_sphinx/association.rb +230 -0
  114. data/lib/thinking_sphinx/attribute.rb +405 -0
  115. data/lib/thinking_sphinx/auto_version.rb +40 -0
  116. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  117. data/lib/thinking_sphinx/class_facet.rb +20 -0
  118. data/lib/thinking_sphinx/configuration.rb +375 -0
  119. data/lib/thinking_sphinx/context.rb +76 -0
  120. data/lib/thinking_sphinx/core/string.rb +15 -0
  121. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  122. data/lib/thinking_sphinx/deltas.rb +28 -0
  123. data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
  124. data/lib/thinking_sphinx/excerpter.rb +23 -0
  125. data/lib/thinking_sphinx/facet.rb +135 -0
  126. data/lib/thinking_sphinx/facet_search.rb +170 -0
  127. data/lib/thinking_sphinx/field.rb +98 -0
  128. data/lib/thinking_sphinx/index/builder.rb +315 -0
  129. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  130. data/lib/thinking_sphinx/index.rb +159 -0
  131. data/lib/thinking_sphinx/join.rb +37 -0
  132. data/lib/thinking_sphinx/property.rb +187 -0
  133. data/lib/thinking_sphinx/railtie.rb +43 -0
  134. data/lib/thinking_sphinx/search.rb +1061 -0
  135. data/lib/thinking_sphinx/search_methods.rb +439 -0
  136. data/lib/thinking_sphinx/sinatra.rb +7 -0
  137. data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
  138. data/lib/thinking_sphinx/source/sql.rb +174 -0
  139. data/lib/thinking_sphinx/source.rb +194 -0
  140. data/lib/thinking_sphinx/tasks.rb +142 -0
  141. data/lib/thinking_sphinx/test.rb +55 -0
  142. data/lib/thinking_sphinx/version.rb +3 -0
  143. data/lib/thinking_sphinx.rb +297 -0
  144. data/spec/fixtures/data.sql +32 -0
  145. data/spec/fixtures/database.yml.default +3 -0
  146. data/spec/fixtures/models.rb +164 -0
  147. data/spec/fixtures/structure.sql +146 -0
  148. data/spec/spec_helper.rb +61 -0
  149. data/spec/sphinx_helper.rb +60 -0
  150. data/spec/support/rails.rb +25 -0
  151. data/spec/thinking_sphinx/active_record/delta_spec.rb +122 -0
  152. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +173 -0
  153. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  154. data/spec/thinking_sphinx/active_record_spec.rb +573 -0
  155. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
  156. data/spec/thinking_sphinx/association_spec.rb +250 -0
  157. data/spec/thinking_sphinx/attribute_spec.rb +552 -0
  158. data/spec/thinking_sphinx/auto_version_spec.rb +103 -0
  159. data/spec/thinking_sphinx/configuration_spec.rb +326 -0
  160. data/spec/thinking_sphinx/context_spec.rb +126 -0
  161. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  162. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  163. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  164. data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
  165. data/spec/thinking_sphinx/facet_spec.rb +359 -0
  166. data/spec/thinking_sphinx/field_spec.rb +127 -0
  167. data/spec/thinking_sphinx/index/builder_spec.rb +532 -0
  168. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  169. data/spec/thinking_sphinx/index_spec.rb +189 -0
  170. data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
  171. data/spec/thinking_sphinx/search_spec.rb +1455 -0
  172. data/spec/thinking_sphinx/source_spec.rb +267 -0
  173. data/spec/thinking_sphinx/test_spec.rb +20 -0
  174. data/spec/thinking_sphinx_spec.rb +204 -0
  175. metadata +524 -0
@@ -0,0 +1,53 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module AttributeUpdates
4
+ def self.included(base)
5
+ base.class_eval do
6
+ after_save :update_attribute_values
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def update_attribute_values
13
+ return true unless ThinkingSphinx.updates_enabled? &&
14
+ ThinkingSphinx.sphinx_running?
15
+
16
+ self.class.sphinx_indexes.each do |index|
17
+ attribute_pairs = attribute_values_for_index(index)
18
+ attribute_names = attribute_pairs.keys
19
+ attribute_values = attribute_names.collect { |key|
20
+ attribute_pairs[key]
21
+ }
22
+
23
+ update_index index.core_name, attribute_names, attribute_values
24
+ next unless index.delta?
25
+ update_index index.delta_name, attribute_names, attribute_values
26
+ end
27
+
28
+ true
29
+ end
30
+
31
+ def updatable_attributes(index)
32
+ index.attributes.select { |attrib| attrib.updatable? }
33
+ end
34
+
35
+ def attribute_values_for_index(index)
36
+ updatable_attributes(index).inject({}) { |hash, attrib|
37
+ hash[attrib.unique_name.to_s] = attrib.live_value self
38
+ hash
39
+ }
40
+ end
41
+
42
+ def update_index(index_name, attribute_names, attribute_values)
43
+ config = ThinkingSphinx::Configuration.instance
44
+ config.client.update index_name, attribute_names, {
45
+ sphinx_document_id => attribute_values
46
+ }
47
+ rescue Riddle::ConnectionError, Riddle::ResponseError,
48
+ ThinkingSphinx::SphinxError, Errno::ETIMEDOUT
49
+ # Not the end of the world if Sphinx isn't running.
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module CollectionProxy
4
+ def search(*args)
5
+ proxy_association.klass.search(*association_args(args))
6
+ end
7
+
8
+ def facets(*args)
9
+ proxy_association.klass.facets(*association_args(args))
10
+ end
11
+
12
+ private
13
+
14
+ def association_args(args)
15
+ options = args.extract_options!
16
+ options[:with] ||= {}
17
+ options[:with].merge! default_filter
18
+
19
+ args + [options]
20
+ end
21
+
22
+ def attribute_for_foreign_key
23
+ if proxy_association.reflection.through_reflection
24
+ foreign_key = proxy_association.reflection.through_reflection.foreign_key
25
+ else
26
+ foreign_key = proxy_association.reflection.foreign_key
27
+ end
28
+
29
+ proxy_association.klass.define_indexes
30
+ (proxy_association.klass.sphinx_indexes || []).each do |index|
31
+ attribute = index.attributes.detect { |attrib|
32
+ attrib.columns.length == 1 &&
33
+ attrib.columns.first.__name == foreign_key.to_sym ||
34
+ attrib.alias == foreign_key.to_sym
35
+ }
36
+ return attribute unless attribute.nil?
37
+ end
38
+
39
+ raise "Missing Attribute for Foreign Key #{foreign_key}"
40
+ end
41
+
42
+ def default_filter
43
+ {attribute_for_foreign_key.unique_name => proxy_association.owner.id}
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module CollectionProxyWithScopes
4
+ def self.included(base)
5
+ base.class_eval do
6
+ alias_method_chain :method_missing, :sphinx_scopes
7
+ alias_method_chain :respond_to?, :sphinx_scopes
8
+ end
9
+ end
10
+
11
+ def method_missing_with_sphinx_scopes(method, *args, &block)
12
+ klass = proxy_association.klass
13
+ if klass.respond_to?(:sphinx_scopes) && klass.sphinx_scopes.include?(method)
14
+ klass.search(:with => default_filter).send(method, *args, &block)
15
+ else
16
+ method_missing_without_sphinx_scopes(method, *args, &block)
17
+ end
18
+ end
19
+
20
+ def respond_to_with_sphinx_scopes?(method)
21
+ proxy_association.klass.respond_to?(:sphinx_scopes) &&
22
+ proxy_association.klass.sphinx_scopes.include?(method) ||
23
+ respond_to_without_sphinx_scopes?(method)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ # This module contains all the delta-related code for models. There isn't
4
+ # really anything you need to call manually in here - except perhaps
5
+ # index_delta, but not sure what reason why.
6
+ #
7
+ module Delta
8
+ # Code for after_commit callback is written by Eli Miller:
9
+ # http://elimiller.blogspot.com/2007/06/proper-cache-expiry-with-aftercommit.html
10
+ # with slight modification from Joost Hietbrink.
11
+ #
12
+ def self.included(base)
13
+ base.class_eval do
14
+ class << self
15
+ # Build the delta index for the related model. This won't be called
16
+ # if running in the test environment.
17
+ #
18
+ def index_delta(instance = nil)
19
+ delta_objects.each { |obj| obj.index(self, instance) }
20
+ end
21
+
22
+ def delta_objects
23
+ self.sphinx_indexes.collect(&:delta_object).compact
24
+ end
25
+ end
26
+
27
+ def toggled_delta?
28
+ self.class.delta_objects.any? { |obj| obj.toggled(self) }
29
+ end
30
+
31
+ private
32
+
33
+ # Set the delta value for the model to be true.
34
+ def toggle_delta
35
+ self.class.delta_objects.each { |obj|
36
+ obj.toggle(self)
37
+ } if should_toggle_delta?
38
+ end
39
+
40
+ # Build the delta index for the related model. This won't be called
41
+ # if running in the test environment.
42
+ #
43
+ def index_delta
44
+ self.class.index_delta(self) if self.class.delta_objects.any? { |obj|
45
+ obj.toggled(self)
46
+ }
47
+ end
48
+
49
+ def should_toggle_delta?
50
+ return fire_delta? if respond_to?(:fire_delta?)
51
+
52
+ self.new_record? || indexed_data_changed?
53
+ end
54
+
55
+ def indexed_data_changed?
56
+ sphinx_indexes.any? { |index|
57
+ index.fields.any? { |field| field.changed?(self) } ||
58
+ index.attributes.any? { |attrib|
59
+ attrib.public? && attrib.changed?(self) && !attrib.updatable?
60
+ }
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,44 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module HasManyAssociation
4
+ def search(*args)
5
+ @reflection.klass.search(*association_args(args))
6
+ end
7
+
8
+ def facets(*args)
9
+ @reflection.klass.facets(*association_args(args))
10
+ end
11
+
12
+ private
13
+
14
+ def association_args(args)
15
+ options = args.extract_options!
16
+ options[:with] ||= {}
17
+ options[:with].merge! default_filter
18
+
19
+ args + [options]
20
+ end
21
+
22
+ def attribute_for_foreign_key
23
+ foreign_key = @reflection.primary_key_name
24
+ stack = [@reflection.options[:through]].compact
25
+
26
+ @reflection.klass.define_indexes
27
+ (@reflection.klass.sphinx_indexes || []).each do |index|
28
+ attribute = index.attributes.detect { |attrib|
29
+ attrib.columns.length == 1 &&
30
+ attrib.columns.first.__name == foreign_key.to_sym ||
31
+ attrib.alias == foreign_key.to_sym
32
+ }
33
+ return attribute unless attribute.nil?
34
+ end
35
+
36
+ raise "Missing Attribute for Foreign Key #{foreign_key}"
37
+ end
38
+
39
+ def default_filter
40
+ {attribute_for_foreign_key.unique_name => @owner.id}
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module HasManyAssociationWithScopes
4
+ def method_missing(method, *args, &block)
5
+ if responds_to_scope(method)
6
+ @reflection.klass.
7
+ search(:with => default_filter).
8
+ send(method, *args, &block)
9
+ else
10
+ super
11
+ end
12
+ end
13
+
14
+ private
15
+ def responds_to_scope(scope)
16
+ @reflection.klass.respond_to?(:sphinx_scopes) &&
17
+ @reflection.klass.sphinx_scopes.include?(scope)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ require 'active_support/log_subscriber'
2
+
3
+ module ThinkingSphinx
4
+ module ActiveRecord
5
+ class LogSubscriber < ActiveSupport::LogSubscriber
6
+ def self.runtime=(value)
7
+ Thread.current['thinking_sphinx_query_runtime'] = value
8
+ end
9
+
10
+ def self.runtime
11
+ Thread.current['thinking_sphinx_query_runtime'] ||= 0
12
+ end
13
+
14
+ def self.reset_runtime
15
+ rt, self.runtime = runtime, 0
16
+ rt
17
+ end
18
+
19
+ def initialize
20
+ super
21
+ @odd_or_even = false
22
+ end
23
+
24
+ def query(event)
25
+ self.class.runtime += event.duration
26
+ return unless logger.debug?
27
+
28
+ identifier = color('Sphinx Query (%.1fms)' % event.duration, GREEN, true)
29
+ query = event.payload[:query]
30
+ query = color query, nil, true if odd?
31
+
32
+ debug " #{identifier} #{query}"
33
+ end
34
+
35
+ def message(event)
36
+ return unless logger.debug?
37
+
38
+ identifier = color 'Sphinx', GREEN, true
39
+ message = event.payload[:message]
40
+ message = color message, nil, true if odd?
41
+
42
+ debug " #{identifier} #{message}"
43
+ end
44
+
45
+ def odd?
46
+ @odd_or_even = !@odd_or_even
47
+ end
48
+
49
+ def logger
50
+ return @logger if defined? @logger
51
+ self.logger = ::ActiveRecord::Base.logger
52
+ end
53
+
54
+ def logger=(logger)
55
+ @logger = logger
56
+ end
57
+
58
+ attach_to :thinking_sphinx
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,110 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module Scopes
4
+ def self.included(base)
5
+ base.class_eval do
6
+ extend ThinkingSphinx::ActiveRecord::Scopes::ClassMethods
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ # Similar to ActiveRecord's default_scope method Thinking Sphinx supports
13
+ # a default_sphinx_scope. For example:
14
+ #
15
+ # default_sphinx_scope :some_sphinx_named_scope
16
+ #
17
+ # The scope is automatically applied when the search method is called. It
18
+ # will only be applied if it is an existing sphinx_scope.
19
+ def default_sphinx_scope(sphinx_scope_name)
20
+ add_sphinx_scopes_support_to_has_many_associations
21
+ @default_sphinx_scope = sphinx_scope_name
22
+ end
23
+
24
+ # Returns the default_sphinx_scope or nil if none is set.
25
+ def get_default_sphinx_scope
26
+ @default_sphinx_scope
27
+ end
28
+
29
+ # Returns true if the current Model has a default_sphinx_scope. Also checks if
30
+ # the default_sphinx_scope actually is a scope.
31
+ def has_default_sphinx_scope?
32
+ !@default_sphinx_scope.nil? && sphinx_scopes.include?(@default_sphinx_scope)
33
+ end
34
+
35
+ # Similar to ActiveRecord's named_scope method Thinking Sphinx supports
36
+ # scopes. For example:
37
+ #
38
+ # sphinx_scope(:latest_first) {
39
+ # {:order => 'created_at DESC, @relevance DESC'}
40
+ # }
41
+ #
42
+ # Usage:
43
+ #
44
+ # @articles = Article.latest_first.search 'pancakes'
45
+ #
46
+ def sphinx_scope(method, &block)
47
+ add_sphinx_scopes_support_to_has_many_associations
48
+
49
+ @sphinx_scopes ||= []
50
+ @sphinx_scopes << method
51
+
52
+ singleton_class.instance_eval do
53
+ define_method(method) do |*args|
54
+ options = {:classes => classes_option}
55
+ options.merge! block.call(*args)
56
+
57
+ ThinkingSphinx::Search.new(options)
58
+ end
59
+
60
+ define_method("#{method}_without_default".to_sym) do |*args|
61
+ options = {:classes => classes_option, :ignore_default => true}
62
+ options.merge! block.call(*args)
63
+
64
+ ThinkingSphinx::Search.new(options)
65
+ end
66
+ end
67
+ end
68
+
69
+ # This returns an Array of all defined scopes. The default
70
+ # scope shows as :default.
71
+ def sphinx_scopes
72
+ @sphinx_scopes || []
73
+ end
74
+
75
+ def remove_sphinx_scopes
76
+ sphinx_scopes.each do |scope|
77
+ singleton_class.send(:undef_method, scope)
78
+ end
79
+
80
+ sphinx_scopes.clear
81
+ end
82
+
83
+ def add_sphinx_scopes_support_to_has_many_associations
84
+ mixin = sphinx_scopes_support_mixin
85
+ sphinx_scopes_support_classes.each do |klass|
86
+ klass.send(:include, mixin) unless klass.ancestors.include?(mixin)
87
+ end
88
+ end
89
+
90
+ def sphinx_scopes_support_classes
91
+ if ThinkingSphinx.rails_3_1?
92
+ [::ActiveRecord::Associations::CollectionProxy]
93
+ else
94
+ [::ActiveRecord::Associations::HasManyAssociation,
95
+ ::ActiveRecord::Associations::HasManyThroughAssociation]
96
+ end
97
+ end
98
+
99
+ def sphinx_scopes_support_mixin
100
+ if ThinkingSphinx.rails_3_1?
101
+ ::ThinkingSphinx::ActiveRecord::CollectionProxyWithScopes
102
+ else
103
+ ::ThinkingSphinx::ActiveRecord::HasManyAssociationWithScopes
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+ end
110
+ end