sayso-thinking-sphinx 2.0.3.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/LICENCE +20 -0
  2. data/README.textile +251 -0
  3. data/VERSION +1 -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 +168 -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 +197 -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/fixtures/alphas.rb +10 -0
  38. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  39. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  40. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  41. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  42. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  43. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  44. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  45. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  46. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  47. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  48. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  49. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  50. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  51. data/features/thinking_sphinx/db/fixtures/posts.rb +6 -0
  52. data/features/thinking_sphinx/db/fixtures/robots.rb +14 -0
  53. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  54. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  55. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  56. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  57. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  58. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  59. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  60. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  61. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  62. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  63. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  64. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  65. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  66. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  67. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  68. data/features/thinking_sphinx/db/migrations/create_posts.rb +5 -0
  69. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  70. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  71. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  72. data/features/thinking_sphinx/models/alpha.rb +23 -0
  73. data/features/thinking_sphinx/models/andrew.rb +17 -0
  74. data/features/thinking_sphinx/models/animal.rb +5 -0
  75. data/features/thinking_sphinx/models/author.rb +3 -0
  76. data/features/thinking_sphinx/models/beta.rb +13 -0
  77. data/features/thinking_sphinx/models/box.rb +8 -0
  78. data/features/thinking_sphinx/models/cat.rb +3 -0
  79. data/features/thinking_sphinx/models/category.rb +4 -0
  80. data/features/thinking_sphinx/models/comment.rb +10 -0
  81. data/features/thinking_sphinx/models/developer.rb +20 -0
  82. data/features/thinking_sphinx/models/dog.rb +3 -0
  83. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  84. data/features/thinking_sphinx/models/fox.rb +5 -0
  85. data/features/thinking_sphinx/models/gamma.rb +5 -0
  86. data/features/thinking_sphinx/models/genre.rb +3 -0
  87. data/features/thinking_sphinx/models/medium.rb +5 -0
  88. data/features/thinking_sphinx/models/music.rb +8 -0
  89. data/features/thinking_sphinx/models/person.rb +24 -0
  90. data/features/thinking_sphinx/models/post.rb +21 -0
  91. data/features/thinking_sphinx/models/robot.rb +12 -0
  92. data/features/thinking_sphinx/models/tag.rb +3 -0
  93. data/features/thinking_sphinx/models/tagging.rb +4 -0
  94. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  95. data/lib/cucumber/thinking_sphinx/internal_world.rb +127 -0
  96. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  97. data/lib/thinking-sphinx.rb +1 -0
  98. data/lib/thinking_sphinx.rb +301 -0
  99. data/lib/thinking_sphinx/action_controller.rb +31 -0
  100. data/lib/thinking_sphinx/active_record.rb +352 -0
  101. data/lib/thinking_sphinx/active_record/attribute_updates.rb +52 -0
  102. data/lib/thinking_sphinx/active_record/delta.rb +92 -0
  103. data/lib/thinking_sphinx/active_record/has_many_association.rb +36 -0
  104. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  105. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  106. data/lib/thinking_sphinx/active_record/scopes.rb +93 -0
  107. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  108. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +58 -0
  109. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +153 -0
  110. data/lib/thinking_sphinx/association.rb +169 -0
  111. data/lib/thinking_sphinx/attribute.rb +389 -0
  112. data/lib/thinking_sphinx/auto_version.rb +38 -0
  113. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  114. data/lib/thinking_sphinx/class_facet.rb +16 -0
  115. data/lib/thinking_sphinx/configuration.rb +355 -0
  116. data/lib/thinking_sphinx/context.rb +76 -0
  117. data/lib/thinking_sphinx/core/string.rb +15 -0
  118. data/lib/thinking_sphinx/deltas.rb +28 -0
  119. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  120. data/lib/thinking_sphinx/deploy/capistrano.rb +101 -0
  121. data/lib/thinking_sphinx/excerpter.rb +23 -0
  122. data/lib/thinking_sphinx/facet.rb +127 -0
  123. data/lib/thinking_sphinx/facet_search.rb +166 -0
  124. data/lib/thinking_sphinx/field.rb +82 -0
  125. data/lib/thinking_sphinx/index.rb +157 -0
  126. data/lib/thinking_sphinx/index/builder.rb +312 -0
  127. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  128. data/lib/thinking_sphinx/join.rb +37 -0
  129. data/lib/thinking_sphinx/property.rb +185 -0
  130. data/lib/thinking_sphinx/railtie.rb +46 -0
  131. data/lib/thinking_sphinx/search.rb +950 -0
  132. data/lib/thinking_sphinx/search_methods.rb +439 -0
  133. data/lib/thinking_sphinx/source.rb +163 -0
  134. data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
  135. data/lib/thinking_sphinx/source/sql.rb +148 -0
  136. data/lib/thinking_sphinx/tasks.rb +139 -0
  137. data/lib/thinking_sphinx/test.rb +55 -0
  138. data/spec/thinking_sphinx/active_record/delta_spec.rb +128 -0
  139. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +72 -0
  140. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  141. data/spec/thinking_sphinx/active_record_spec.rb +576 -0
  142. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
  143. data/spec/thinking_sphinx/association_spec.rb +216 -0
  144. data/spec/thinking_sphinx/attribute_spec.rb +560 -0
  145. data/spec/thinking_sphinx/auto_version_spec.rb +63 -0
  146. data/spec/thinking_sphinx/configuration_spec.rb +288 -0
  147. data/spec/thinking_sphinx/context_spec.rb +128 -0
  148. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  149. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  150. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  151. data/spec/thinking_sphinx/facet_search_spec.rb +170 -0
  152. data/spec/thinking_sphinx/facet_spec.rb +359 -0
  153. data/spec/thinking_sphinx/field_spec.rb +127 -0
  154. data/spec/thinking_sphinx/index/builder_spec.rb +508 -0
  155. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  156. data/spec/thinking_sphinx/index_spec.rb +183 -0
  157. data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
  158. data/spec/thinking_sphinx/search_spec.rb +1387 -0
  159. data/spec/thinking_sphinx/source_spec.rb +253 -0
  160. data/spec/thinking_sphinx/test_spec.rb +20 -0
  161. data/spec/thinking_sphinx_spec.rb +203 -0
  162. data/tasks/distribution.rb +33 -0
  163. data/tasks/testing.rb +80 -0
  164. metadata +509 -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, nil,
6
+ @model.primary_key_for_sphinx.to_sym
7
+ add_internal_attribute :class_crc, :integer, crc_column, true
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(:descendants).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,148 @@
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
7
+ # the 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
+ relation = @model.unscoped
17
+ pre_select = 'SQL_NO_CACHE ' if adapter.sphinx_identifier == "mysql"
18
+ relation = relation.select(
19
+ pre_select.to_s + sql_select_clause(options[:offset])
20
+ )
21
+
22
+ all_associations.each do |assoc|
23
+ relation = relation.joins(assoc.arel_join)
24
+ end
25
+
26
+ relation = relation.where(sql_where_clause(options))
27
+ relation = relation.group(sql_group_clause)
28
+ relation = relation.order('NULL') if adapter.sphinx_identifier == "mysql"
29
+ relation.to_sql
30
+ end
31
+
32
+ # Simple helper method for the query range SQL - which is a statement that
33
+ # returns minimum and maximum id values. These can be filtered by delta -
34
+ # so pass in :delta => true to get the delta version of the SQL.
35
+ #
36
+ def to_sql_query_range(options={})
37
+ return nil if @index.options[:disable_range]
38
+
39
+ min_statement = adapter.convert_nulls(
40
+ "MIN(#{quote_column(@model.primary_key_for_sphinx)})", 1
41
+ )
42
+ max_statement = adapter.convert_nulls(
43
+ "MAX(#{quote_column(@model.primary_key_for_sphinx)})", 1
44
+ )
45
+
46
+ sql = "SELECT #{min_statement}, #{max_statement} " +
47
+ "FROM #{@model.quoted_table_name} "
48
+ if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
49
+ sql << "WHERE #{@index.delta_object.clause(@model, options[:delta])}"
50
+ end
51
+
52
+ sql
53
+ end
54
+
55
+ # Simple helper method for the query info SQL - which is a statement that
56
+ # returns the single row for a corresponding id.
57
+ #
58
+ def to_sql_query_info(offset)
59
+ "SELECT * FROM #{@model.quoted_table_name} WHERE " +
60
+ "#{quote_column(@model.primary_key_for_sphinx)} = (($id - #{offset}) / #{ThinkingSphinx.context.indexed_models.size})"
61
+ end
62
+
63
+ def sql_select_clause(offset)
64
+ unique_id_expr = ThinkingSphinx.unique_id_expression(offset)
65
+
66
+ (
67
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} #{unique_id_expr} AS #{quote_column(@model.primary_key_for_sphinx)} "] +
68
+ @fields.collect { |field| field.to_select_sql } +
69
+ @attributes.collect { |attribute| attribute.to_select_sql }
70
+ ).compact.join(", ")
71
+ end
72
+
73
+ def sql_where_clause(options)
74
+ logic = []
75
+ logic += [
76
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} >= $start",
77
+ "#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)} <= $end"
78
+ ] unless @index.options[:disable_range]
79
+
80
+ if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
81
+ logic << "#{@index.delta_object.clause(@model, options[:delta])}"
82
+ end
83
+
84
+ logic += (@conditions || [])
85
+ logic.join(' AND ')
86
+ end
87
+
88
+ def sql_group_clause
89
+ internal_groupings = []
90
+ if @model.column_names.include?(@model.inheritance_column)
91
+ internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
92
+ end
93
+
94
+ (
95
+ ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key_for_sphinx)}"] +
96
+ @fields.collect { |field| field.to_group_sql }.compact +
97
+ @attributes.collect { |attribute| attribute.to_group_sql }.compact +
98
+ @groupings + internal_groupings
99
+ ).join(", ")
100
+ end
101
+
102
+ def sql_query_pre_for_core
103
+ if self.delta? && !@index.delta_object.reset_query(@model).blank?
104
+ [@index.delta_object.reset_query(@model)]
105
+ else
106
+ []
107
+ end
108
+ end
109
+
110
+ def sql_query_pre_for_delta
111
+ [""]
112
+ end
113
+
114
+ def quote_column(column)
115
+ @model.connection.quote_column_name(column)
116
+ end
117
+
118
+ def crc_column
119
+ if @model.table_exists? &&
120
+ @model.column_names.include?(@model.inheritance_column)
121
+
122
+ types = types_to_crcs
123
+ return @model.to_crc32.to_s if types.empty?
124
+
125
+ adapter.case(adapter.convert_nulls(
126
+ adapter.quote_with_table(@model.inheritance_column)),
127
+ types_to_crcs, @model.to_crc32)
128
+ else
129
+ @model.to_crc32.to_s
130
+ end
131
+ end
132
+
133
+ def type_values
134
+ @model.connection.select_values <<-SQL
135
+ SELECT DISTINCT #{@model.inheritance_column}
136
+ FROM #{@model.table_name}
137
+ SQL
138
+ end
139
+
140
+ def types_to_crcs
141
+ type_values.compact.inject({}) { |hash, type|
142
+ hash[type] = type.to_crc32
143
+ hash
144
+ }
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,139 @@
1
+ require 'fileutils'
2
+ require 'timeout'
3
+
4
+ namespace :thinking_sphinx do
5
+ task :app_env do
6
+ if defined?(Rails)
7
+ Rails.application.require_environment!
8
+ Rails.configuration.cache_classes = false
9
+ elsif defined?(Merb)
10
+ Rake::Task[:merb_env].invoke
11
+ elsif defined?(Sinatra)
12
+ Sinatra::Application.environment = ENV['RACK_ENV']
13
+ end
14
+ end
15
+
16
+ desc "Output the current Thinking Sphinx version"
17
+ task :version => :app_env do
18
+ puts "Thinking Sphinx v" + ThinkingSphinx.version
19
+ end
20
+
21
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
22
+ task :running_start => :app_env do
23
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
24
+ Rake::Task["thinking_sphinx:start"].invoke
25
+ end
26
+
27
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
28
+ task :start => :app_env do
29
+ config = ThinkingSphinx::Configuration.instance
30
+
31
+ FileUtils.mkdir_p config.searchd_file_path
32
+ puts("searchd is already running.") and return if sphinx_running?
33
+
34
+ Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
35
+
36
+ old_env_tz = ENV['TZ']
37
+ ENV['TZ'] = nil
38
+ config.controller.start
39
+ ENV['TZ'] = old_env_tz
40
+
41
+ if sphinx_running?
42
+ puts "Started successfully (pid #{sphinx_pid})."
43
+ else
44
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
45
+ end
46
+ end
47
+
48
+ desc "Stop Sphinx using Thinking Sphinx's settings"
49
+ task :stop => :app_env do
50
+ unless sphinx_running?
51
+ puts "searchd is not running"
52
+ else
53
+ config = ThinkingSphinx::Configuration.instance
54
+ pid = sphinx_pid
55
+ config.controller.stop
56
+ begin
57
+ # Ensure searchd is stopped, but don't try too hard
58
+ Timeout.timeout(5) do
59
+ while sphinx_running?
60
+ sleep(0.01)
61
+ end
62
+ end
63
+ rescue Timeout::Error
64
+ puts "sending KILL signal to pid #{pid}."
65
+ Process.kill("KILL", pid.to_i)
66
+ ensure
67
+ puts "Stopped search daemon (pid #{pid})."
68
+ end
69
+ end
70
+ end
71
+
72
+ desc "Restart Sphinx"
73
+ task :restart => [:app_env, :stop, :start]
74
+
75
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
76
+ task :configure => :app_env do
77
+ config = ThinkingSphinx::Configuration.instance
78
+ puts "Generating Configuration to #{config.config_file}"
79
+ config.build
80
+ end
81
+
82
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
83
+ task :index => :app_env do
84
+ config = ThinkingSphinx::Configuration.instance
85
+ unless ENV["INDEX_ONLY"] == "true"
86
+ puts "Generating Configuration to #{config.config_file}"
87
+ config.build
88
+ end
89
+
90
+ FileUtils.mkdir_p config.searchd_file_path
91
+ config.controller.index :verbose => true
92
+ end
93
+
94
+ desc "Reindex Sphinx without regenerating the configuration file"
95
+ task :reindex => :app_env do
96
+ config = ThinkingSphinx::Configuration.instance
97
+ FileUtils.mkdir_p config.searchd_file_path
98
+ puts config.controller.index
99
+ end
100
+
101
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
102
+ task :rebuild => :app_env do
103
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
104
+ Rake::Task["thinking_sphinx:index"].invoke
105
+ Rake::Task["thinking_sphinx:start"].invoke
106
+ end
107
+ end
108
+
109
+ namespace :ts do
110
+ desc "Output the current Thinking Sphinx version"
111
+ task :version => "thinking_sphinx:version"
112
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
113
+ task :run => "thinking_sphinx:running_start"
114
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
115
+ task :start => "thinking_sphinx:start"
116
+ desc "Stop Sphinx using Thinking Sphinx's settings"
117
+ task :stop => "thinking_sphinx:stop"
118
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
119
+ task :in => "thinking_sphinx:index"
120
+ task :index => "thinking_sphinx:index"
121
+ desc "Reindex Sphinx without regenerating the configuration file"
122
+ task :reindex => "thinking_sphinx:reindex"
123
+ desc "Restart Sphinx"
124
+ task :restart => "thinking_sphinx:restart"
125
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
126
+ task :conf => "thinking_sphinx:configure"
127
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
128
+ task :config => "thinking_sphinx:configure"
129
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
130
+ task :rebuild => "thinking_sphinx:rebuild"
131
+ end
132
+
133
+ def sphinx_pid
134
+ ThinkingSphinx.sphinx_pid
135
+ end
136
+
137
+ def sphinx_running?
138
+ ThinkingSphinx.sphinx_running?
139
+ end
@@ -0,0 +1,55 @@
1
+ class ThinkingSphinx::Test
2
+ def self.init(suppress_delta_output = true)
3
+ set_flags suppress_delta_output
4
+ create_indexes_folder
5
+ end
6
+
7
+ def self.start
8
+ config.build
9
+ config.controller.index
10
+ config.controller.start
11
+ end
12
+
13
+ def self.start_with_autostop
14
+ autostop
15
+ start
16
+ end
17
+
18
+ def self.stop
19
+ config.controller.stop
20
+ sleep(0.5) # Ensure Sphinx has shut down completely
21
+ end
22
+
23
+ def self.autostop
24
+ Kernel.at_exit do
25
+ ThinkingSphinx::Test.stop
26
+ end
27
+ end
28
+
29
+ def self.run(&block)
30
+ begin
31
+ start
32
+ yield
33
+ ensure
34
+ stop
35
+ end
36
+ end
37
+
38
+ def self.config
39
+ @config ||= ::ThinkingSphinx::Configuration.instance
40
+ end
41
+
42
+ def self.index(*indexes)
43
+ config.controller.index *indexes
44
+ end
45
+
46
+ def self.set_flags(suppress_delta_output)
47
+ ::ThinkingSphinx.deltas_enabled = true
48
+ ::ThinkingSphinx.updates_enabled = true
49
+ ::ThinkingSphinx.suppress_delta_output = suppress_delta_output
50
+ end
51
+
52
+ def self.create_indexes_folder
53
+ FileUtils.mkdir_p config.searchd_file_path
54
+ end
55
+ end
@@ -0,0 +1,128 @@
1
+ require '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_suspended = false
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_suspended=).once.with(true)
26
+ ThinkingSphinx.should_receive(:deltas_suspended=).once.with(false)
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_suspended = true
34
+ ThinkingSphinx.should_receive(:deltas_suspended=).twice.with(true)
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_suspended = true
40
+ ThinkingSphinx.should_receive(:deltas_suspended=).twice.with(true)
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_objects.first.stub!(:` => "", :toggled => true)
74
+
75
+ @person = Person.new
76
+ Person.stub!(:search_for_id => false)
77
+ @person.stub!(:sphinx_document_id => 1)
78
+
79
+ @client = Riddle::Client.new
80
+ @client.stub!(:update => true)
81
+ ThinkingSphinx::Configuration.instance.stub!(:client => @client)
82
+ end
83
+
84
+ it "shouldn't index if delta indexing is disabled" do
85
+ ThinkingSphinx.deltas_enabled = false
86
+ Person.sphinx_indexes.first.delta_object.should_not_receive(:`)
87
+ @client.should_not_receive(:update)
88
+
89
+ @person.send(:index_delta)
90
+ end
91
+
92
+ it "shouldn't index if index updating is disabled" do
93
+ ThinkingSphinx.updates_enabled = false
94
+ Person.sphinx_indexes.first.delta_object.should_not_receive(:`)
95
+
96
+ @person.send(:index_delta)
97
+ end
98
+
99
+ it "shouldn't index if the environment is 'test'" do
100
+ ThinkingSphinx.deltas_enabled = nil
101
+ ThinkingSphinx::Configuration.stub!(:environment => "test")
102
+ Person.sphinx_indexes.first.delta_object.should_not_receive(:`)
103
+
104
+ @person.send(:index_delta)
105
+ end
106
+
107
+ it "should call indexer for the delta index" do
108
+ Person.sphinx_indexes.first.delta_object.should_receive(:`).with(
109
+ "#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config \"#{ThinkingSphinx::Configuration.instance.config_file}\" --rotate person_delta"
110
+ )
111
+
112
+ @person.send(:index_delta)
113
+ end
114
+
115
+ it "shouldn't update the deleted attribute if not in the index" do
116
+ @client.should_not_receive(:update)
117
+
118
+ @person.send(:index_delta)
119
+ end
120
+
121
+ it "should update the deleted attribute if in the core index" do
122
+ Person.stub!(:search_for_id => true)
123
+ @client.should_receive(:update)
124
+
125
+ @person.send(:index_delta)
126
+ end
127
+ end
128
+ end