warp-thinking-sphinx 1.2.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. data/LICENCE +20 -0
  2. data/README.textile +144 -0
  3. data/VERSION.yml +4 -0
  4. data/features/a.rb +17 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +33 -0
  8. data/features/datetime_deltas.feature +66 -0
  9. data/features/delayed_delta_indexing.feature +37 -0
  10. data/features/deleting_instances.feature +64 -0
  11. data/features/direct_attributes.feature +11 -0
  12. data/features/excerpts.feature +13 -0
  13. data/features/extensible_delta_indexing.feature +9 -0
  14. data/features/facets.feature +76 -0
  15. data/features/facets_across_model.feature +29 -0
  16. data/features/handling_edits.feature +92 -0
  17. data/features/retry_stale_indexes.feature +24 -0
  18. data/features/searching_across_models.feature +20 -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 +35 -0
  23. data/features/step_definitions/alpha_steps.rb +3 -0
  24. data/features/step_definitions/beta_steps.rb +7 -0
  25. data/features/step_definitions/common_steps.rb +178 -0
  26. data/features/step_definitions/datetime_delta_steps.rb +15 -0
  27. data/features/step_definitions/delayed_delta_indexing_steps.rb +7 -0
  28. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  29. data/features/step_definitions/facet_steps.rb +92 -0
  30. data/features/step_definitions/find_arguments_steps.rb +36 -0
  31. data/features/step_definitions/gamma_steps.rb +15 -0
  32. data/features/step_definitions/scope_steps.rb +11 -0
  33. data/features/step_definitions/search_steps.rb +89 -0
  34. data/features/step_definitions/sphinx_steps.rb +31 -0
  35. data/features/sti_searching.feature +14 -0
  36. data/features/support/db/active_record.rb +40 -0
  37. data/features/support/db/database.example.yml +3 -0
  38. data/features/support/db/fixtures/alphas.rb +10 -0
  39. data/features/support/db/fixtures/authors.rb +1 -0
  40. data/features/support/db/fixtures/betas.rb +10 -0
  41. data/features/support/db/fixtures/boxes.rb +9 -0
  42. data/features/support/db/fixtures/categories.rb +1 -0
  43. data/features/support/db/fixtures/cats.rb +3 -0
  44. data/features/support/db/fixtures/comments.rb +24 -0
  45. data/features/support/db/fixtures/delayed_betas.rb +10 -0
  46. data/features/support/db/fixtures/developers.rb +29 -0
  47. data/features/support/db/fixtures/dogs.rb +3 -0
  48. data/features/support/db/fixtures/extensible_betas.rb +10 -0
  49. data/features/support/db/fixtures/gammas.rb +10 -0
  50. data/features/support/db/fixtures/people.rb +1001 -0
  51. data/features/support/db/fixtures/posts.rb +6 -0
  52. data/features/support/db/fixtures/robots.rb +14 -0
  53. data/features/support/db/fixtures/tags.rb +27 -0
  54. data/features/support/db/fixtures/thetas.rb +10 -0
  55. data/features/support/db/migrations/create_alphas.rb +7 -0
  56. data/features/support/db/migrations/create_animals.rb +5 -0
  57. data/features/support/db/migrations/create_authors.rb +3 -0
  58. data/features/support/db/migrations/create_authors_posts.rb +6 -0
  59. data/features/support/db/migrations/create_betas.rb +5 -0
  60. data/features/support/db/migrations/create_boxes.rb +5 -0
  61. data/features/support/db/migrations/create_categories.rb +3 -0
  62. data/features/support/db/migrations/create_comments.rb +10 -0
  63. data/features/support/db/migrations/create_delayed_betas.rb +17 -0
  64. data/features/support/db/migrations/create_developers.rb +9 -0
  65. data/features/support/db/migrations/create_extensible_betas.rb +5 -0
  66. data/features/support/db/migrations/create_gammas.rb +3 -0
  67. data/features/support/db/migrations/create_people.rb +13 -0
  68. data/features/support/db/migrations/create_posts.rb +5 -0
  69. data/features/support/db/migrations/create_robots.rb +5 -0
  70. data/features/support/db/migrations/create_taggings.rb +5 -0
  71. data/features/support/db/migrations/create_tags.rb +4 -0
  72. data/features/support/db/migrations/create_thetas.rb +5 -0
  73. data/features/support/db/mysql.rb +3 -0
  74. data/features/support/db/postgresql.rb +3 -0
  75. data/features/support/env.rb +6 -0
  76. data/features/support/lib/generic_delta_handler.rb +8 -0
  77. data/features/support/models/alpha.rb +10 -0
  78. data/features/support/models/animal.rb +5 -0
  79. data/features/support/models/author.rb +3 -0
  80. data/features/support/models/beta.rb +8 -0
  81. data/features/support/models/box.rb +8 -0
  82. data/features/support/models/cat.rb +3 -0
  83. data/features/support/models/category.rb +4 -0
  84. data/features/support/models/comment.rb +10 -0
  85. data/features/support/models/delayed_beta.rb +7 -0
  86. data/features/support/models/developer.rb +16 -0
  87. data/features/support/models/dog.rb +3 -0
  88. data/features/support/models/extensible_beta.rb +9 -0
  89. data/features/support/models/gamma.rb +5 -0
  90. data/features/support/models/person.rb +23 -0
  91. data/features/support/models/post.rb +20 -0
  92. data/features/support/models/robot.rb +8 -0
  93. data/features/support/models/tag.rb +3 -0
  94. data/features/support/models/tagging.rb +4 -0
  95. data/features/support/models/theta.rb +7 -0
  96. data/features/support/post_database.rb +43 -0
  97. data/features/support/z.rb +19 -0
  98. data/lib/thinking_sphinx.rb +212 -0
  99. data/lib/thinking_sphinx/active_record.rb +306 -0
  100. data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
  101. data/lib/thinking_sphinx/active_record/delta.rb +87 -0
  102. data/lib/thinking_sphinx/active_record/has_many_association.rb +28 -0
  103. data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
  104. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
  105. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
  106. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +136 -0
  107. data/lib/thinking_sphinx/association.rb +243 -0
  108. data/lib/thinking_sphinx/attribute.rb +340 -0
  109. data/lib/thinking_sphinx/class_facet.rb +15 -0
  110. data/lib/thinking_sphinx/configuration.rb +282 -0
  111. data/lib/thinking_sphinx/core/array.rb +7 -0
  112. data/lib/thinking_sphinx/core/string.rb +15 -0
  113. data/lib/thinking_sphinx/deltas.rb +30 -0
  114. data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
  115. data/lib/thinking_sphinx/deltas/default_delta.rb +68 -0
  116. data/lib/thinking_sphinx/deltas/delayed_delta.rb +30 -0
  117. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
  118. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
  119. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
  120. data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
  121. data/lib/thinking_sphinx/excerpter.rb +22 -0
  122. data/lib/thinking_sphinx/facet.rb +125 -0
  123. data/lib/thinking_sphinx/facet_search.rb +134 -0
  124. data/lib/thinking_sphinx/field.rb +81 -0
  125. data/lib/thinking_sphinx/index.rb +99 -0
  126. data/lib/thinking_sphinx/index/builder.rb +286 -0
  127. data/lib/thinking_sphinx/index/faux_column.rb +110 -0
  128. data/lib/thinking_sphinx/property.rb +163 -0
  129. data/lib/thinking_sphinx/rails_additions.rb +150 -0
  130. data/lib/thinking_sphinx/search.rb +708 -0
  131. data/lib/thinking_sphinx/search_methods.rb +421 -0
  132. data/lib/thinking_sphinx/source.rb +152 -0
  133. data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
  134. data/lib/thinking_sphinx/source/sql.rb +127 -0
  135. data/lib/thinking_sphinx/tasks.rb +165 -0
  136. data/rails/init.rb +14 -0
  137. data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +130 -0
  138. data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +49 -0
  139. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
  140. data/spec/lib/thinking_sphinx/active_record_spec.rb +353 -0
  141. data/spec/lib/thinking_sphinx/association_spec.rb +239 -0
  142. data/spec/lib/thinking_sphinx/attribute_spec.rb +507 -0
  143. data/spec/lib/thinking_sphinx/configuration_spec.rb +268 -0
  144. data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
  145. data/spec/lib/thinking_sphinx/core/string_spec.rb +9 -0
  146. data/spec/lib/thinking_sphinx/deltas/job_spec.rb +32 -0
  147. data/spec/lib/thinking_sphinx/excerpter_spec.rb +57 -0
  148. data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
  149. data/spec/lib/thinking_sphinx/facet_spec.rb +333 -0
  150. data/spec/lib/thinking_sphinx/field_spec.rb +154 -0
  151. data/spec/lib/thinking_sphinx/index/builder_spec.rb +455 -0
  152. data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +30 -0
  153. data/spec/lib/thinking_sphinx/index_spec.rb +45 -0
  154. data/spec/lib/thinking_sphinx/rails_additions_spec.rb +203 -0
  155. data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
  156. data/spec/lib/thinking_sphinx/search_spec.rb +1101 -0
  157. data/spec/lib/thinking_sphinx/source_spec.rb +227 -0
  158. data/spec/lib/thinking_sphinx_spec.rb +162 -0
  159. data/tasks/distribution.rb +55 -0
  160. data/tasks/rails.rake +1 -0
  161. data/tasks/testing.rb +83 -0
  162. data/vendor/after_commit/LICENSE +20 -0
  163. data/vendor/after_commit/README +16 -0
  164. data/vendor/after_commit/Rakefile +22 -0
  165. data/vendor/after_commit/init.rb +8 -0
  166. data/vendor/after_commit/lib/after_commit.rb +45 -0
  167. data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
  168. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
  169. data/vendor/after_commit/test/after_commit_test.rb +53 -0
  170. data/vendor/delayed_job/lib/delayed/job.rb +251 -0
  171. data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
  172. data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
  173. data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
  174. data/vendor/riddle/lib/riddle.rb +30 -0
  175. data/vendor/riddle/lib/riddle/client.rb +635 -0
  176. data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
  177. data/vendor/riddle/lib/riddle/client/message.rb +66 -0
  178. data/vendor/riddle/lib/riddle/client/response.rb +84 -0
  179. data/vendor/riddle/lib/riddle/configuration.rb +33 -0
  180. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
  181. data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
  182. data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
  183. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
  184. data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
  185. data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
  186. data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
  187. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
  188. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
  189. data/vendor/riddle/lib/riddle/controller.rb +53 -0
  190. metadata +267 -0
@@ -0,0 +1,46 @@
1
+ module ThinkingSphinx
2
+ class Source
3
+ module InternalProperties
4
+ def add_internal_attributes_and_facets
5
+ add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key_for_sphinx.to_sym
6
+ add_internal_attribute :class_crc, :integer, crc_column, true
7
+ add_internal_attribute :subclass_crcs, :multi, subclasses_to_s
8
+ add_internal_attribute :sphinx_deleted, :integer, "0"
9
+
10
+ add_internal_facet :class_crc
11
+ end
12
+
13
+ def add_internal_attribute(name, type, contents, facet = false)
14
+ return unless attribute_by_alias(name).nil?
15
+
16
+ Attribute.new(self,
17
+ ThinkingSphinx::Index::FauxColumn.new(contents),
18
+ :type => type,
19
+ :as => name,
20
+ :facet => facet,
21
+ :admin => true
22
+ )
23
+ end
24
+
25
+ def add_internal_facet(name)
26
+ return unless facet_by_alias(name).nil?
27
+
28
+ @model.sphinx_facets << ClassFacet.new(attribute_by_alias(name))
29
+ end
30
+
31
+ def attribute_by_alias(attr_alias)
32
+ @attributes.detect { |attrib| attrib.alias == attr_alias }
33
+ end
34
+
35
+ def facet_by_alias(name)
36
+ @model.sphinx_facets.detect { |facet| facet.name == name }
37
+ end
38
+
39
+ def subclasses_to_s
40
+ "'" + (@model.send(:subclasses).collect { |klass|
41
+ klass.to_crc32.to_s
42
+ } << @model.to_crc32.to_s).join(",") + "'"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,127 @@
1
+ module ThinkingSphinx
2
+ class Source
3
+ module SQL
4
+ # Generates the big SQL statement to get the data back for all the fields
5
+ # and attributes, using all the relevant association joins. If you want
6
+ # the version filtered for delta values, send through :delta => true in the
7
+ # options. Won't do much though if the index isn't set up to support a
8
+ # delta sibling.
9
+ #
10
+ # Examples:
11
+ #
12
+ # source.to_sql
13
+ # source.to_sql(:delta => true)
14
+ #
15
+ def to_sql(options={})
16
+ sql = <<-SQL
17
+ SELECT #{ sql_select_clause options[:offset] }
18
+ FROM #{ @model.quoted_table_name }
19
+ #{ all_associations.collect { |assoc| assoc.to_sql }.join(' ') }
20
+ #{ sql_where_clause(options) }
21
+ SQL
22
+
23
+ sql += " ORDER BY NULL" if adapter.sphinx_identifier == "mysql"
24
+ sql
25
+ end
26
+
27
+ # Simple helper method for the query range SQL - which is a statement that
28
+ # returns minimum and maximum id values. These can be filtered by delta -
29
+ # so pass in :delta => true to get the delta version of the SQL.
30
+ #
31
+ def to_sql_query_range(options={})
32
+ return nil if @index.options[:disable_range]
33
+
34
+ min_statement = adapter.convert_nulls(
35
+ "MIN(#{quote_column(@model.primary_key_for_sphinx)})", 1
36
+ )
37
+ max_statement = adapter.convert_nulls(
38
+ "MAX(#{quote_column(@model.primary_key_for_sphinx)})", 1
39
+ )
40
+
41
+ sql = "SELECT #{min_statement}, #{max_statement} " +
42
+ "FROM #{@model.quoted_table_name} "
43
+ if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
44
+ sql << "WHERE #{@index.delta_object.clause(@model, options[:delta])}"
45
+ end
46
+
47
+ sql
48
+ end
49
+
50
+ # Simple helper method for the query info SQL - which is a statement that
51
+ # returns the single row for a corresponding id.
52
+ #
53
+ def to_sql_query_info(offset)
54
+ "SELECT * FROM #{@model.quoted_table_name} WHERE " +
55
+ "#{quote_column(@model.primary_key_for_sphinx)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
56
+ end
57
+
58
+ def sql_select_clause(offset)
59
+ unique_id_expr = ThinkingSphinx.unique_id_expression(offset)
60
+
61
+ (
62
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} #{unique_id_expr} AS #{quote_column(@model.primary_key_for_sphinx)} "] +
63
+ @fields.collect { |field| field.to_select_sql } +
64
+ @attributes.collect { |attribute| attribute.to_select_sql }
65
+ ).compact.join(", ")
66
+ end
67
+
68
+ def sql_where_clause(options)
69
+ logic = []
70
+ logic += [
71
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} >= $start",
72
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} <= $end"
73
+ ] unless @index.options[:disable_range]
74
+
75
+ if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
76
+ logic << "#{@index.delta_object.clause(@model, options[:delta])}"
77
+ end
78
+
79
+ logic += (@conditions || [])
80
+ logic.empty? ? "" : "WHERE #{logic.join(' AND ')}"
81
+ end
82
+
83
+ def sql_group_clause
84
+ internal_groupings = []
85
+ if @model.column_names.include?(@model.inheritance_column)
86
+ internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
87
+ end
88
+
89
+ (
90
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)}"] +
91
+ @fields.collect { |field| field.to_group_sql }.compact +
92
+ @attributes.collect { |attribute| attribute.to_group_sql }.compact +
93
+ @groupings + internal_groupings
94
+ ).join(", ")
95
+ end
96
+
97
+ def sql_query_pre_for_core
98
+ if self.delta? && !@index.delta_object.reset_query(@model).blank?
99
+ [@index.delta_object.reset_query(@model)]
100
+ else
101
+ []
102
+ end
103
+ end
104
+
105
+ def sql_query_pre_for_delta
106
+ [""]
107
+ end
108
+
109
+ def quote_column(column)
110
+ @model.connection.quote_column_name(column)
111
+ end
112
+
113
+ def crc_column
114
+ if @model.table_exists? &&
115
+ @model.column_names.include?(@model.inheritance_column)
116
+
117
+ adapter.cast_to_unsigned(adapter.convert_nulls(
118
+ adapter.crc(adapter.quote_with_table(@model.inheritance_column), true),
119
+ @model.to_crc32
120
+ ))
121
+ else
122
+ @model.to_crc32.to_s
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,165 @@
1
+ require 'fileutils'
2
+
3
+ namespace :thinking_sphinx do
4
+ task :app_env do
5
+ if defined?(RAILS_ROOT)
6
+ Rake::Task[:environment].invoke
7
+ Rails.configuration.cache_classes = false
8
+ end
9
+
10
+ Rake::Task[:merb_env].invoke if defined?(Merb)
11
+ end
12
+
13
+ desc "Output the current Thinking Sphinx version"
14
+ task :version => :app_env do
15
+ puts "Thinking Sphinx v" + ThinkingSphinx.version
16
+ end
17
+
18
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
19
+ task :running_start => :app_env do
20
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
21
+ Rake::Task["thinking_sphinx:start"].invoke
22
+ end
23
+
24
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
25
+ task :start => :app_env do
26
+ config = ThinkingSphinx::Configuration.instance
27
+
28
+ FileUtils.mkdir_p config.searchd_file_path
29
+ raise RuntimeError, "searchd is already running." if sphinx_running?
30
+
31
+ Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
32
+
33
+ system! "#{config.bin_path}#{config.searchd_binary_name} --pidfile --config \"#{config.config_file}\""
34
+
35
+ sleep(2)
36
+
37
+ if sphinx_running?
38
+ puts "Started successfully (pid #{sphinx_pid})."
39
+ else
40
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
41
+ end
42
+ end
43
+
44
+ desc "Stop Sphinx using Thinking Sphinx's settings"
45
+ task :stop => :app_env do
46
+ unless sphinx_running?
47
+ puts "searchd is not running"
48
+ else
49
+ config = ThinkingSphinx::Configuration.instance
50
+ pid = sphinx_pid
51
+ system! "#{config.bin_path}#{config.searchd_binary_name} --stop --config \"#{config.config_file}\""
52
+ puts "Stopped search daemon (pid #{pid})."
53
+ end
54
+ end
55
+
56
+ desc "Restart Sphinx"
57
+ task :restart => [:app_env, :stop, :start]
58
+
59
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
60
+ task :configure => :app_env do
61
+ config = ThinkingSphinx::Configuration.instance
62
+ puts "Generating Configuration to #{config.config_file}"
63
+ config.build
64
+ end
65
+
66
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
67
+ task :index => :app_env do
68
+ ThinkingSphinx::Deltas::Job.cancel_thinking_sphinx_jobs
69
+
70
+ config = ThinkingSphinx::Configuration.instance
71
+ unless ENV["INDEX_ONLY"] == "true"
72
+ puts "Generating Configuration to #{config.config_file}"
73
+ config.build
74
+ end
75
+
76
+ FileUtils.mkdir_p config.searchd_file_path
77
+ cmd = "#{config.bin_path}#{config.indexer_binary_name} --config \"#{config.config_file}\" --all"
78
+ cmd << " --rotate" if sphinx_running?
79
+
80
+ system! cmd
81
+ end
82
+
83
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
84
+ task :rebuild => :app_env do
85
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
86
+ Rake::Task["thinking_sphinx:index"].invoke
87
+ Rake::Task["thinking_sphinx:start"].invoke
88
+ end
89
+
90
+ namespace :index do
91
+ task :delta => :app_env do
92
+ ThinkingSphinx.indexed_models.select { |model|
93
+ model.constantize.sphinx_indexes.any? { |index| index.delta? }
94
+ }.each do |model|
95
+ model.constantize.sphinx_indexes.select { |index|
96
+ index.delta? && index.delta_object.respond_to?(:delayed_index)
97
+ }.each { |index|
98
+ index.delta_object.delayed_index(index.model)
99
+ }
100
+ end
101
+ end
102
+ end
103
+
104
+ desc "Process stored delta index requests"
105
+ task :delayed_delta => :app_env do
106
+ require 'delayed/worker'
107
+
108
+ Delayed::Worker.new(
109
+ :min_priority => ENV['MIN_PRIORITY'],
110
+ :max_priority => ENV['MAX_PRIORITY']
111
+ ).start
112
+ end
113
+ end
114
+
115
+ namespace :ts do
116
+ desc "Output the current Thinking Sphinx version"
117
+ task :version => "thinking_sphinx:version"
118
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
119
+ task :run => "thinking_sphinx:running_start"
120
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
121
+ task :start => "thinking_sphinx:start"
122
+ desc "Stop Sphinx using Thinking Sphinx's settings"
123
+ task :stop => "thinking_sphinx:stop"
124
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
125
+ task :in => "thinking_sphinx:index"
126
+ namespace :in do
127
+ desc "Index Thinking Sphinx datetime delta indexes"
128
+ task :delta => "thinking_sphinx:index:delta"
129
+ end
130
+ task :index => "thinking_sphinx:index"
131
+ desc "Restart Sphinx"
132
+ task :restart => "thinking_sphinx:restart"
133
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
134
+ task :conf => "thinking_sphinx:configure"
135
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
136
+ task :config => "thinking_sphinx:configure"
137
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
138
+ task :rebuild => "thinking_sphinx:rebuild"
139
+ desc "Process stored delta index requests"
140
+ task :dd => "thinking_sphinx:delayed_delta"
141
+ end
142
+
143
+ def sphinx_pid
144
+ ThinkingSphinx.sphinx_pid
145
+ end
146
+
147
+ def sphinx_running?
148
+ ThinkingSphinx.sphinx_running?
149
+ end
150
+
151
+ # a fail-fast, hopefully helpful version of system
152
+ def system!(cmd)
153
+ unless system(cmd)
154
+ raise <<-SYSTEM_CALL_FAILED
155
+ The following command failed:
156
+ #{cmd}
157
+
158
+ This could be caused by a PATH issue in the environment of cron/passenger/etc. Your current PATH:
159
+ #{ENV['PATH']}
160
+ You can set the path to your indexer and searchd binaries using the bin_path property in config/sphinx.yml:
161
+ production:
162
+ bin_path: '/usr/local/bin'
163
+ SYSTEM_CALL_FAILED
164
+ end
165
+ end
@@ -0,0 +1,14 @@
1
+ require 'thinking_sphinx'
2
+ require 'action_controller/dispatcher'
3
+
4
+ ActionController::Dispatcher.to_prepare :thinking_sphinx do
5
+ # Force internationalisation to be loaded.
6
+ if Rails::VERSION::STRING.to_f > 2.2
7
+ I18n.backend.reload!
8
+ I18n.backend.available_locales
9
+ elsif Rails::VERSION::STRING.to_f > 2.1
10
+ I18n.backend.load_translations(*I18n.load_path)
11
+ end
12
+
13
+ ThinkingSphinx::Configuration.instance.load_models
14
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "ThinkingSphinx::ActiveRecord::Delta" do
4
+ it "should call the toggle_delta method after a save" do
5
+ @beta = Beta.new(:name => 'beta')
6
+ @beta.should_receive(:toggle_delta).and_return(true)
7
+
8
+ @beta.save
9
+ end
10
+
11
+ it "should call the toggle_delta method after a save!" do
12
+ @beta = Beta.new(:name => 'beta')
13
+ @beta.should_receive(:toggle_delta).and_return(true)
14
+
15
+ @beta.save!
16
+ end
17
+
18
+ describe "suspended_delta method" do
19
+ before :each do
20
+ ThinkingSphinx.deltas_enabled = true
21
+ Person.sphinx_indexes.first.delta_object.stub!(:` => "")
22
+ end
23
+
24
+ it "should execute the argument block with deltas disabled" do
25
+ ThinkingSphinx.should_receive(:deltas_enabled=).once.with(false)
26
+ ThinkingSphinx.should_receive(:deltas_enabled=).once.with(true)
27
+ lambda { Person.suspended_delta { raise 'i was called' } }.should(
28
+ raise_error(Exception)
29
+ )
30
+ end
31
+
32
+ it "should restore deltas_enabled to its original setting" do
33
+ ThinkingSphinx.deltas_enabled = false
34
+ ThinkingSphinx.should_receive(:deltas_enabled=).twice.with(false)
35
+ Person.suspended_delta { 'no-op' }
36
+ end
37
+
38
+ it "should restore deltas_enabled to its original setting even if there was an exception" do
39
+ ThinkingSphinx.deltas_enabled = false
40
+ ThinkingSphinx.should_receive(:deltas_enabled=).twice.with(false)
41
+ lambda { Person.suspended_delta { raise 'bad error' } }.should(
42
+ raise_error(Exception)
43
+ )
44
+ end
45
+
46
+ it "should reindex by default after the code block is run" do
47
+ Person.should_receive(:index_delta)
48
+ Person.suspended_delta { 'no-op' }
49
+ end
50
+
51
+ it "should not reindex after the code block if false is passed in" do
52
+ Person.should_not_receive(:index_delta)
53
+ Person.suspended_delta(false) { 'no-op' }
54
+ end
55
+ end
56
+
57
+ describe "toggle_delta method" do
58
+ it "should set the delta value to true" do
59
+ @person = Person.new
60
+
61
+ @person.delta.should be_false
62
+ @person.send(:toggle_delta)
63
+ @person.delta.should be_true
64
+ end
65
+ end
66
+
67
+ describe "index_delta method" do
68
+ before :each do
69
+ ThinkingSphinx::Configuration.stub!(:environment => "spec")
70
+ ThinkingSphinx.deltas_enabled = true
71
+ ThinkingSphinx.updates_enabled = true
72
+ ThinkingSphinx.stub!(:sphinx_running? => true)
73
+ Person.delta_object.stub!(:` => "", :toggled => true)
74
+
75
+ @person = Person.new
76
+ @person.stub!(
77
+ :in_both_indexes? => false,
78
+ :sphinx_document_id => 1
79
+ )
80
+
81
+ @client = Riddle::Client.new
82
+ @client.stub!(:update => true)
83
+ Riddle::Client.stub!(:new => @client)
84
+ end
85
+
86
+ it "shouldn't index if delta indexing is disabled" do
87
+ ThinkingSphinx.deltas_enabled = false
88
+ Person.sphinx_indexes.first.delta_object.should_not_receive(:`)
89
+ @client.should_not_receive(:update)
90
+
91
+ @person.send(:index_delta)
92
+ end
93
+
94
+ it "shouldn't index if index updating is disabled" do
95
+ ThinkingSphinx.updates_enabled = false
96
+ Person.sphinx_indexes.first.delta_object.should_not_receive(:`)
97
+
98
+ @person.send(:index_delta)
99
+ end
100
+
101
+ it "shouldn't index if the environment is 'test'" do
102
+ ThinkingSphinx.deltas_enabled = nil
103
+ ThinkingSphinx::Configuration.stub!(:environment => "test")
104
+ Person.sphinx_indexes.first.delta_object.should_not_receive(:`)
105
+
106
+ @person.send(:index_delta)
107
+ end
108
+
109
+ it "should call indexer for the delta index" do
110
+ Person.sphinx_indexes.first.delta_object.should_receive(:`).with(
111
+ "#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config #{ThinkingSphinx::Configuration.instance.config_file} --rotate person_delta"
112
+ )
113
+
114
+ @person.send(:index_delta)
115
+ end
116
+
117
+ it "shouldn't update the deleted attribute if not in the index" do
118
+ @client.should_not_receive(:update)
119
+
120
+ @person.send(:index_delta)
121
+ end
122
+
123
+ it "should update the deleted attribute if in the core index" do
124
+ @person.stub!(:in_both_indexes? => true)
125
+ @client.should_receive(:update)
126
+
127
+ @person.send(:index_delta)
128
+ end
129
+ end
130
+ end