thinking-sphinx 2.0.6 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/HISTORY +157 -0
  2. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  3. data/lib/cucumber/thinking_sphinx/internal_world.rb +127 -0
  4. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  5. data/lib/thinking-sphinx.rb +1 -0
  6. data/lib/thinking_sphinx/action_controller.rb +31 -0
  7. data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
  8. data/lib/thinking_sphinx/active_record/collection_proxy.rb +40 -0
  9. data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
  10. data/lib/thinking_sphinx/active_record/delta.rb +65 -0
  11. data/lib/thinking_sphinx/active_record/has_many_association.rb +37 -0
  12. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  13. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  14. data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
  15. data/lib/thinking_sphinx/active_record.rb +383 -0
  16. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  17. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
  18. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +171 -0
  19. data/lib/thinking_sphinx/association.rb +229 -0
  20. data/lib/thinking_sphinx/attribute.rb +407 -0
  21. data/lib/thinking_sphinx/auto_version.rb +38 -0
  22. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  23. data/lib/thinking_sphinx/class_facet.rb +20 -0
  24. data/lib/thinking_sphinx/configuration.rb +335 -0
  25. data/lib/thinking_sphinx/context.rb +77 -0
  26. data/lib/thinking_sphinx/core/string.rb +15 -0
  27. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  28. data/lib/thinking_sphinx/deltas.rb +28 -0
  29. data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
  30. data/lib/thinking_sphinx/excerpter.rb +23 -0
  31. data/lib/thinking_sphinx/facet.rb +128 -0
  32. data/lib/thinking_sphinx/facet_search.rb +170 -0
  33. data/lib/thinking_sphinx/field.rb +98 -0
  34. data/lib/thinking_sphinx/index/builder.rb +312 -0
  35. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  36. data/lib/thinking_sphinx/index.rb +157 -0
  37. data/lib/thinking_sphinx/join.rb +37 -0
  38. data/lib/thinking_sphinx/property.rb +185 -0
  39. data/lib/thinking_sphinx/railtie.rb +46 -0
  40. data/lib/thinking_sphinx/search.rb +995 -0
  41. data/lib/thinking_sphinx/search_methods.rb +439 -0
  42. data/lib/thinking_sphinx/sinatra.rb +7 -0
  43. data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
  44. data/lib/thinking_sphinx/source/sql.rb +157 -0
  45. data/lib/thinking_sphinx/source.rb +194 -0
  46. data/lib/thinking_sphinx/tasks.rb +132 -0
  47. data/lib/thinking_sphinx/test.rb +55 -0
  48. data/lib/thinking_sphinx/version.rb +3 -0
  49. data/lib/thinking_sphinx.rb +296 -0
  50. metadata +53 -4
data/HISTORY ADDED
@@ -0,0 +1,157 @@
1
+ 2.0.6 - August 28th 2011
2
+ * Don't change superclass index definitions (otherwise sibling subclasses can end up with each others' index definitions).
3
+ * Improved scope support (Andrew White).
4
+ * Fixed association-filtered searches for Rails 3.1 (Andrew White).
5
+ * Fixed polymorphic association support for Rails 3.1 (Jonathan Viney).
6
+ * 1.4.7 changes.
7
+
8
+ 2.0.5 - May 25th 2011
9
+ * Railtie equivalent for Sinatra.
10
+ * Rails 3.1 support (Florent Piteau).
11
+ * 1.4.6 changes.
12
+
13
+ 2.0.4 - May 12th 2011
14
+ * Only determine Sphinx version when initializing Thinking Sphinx, instead of upon require.
15
+ * Still execute logged blocks for searching even when logging isn't enabled.
16
+ * 1.4.5 changes.
17
+
18
+ 2.0.3 - April 3rd 2011
19
+ * 1.4.4 changes.
20
+
21
+ 2.0.2 - January 13th 2011
22
+ * Improvement to loading the environment via Rake (Brenton Fletcher).
23
+ * 1.4.2 changes.
24
+ * 1.4.1 changes.
25
+
26
+ 2.0.1 - November 19th 2011
27
+ * Intermittent as_json bug fix.
28
+ * Using Rails 3 logging system (Stephen Celis).
29
+ * Fixes for Rails 3.0.2 (Ivan Ukhov).
30
+
31
+ 2.0.0 - November 14th 2011
32
+ * Handle has_many conditions defined as a hash (Artem Orlov).
33
+ * Deprecation fixes for logging (Anton Sozontov).
34
+ * Only load Thinking Sphinx if ActiveRecord is loaded (Matthew Higgins).
35
+ * Rails 3 support.
36
+ * 1.4.0 changes.
37
+
38
+ 1.4.7 - August 28th 2011
39
+ * Don't search for objects when updating attributes on every save - if the object isn't there, then the error will be caught anyway.
40
+ * Extra flexibility for association attribute references (Andrew White).
41
+ * Setting up test suite for Travis, and fixing tests across many rubies.
42
+ * Removing test for filtering on wordcount attribute - seems Sphinx doesn't like that.
43
+ * If many indices are searched on, only use the first for excerpts.
44
+ * Allow MVA facets from SQL strings that are all integers to skip object translation.
45
+ * Adding total_count method for better Kaminari support (Dan Pickett).
46
+ * Switching from Jeweler to Bundler for gem management.
47
+ * Cleaning up Context - re-using camelized class names.
48
+ * Simplified auto-star regex for 1.9 - no need to set the unicode flag.
49
+ * Don't update sphinx_internal_id attribute when updating a record's attributes.
50
+ * Ignore empty arrays for :without_ids (Jason Rust).
51
+ * Now catching Riddle::ResponseError when we aren't fussed about the responses from Sphinx.
52
+ * Distinguishing between Ruby 1.8 and 1.9 for auto-star regex (Andrew White).
53
+ * Don't re-index at the end of suspended_delta calls if requested.
54
+ * Adding stop_timeout setting to configure how long ts:stop should wait while checking whether Sphinx has stopped (Justin Tanner).
55
+ * Speed improvement for types_to_crcs (Josh Goebel).
56
+ * Adding a touched_reindex_file option, for a file that will be touched after indices are rotated (Rémi Prévost).
57
+ * Better support for the Mysql2 adapter (Andrew White).
58
+ * More pagination methods - next_page? and first_page?.
59
+ * Adding use_64_bit option to ensure all timestamp attributes are treated as 64 bit integers (Andrey Deryabin).
60
+ * Simpler, less brittle support for all Sphinx settings via Riddle (Clemens Kofler).
61
+ * Optimised start/stop behaviour in Capistrano recipe (Lars Weiler).
62
+ * Catch and ignore timeout errors for low priority requests.
63
+ * Don't double up on de-polymorphised associations for generated Sphinx source SQL queries.
64
+
65
+ 1.4.6 - May 25th 2011
66
+ * Require Riddle 1.3.3 or better.
67
+ * Cast Sphinx document ids to 32bit integers to keep PostgreSQL happy when Sphinx is compiled for id64 support (Bruno Santschi).
68
+ * Making sure suspended_delta always exists on indexed models, whether indices have been defined or not.
69
+ * Workaround for ActsAsTaggableOn to ensure association joins work.
70
+ * Use Sphinx string attributes instead of class_crc integers to determine which class each search result is from (Sphinx 2.x or newer).
71
+ * String attribute support - and so, using this instead of str2ordinal for string sorting.
72
+ * Support for fields with paired string attributes and wordcount attributes (Sphinx 1.10-beta or newer).
73
+ * Support for file fields (Sphinx 1.10-beta or newer).
74
+ * Wordcount attribute support (Sphinx 1.10-beta or newer).
75
+ * Sinatra support is only used when Rails isn't loaded.
76
+ * Sphinx 2.0.x (including 2.0.2-dev) support.
77
+ * :without_any filtering option (the reverse of :with_all).
78
+
79
+ 1.4.5 - May 12th 2011
80
+ * Require Riddle 1.3.2 or better.
81
+ * Improved 1.9.2 support
82
+ * Don't hack Array - make ThinkingSphinx::Search a subclass of Array instead.
83
+ * Distinguishing between suspended deltas and disabled deltas.
84
+ * Removing most Thread references, opting for Mutexes for better multi-threading support.
85
+ * Added support for client_key to allow for authenticated Sphinx communication.
86
+ * Sphinx 2.0.1 support.
87
+ * Don't try to translate nil values (Alex Chee).
88
+ * Performance fix for facets (Clemens Kofler).
89
+ * Use Sphinx 0.9.9 in Capistrano recipe (Konstantin Shabanov).
90
+ * Sinatra support (Patrick Tulskie).
91
+ * Offer sanitize_sql method within define_index blocks (Matt Todd).
92
+ * Performance fix: caching primary_key_for_sphinx value.
93
+
94
+ 1.4.4 - April 3 2011
95
+ * Being consistent with === comparisons for Array monkeypatch.
96
+ * Kaminari pagination support.
97
+ * Ensure deltas are supported across multiple indices for a given model.
98
+ * Use custom index names for excerpts calls if that's what exists (Javier Ramírez).
99
+ * Allow for Sphinx versions compiled from source (Greg Weber).
100
+ * Ensuring thread is being required to allow Mutexes to work.
101
+ * Ensure primary_key_for_sphinx is inherited to subclasses (Robert Stern).
102
+ * Don't complain if there's an error when checking for documents in a given index.
103
+
104
+ 1.4.3 - January 29th 2011
105
+ * Don't memoize the database adapter.
106
+ * Allow queries to just return specific attributes via the :only option, instead of ActiveRecord objects (Hans Hasselberg).
107
+ * Performance fix: Only add sphinx scopes to has_many associations if there are scopes in play (Kirill Maximov).
108
+ * Fixing CRC32 function for PostgreSQL to handle UTF characters that use more than one byte.
109
+
110
+ 1.4.2 - January 13th 2011
111
+ * Ignore Sphinx errors when updating attributes (it's a low priority request).
112
+ * Don't use the CRC32 function within the generated SQL queries for class types.
113
+ * Direct facets to use a method to translate values using :value.
114
+ * Ensure that if one sphinx_internal_id attribute is a bigint, they all are.
115
+ * Ignore Sphinx errors when marking documents as deleted (it's a low priority request).
116
+ * Don't select columns from polymorphic joins if they're not available.
117
+ * Don't query Sphinx for a facet request if zero facets are requested.
118
+ * Ensure we're only using local_options when it's available (Paul Schyska).
119
+ * Don't presume the environment for Capistrano is production (Robert Glaser).
120
+ * Allow for custom database adapters.
121
+ * Fix for custom delta columns (Marcin Stecki).
122
+
123
+ 1.4.1 - December 21st 2010
124
+ * No longer looking for attributes in :conditions option when searching.
125
+ * Copy and then modify sort arguments.
126
+ * Allow the generation of Sphinx configuration objects without necessarily writing to the configuration file.
127
+ * Allow Sphinx 1.10-beta versions that have been compiled with id64 support.
128
+ * Raise Sphinx errors when searching - which can be ignored using the :ignore_errors option (Matt Todd).
129
+ * Handle complex :include arguments, including hashes within arrays (Paco Guzmán).
130
+ * Handle hashes passed through to :include when searching.
131
+ * Can now require either 'thinking_sphinx' or 'thinking-sphinx'.
132
+ * Excerpts are less fussy about inputs.
133
+ * No longer hard-coding RSpec colour setting - that's up to the developer.
134
+ * Require Riddle 1.2.0 or better.
135
+ * Use searchd to stop Sphinx (via --stop or --stopwait arguments) (Matt Todd).
136
+
137
+ 1.4.0 - November 14th 2010
138
+ * No longer supporting attributes in :conditions option.
139
+ * Case insensitive field sorting if :sortable is set to :insensitive.
140
+ * Using Bundler for development
141
+ * Can use make facet requests on existing searches and Sphinx scopes.
142
+ * Don't star field markers in search queries.
143
+ * Can use search_for_ids on Sphinx scopes.
144
+ * Ensure the CRC32 function for PostgreSQL handles empty strings and NULLs.
145
+ * Allow custom determination for database adapters.
146
+ * :include search option now limits itself to the relevant classes when searching across more than one class.
147
+ * Don't memoize primary_key_for_sphinx, which was causing trouble when objects are deleted or frozen.
148
+ * Allow for excerpts options when searching (using :excerpt_options) (Lee Capps).
149
+ * Wrap UTC in quotes when setting the timezone in PostgreSQL (Keith Pitt).
150
+ * Don't colourize logs unless ActiveRecord allows for it.
151
+ * Consistent contribution documentation (Sam Goldstein).
152
+ * Allow the setting of Riddle's timeout value via sphinx.yml (Sam Goldstein).
153
+ * 1.10-beta support.
154
+ * Can set the riddle client for the search request using :client.
155
+ * Fixing default sphinx scope support.
156
+ * Make a reasonable attempt to check that Sphinx has stopped (Matt Todd).
157
+ * Be more verbose if a model can't be loaded by Thinking Sphinx.
@@ -0,0 +1,12 @@
1
+ require 'thinking_sphinx/test'
2
+
3
+ module Cucumber
4
+ module ThinkingSphinx
5
+ class ExternalWorld
6
+ def initialize(suppress_delta_output = true)
7
+ ::ThinkingSphinx::Test.init
8
+ ::ThinkingSphinx::Test.start_with_autostop
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,127 @@
1
+ require 'cucumber/thinking_sphinx/sql_logger'
2
+
3
+ module Cucumber
4
+ module ThinkingSphinx
5
+ class InternalWorld
6
+ attr_accessor :temporary_directory, :migrations_directory,
7
+ :models_directory, :fixtures_directory, :database_file
8
+ attr_accessor :adapter, :database, :username,
9
+ :password, :host
10
+
11
+ def initialize
12
+ pwd = Dir.pwd
13
+ @temporary_directory = "#{pwd}/tmp"
14
+ @migrations_directory = "#{pwd}/features/thinking_sphinx/db/migrations"
15
+ @models_directory = "#{pwd}/features/thinking_sphinx/models"
16
+ @fixtures_directory = "#{pwd}/features/thinking_sphinx/db/fixtures"
17
+ @database_file = "#{pwd}/features/thinking_sphinx/database.yml"
18
+
19
+ @adapter = (ENV['DATABASE'] || 'mysql').gsub /^mysql$/, 'mysql2'
20
+ @database = 'thinking_sphinx'
21
+ @username = @adapter[/mysql/] ? 'root' : 'postgres'
22
+ # @password = 'thinking_sphinx'
23
+ @host = 'localhost'
24
+ end
25
+
26
+ def setup
27
+ make_temporary_directory
28
+
29
+ configure_cleanup
30
+ configure_thinking_sphinx
31
+ configure_active_record
32
+
33
+ prepare_data
34
+ setup_sphinx
35
+
36
+ self
37
+ end
38
+
39
+ def configure_database
40
+ ActiveRecord::Base.establish_connection database_settings
41
+ self
42
+ end
43
+
44
+ private
45
+
46
+ def config
47
+ @config ||= ::ThinkingSphinx::Configuration.instance
48
+ end
49
+
50
+ def make_temporary_directory
51
+ FileUtils.mkdir_p temporary_directory
52
+ Dir["#{temporary_directory}/*"].each do |file|
53
+ FileUtils.rm_rf file
54
+ end
55
+ end
56
+
57
+ def configure_thinking_sphinx
58
+ config.config_file = "#{temporary_directory}/sphinx.conf"
59
+ config.searchd_log_file = "#{temporary_directory}/searchd.log"
60
+ config.query_log_file = "#{temporary_directory}/searchd.query.log"
61
+ config.pid_file = "#{temporary_directory}/searchd.pid"
62
+ config.searchd_file_path = "#{temporary_directory}/indexes/"
63
+
64
+ ::ThinkingSphinx.suppress_delta_output = true
65
+ end
66
+
67
+ def configure_cleanup
68
+ Kernel.at_exit do
69
+ ::ThinkingSphinx::Configuration.instance.controller.stop
70
+ sleep(0.5) # Ensure Sphinx has shut down completely
71
+ ::ThinkingSphinx::ActiveRecord::LogSubscriber.logger.close
72
+ end
73
+ end
74
+
75
+ def yaml_database_settings
76
+ return {} unless File.exist?(@database_file)
77
+
78
+ YAML.load open(@database_file)
79
+ end
80
+
81
+ def database_settings
82
+ {
83
+ 'adapter' => @adapter,
84
+ 'database' => @database,
85
+ 'username' => @username,
86
+ 'password' => @password,
87
+ 'host' => @host
88
+ }.merge yaml_database_settings
89
+ end
90
+
91
+ def configure_active_record
92
+ ::ThinkingSphinx::ActiveRecord::LogSubscriber.logger = Logger.new(
93
+ open("#{temporary_directory}/active_record.log", "a")
94
+ )
95
+
96
+ ActiveRecord::Base.connection.class.send(
97
+ :include, Cucumber::ThinkingSphinx::SqlLogger
98
+ )
99
+ end
100
+
101
+ def prepare_data
102
+ ::ThinkingSphinx.deltas_enabled = false
103
+
104
+ load_files migrations_directory
105
+ load_files models_directory
106
+ load_files fixtures_directory
107
+
108
+ ::ThinkingSphinx.deltas_enabled = true
109
+ end
110
+
111
+ def load_files(path)
112
+ files = Dir["#{path}/*.rb"].sort!
113
+ files.each do |file|
114
+ require file.gsub(/\.rb$/, '')
115
+ end
116
+ end
117
+
118
+ def setup_sphinx
119
+ FileUtils.mkdir_p config.searchd_file_path
120
+
121
+ config.build
122
+ config.controller.index
123
+ config.controller.start
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,20 @@
1
+ module Cucumber
2
+ module ThinkingSphinx
3
+ module SqlLogger
4
+ def self.included(base)
5
+ base.send :alias_method_chain, :execute, :query_record
6
+ end
7
+
8
+ IGNORED_SQL = [
9
+ /^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/,
10
+ /^SELECT @@ROWCOUNT/, /^SHOW FIELDS/
11
+ ]
12
+
13
+ def execute_with_query_record(sql, name = nil, &block)
14
+ $queries_executed ||= []
15
+ $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
16
+ execute_without_query_record(sql, name, &block)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ require 'thinking_sphinx'
@@ -0,0 +1,31 @@
1
+ module ThinkingSphinx
2
+ module ActionController
3
+ extend ActiveSupport::Concern
4
+
5
+ protected
6
+
7
+ attr_internal :query_runtime
8
+
9
+ def cleanup_view_runtime
10
+ log_subscriber = ThinkingSphinx::ActiveRecord::LogSubscriber
11
+ query_runtime_pre_render = log_subscriber.reset_runtime
12
+ runtime = super
13
+ query_runtime_post_render = log_subscriber.reset_runtime
14
+ self.query_runtime = query_runtime_pre_render + query_runtime_post_render
15
+ runtime - query_runtime_post_render
16
+ end
17
+
18
+ def append_info_to_payload(payload)
19
+ super
20
+ payload[:query_runtime] = query_runtime
21
+ end
22
+
23
+ module ClassMethods
24
+ def log_process_action(payload)
25
+ messages, query_runtime = super, payload[:query_runtime]
26
+ messages << ("Sphinx: %.1fms" % query_runtime.to_f) if query_runtime
27
+ messages
28
+ end
29
+ end
30
+ end
31
+ end
@@ -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,40 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module CollectionProxy
4
+ def search(*args)
5
+ options = args.extract_options!
6
+ options[:with] ||= {}
7
+ options[:with].merge! default_filter
8
+
9
+ args << options
10
+ proxy_association.klass.search(*args)
11
+ end
12
+
13
+ private
14
+
15
+ def attribute_for_foreign_key
16
+ if proxy_association.reflection.through_reflection
17
+ foreign_key = proxy_association.reflection.through_reflection.foreign_key
18
+ else
19
+ foreign_key = proxy_association.reflection.foreign_key
20
+ end
21
+
22
+ proxy_association.klass.define_indexes
23
+ (proxy_association.klass.sphinx_indexes || []).each do |index|
24
+ attribute = index.attributes.detect { |attrib|
25
+ attrib.columns.length == 1 &&
26
+ attrib.columns.first.__name == foreign_key.to_sym ||
27
+ attrib.alias == foreign_key.to_sym
28
+ }
29
+ return attribute unless attribute.nil?
30
+ end
31
+
32
+ raise "Missing Attribute for Foreign Key #{foreign_key}"
33
+ end
34
+
35
+ def default_filter
36
+ {attribute_for_foreign_key.unique_name => proxy_association.owner.id}
37
+ end
38
+ end
39
+ end
40
+ 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?(scope) ||
23
+ respond_to_without_sphinx_scopes?(method)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,65 @@
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
+ self.new_record? || indexed_data_changed?
51
+ end
52
+
53
+ def indexed_data_changed?
54
+ sphinx_indexes.any? { |index|
55
+ index.fields.any? { |field| field.changed?(self) } ||
56
+ index.attributes.any? { |attrib|
57
+ attrib.public? && attrib.changed?(self) && !attrib.updatable?
58
+ }
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,37 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module HasManyAssociation
4
+ def search(*args)
5
+ options = args.extract_options!
6
+ options[:with] ||= {}
7
+ options[:with].merge! default_filter
8
+
9
+ args << options
10
+ @reflection.klass.search(*args)
11
+ end
12
+
13
+ private
14
+
15
+ def attribute_for_foreign_key
16
+ foreign_key = @reflection.primary_key_name
17
+ stack = [@reflection.options[:through]].compact
18
+
19
+ @reflection.klass.define_indexes
20
+ (@reflection.klass.sphinx_indexes || []).each do |index|
21
+ attribute = index.attributes.detect { |attrib|
22
+ attrib.columns.length == 1 &&
23
+ attrib.columns.first.__name == foreign_key.to_sym ||
24
+ attrib.alias == foreign_key.to_sym
25
+ }
26
+ return attribute unless attribute.nil?
27
+ end
28
+
29
+ raise "Missing Attribute for Foreign Key #{foreign_key}"
30
+ end
31
+
32
+ def default_filter
33
+ {attribute_for_foreign_key.unique_name => @owner.id}
34
+ end
35
+ end
36
+ end
37
+ 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