thinking-sphinx 3.1.4 → 6.0.0

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 (347) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +99 -0
  3. data/.github/actions/test/action.yml +46 -0
  4. data/.github/workflows/ci.yml +75 -0
  5. data/.gitignore +7 -2
  6. data/.travis.yml +31 -16
  7. data/Appraisals +68 -12
  8. data/CHANGELOG.markdown +782 -0
  9. data/Gemfile +13 -4
  10. data/Procfile.support +2 -0
  11. data/README.textile +41 -27
  12. data/Rakefile +2 -0
  13. data/bin/console +15 -0
  14. data/bin/loadsphinx +99 -0
  15. data/bin/testmatrix +48 -0
  16. data/lib/thinking/sphinx.rb +2 -0
  17. data/lib/thinking-sphinx.rb +2 -0
  18. data/lib/thinking_sphinx/active_record/association.rb +2 -0
  19. data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +3 -1
  20. data/lib/thinking_sphinx/active_record/association_proxy/attribute_matcher.rb +2 -0
  21. data/lib/thinking_sphinx/active_record/association_proxy.rb +3 -2
  22. data/lib/thinking_sphinx/active_record/attribute/sphinx_presenter.rb +3 -1
  23. data/lib/thinking_sphinx/active_record/attribute/type.rb +20 -4
  24. data/lib/thinking_sphinx/active_record/attribute/values.rb +2 -0
  25. data/lib/thinking_sphinx/active_record/attribute.rb +2 -0
  26. data/lib/thinking_sphinx/active_record/base.rb +43 -11
  27. data/lib/thinking_sphinx/active_record/callbacks/association_delta_callbacks.rb +21 -0
  28. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +15 -9
  29. data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +20 -5
  30. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +18 -4
  31. data/lib/thinking_sphinx/active_record/column.rb +2 -0
  32. data/lib/thinking_sphinx/active_record/column_sql_presenter.rb +7 -1
  33. data/lib/thinking_sphinx/active_record/database_adapters/abstract_adapter.rb +2 -0
  34. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +9 -1
  35. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +2 -0
  36. data/lib/thinking_sphinx/active_record/database_adapters.rb +3 -1
  37. data/lib/thinking_sphinx/active_record/depolymorph/association_reflection.rb +32 -0
  38. data/lib/thinking_sphinx/active_record/depolymorph/base_reflection.rb +32 -0
  39. data/lib/thinking_sphinx/active_record/depolymorph/conditions_reflection.rb +40 -0
  40. data/lib/thinking_sphinx/active_record/depolymorph/overridden_reflection.rb +50 -0
  41. data/lib/thinking_sphinx/active_record/depolymorph/scoped_reflection.rb +26 -0
  42. data/lib/thinking_sphinx/active_record/field.rb +2 -0
  43. data/lib/thinking_sphinx/active_record/filter_reflection.rb +13 -71
  44. data/lib/thinking_sphinx/active_record/index.rb +7 -5
  45. data/lib/thinking_sphinx/active_record/interpreter.rb +6 -4
  46. data/lib/thinking_sphinx/active_record/join_association.rb +5 -1
  47. data/lib/thinking_sphinx/active_record/log_subscriber.rb +22 -3
  48. data/lib/thinking_sphinx/active_record/polymorpher.rb +2 -0
  49. data/lib/thinking_sphinx/active_record/property.rb +2 -0
  50. data/lib/thinking_sphinx/active_record/property_query.rb +4 -1
  51. data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +2 -0
  52. data/lib/thinking_sphinx/active_record/simple_many_query.rb +2 -0
  53. data/lib/thinking_sphinx/active_record/source_joins.rb +68 -0
  54. data/lib/thinking_sphinx/active_record/sql_builder/clause_builder.rb +2 -0
  55. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +13 -0
  56. data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +2 -0
  57. data/lib/thinking_sphinx/active_record/sql_builder.rb +5 -18
  58. data/lib/thinking_sphinx/active_record/sql_source/template.rb +5 -3
  59. data/lib/thinking_sphinx/active_record/sql_source.rb +35 -9
  60. data/lib/thinking_sphinx/active_record.rb +14 -1
  61. data/lib/thinking_sphinx/attribute_types.rb +72 -0
  62. data/lib/thinking_sphinx/batched_search.rb +2 -0
  63. data/lib/thinking_sphinx/callbacks/appender.rb +63 -0
  64. data/lib/thinking_sphinx/callbacks.rb +29 -0
  65. data/lib/thinking_sphinx/capistrano/v2.rb +2 -0
  66. data/lib/thinking_sphinx/capistrano/v3.rb +2 -0
  67. data/lib/thinking_sphinx/capistrano.rb +2 -0
  68. data/lib/thinking_sphinx/commander.rb +27 -0
  69. data/lib/thinking_sphinx/commands/base.rb +53 -0
  70. data/lib/thinking_sphinx/commands/clear_real_time.rb +22 -0
  71. data/lib/thinking_sphinx/commands/clear_sql.rb +18 -0
  72. data/lib/thinking_sphinx/commands/configure.rb +15 -0
  73. data/lib/thinking_sphinx/commands/index_real_time.rb +15 -0
  74. data/lib/thinking_sphinx/commands/index_sql.rb +25 -0
  75. data/lib/thinking_sphinx/commands/merge.rb +27 -0
  76. data/lib/thinking_sphinx/commands/merge_and_update.rb +57 -0
  77. data/lib/thinking_sphinx/commands/prepare.rb +15 -0
  78. data/lib/thinking_sphinx/commands/rotate.rb +13 -0
  79. data/lib/thinking_sphinx/commands/running.rb +15 -0
  80. data/lib/thinking_sphinx/commands/start_attached.rb +22 -0
  81. data/lib/thinking_sphinx/commands/start_detached.rb +21 -0
  82. data/lib/thinking_sphinx/commands/stop.rb +24 -0
  83. data/lib/thinking_sphinx/commands.rb +20 -0
  84. data/lib/thinking_sphinx/configuration/consistent_ids.rb +2 -0
  85. data/lib/thinking_sphinx/configuration/defaults.rb +2 -0
  86. data/lib/thinking_sphinx/configuration/distributed_indices.rb +3 -1
  87. data/lib/thinking_sphinx/configuration/duplicate_names.rb +36 -0
  88. data/lib/thinking_sphinx/configuration/minimum_fields.rb +17 -12
  89. data/lib/thinking_sphinx/configuration.rb +90 -74
  90. data/lib/thinking_sphinx/connection/client.rb +74 -0
  91. data/lib/thinking_sphinx/connection/jruby.rb +58 -0
  92. data/lib/thinking_sphinx/connection/mri.rb +26 -0
  93. data/lib/thinking_sphinx/connection.rb +13 -114
  94. data/lib/thinking_sphinx/core/field.rb +2 -0
  95. data/lib/thinking_sphinx/core/index.rb +22 -2
  96. data/lib/thinking_sphinx/core/interpreter.rb +2 -0
  97. data/lib/thinking_sphinx/core/property.rb +2 -0
  98. data/lib/thinking_sphinx/core/settings.rb +2 -0
  99. data/lib/thinking_sphinx/core.rb +2 -0
  100. data/lib/thinking_sphinx/deletion.rb +33 -13
  101. data/lib/thinking_sphinx/deltas/default_delta.rb +3 -1
  102. data/lib/thinking_sphinx/deltas/delete_job.rb +16 -4
  103. data/lib/thinking_sphinx/deltas/index_job.rb +14 -2
  104. data/lib/thinking_sphinx/deltas.rb +2 -0
  105. data/lib/thinking_sphinx/distributed/index.rb +25 -3
  106. data/lib/thinking_sphinx/distributed.rb +2 -0
  107. data/lib/thinking_sphinx/errors.rb +47 -3
  108. data/lib/thinking_sphinx/excerpter.rb +2 -0
  109. data/lib/thinking_sphinx/facet.rb +4 -3
  110. data/lib/thinking_sphinx/facet_search.rb +4 -2
  111. data/lib/thinking_sphinx/float_formatter.rb +2 -0
  112. data/lib/thinking_sphinx/frameworks/plain.rb +2 -0
  113. data/lib/thinking_sphinx/frameworks/rails.rb +2 -0
  114. data/lib/thinking_sphinx/frameworks.rb +2 -0
  115. data/lib/thinking_sphinx/guard/file.rb +3 -1
  116. data/lib/thinking_sphinx/guard/files.rb +2 -0
  117. data/lib/thinking_sphinx/guard/none.rb +7 -0
  118. data/lib/thinking_sphinx/guard.rb +3 -0
  119. data/lib/thinking_sphinx/hooks/guard_presence.rb +34 -0
  120. data/lib/thinking_sphinx/index.rb +2 -0
  121. data/lib/thinking_sphinx/index_set.rb +27 -4
  122. data/lib/thinking_sphinx/indexing_strategies/all_at_once.rb +9 -0
  123. data/lib/thinking_sphinx/indexing_strategies/one_at_a_time.rb +16 -0
  124. data/lib/thinking_sphinx/interfaces/base.rb +13 -0
  125. data/lib/thinking_sphinx/interfaces/daemon.rb +27 -0
  126. data/lib/thinking_sphinx/interfaces/real_time.rb +46 -0
  127. data/lib/thinking_sphinx/interfaces/sql.rb +53 -0
  128. data/lib/thinking_sphinx/interfaces.rb +10 -0
  129. data/lib/thinking_sphinx/logger.rb +2 -0
  130. data/lib/thinking_sphinx/masks/group_enumerators_mask.rb +6 -4
  131. data/lib/thinking_sphinx/masks/pagination_mask.rb +2 -0
  132. data/lib/thinking_sphinx/masks/scopes_mask.rb +8 -0
  133. data/lib/thinking_sphinx/masks/weight_enumerator_mask.rb +3 -1
  134. data/lib/thinking_sphinx/masks.rb +2 -0
  135. data/lib/thinking_sphinx/middlewares/active_record_translator.rb +32 -9
  136. data/lib/thinking_sphinx/middlewares/geographer.rb +2 -0
  137. data/lib/thinking_sphinx/middlewares/glazier.rb +14 -1
  138. data/lib/thinking_sphinx/middlewares/ids_only.rb +2 -0
  139. data/lib/thinking_sphinx/middlewares/inquirer.rb +3 -1
  140. data/lib/thinking_sphinx/middlewares/middleware.rb +2 -0
  141. data/lib/thinking_sphinx/middlewares/sphinxql.rb +18 -16
  142. data/lib/thinking_sphinx/middlewares/stale_id_checker.rb +3 -1
  143. data/lib/thinking_sphinx/middlewares/stale_id_filter.rb +4 -2
  144. data/lib/thinking_sphinx/middlewares/valid_options.rb +25 -0
  145. data/lib/thinking_sphinx/middlewares.rb +7 -3
  146. data/lib/thinking_sphinx/panes/attributes_pane.rb +2 -0
  147. data/lib/thinking_sphinx/panes/distance_pane.rb +2 -0
  148. data/lib/thinking_sphinx/panes/excerpts_pane.rb +2 -0
  149. data/lib/thinking_sphinx/panes/weight_pane.rb +3 -1
  150. data/lib/thinking_sphinx/panes.rb +2 -0
  151. data/lib/thinking_sphinx/processor.rb +71 -0
  152. data/lib/thinking_sphinx/query.rb +2 -0
  153. data/lib/thinking_sphinx/railtie.rb +29 -2
  154. data/lib/thinking_sphinx/rake_interface.rb +14 -69
  155. data/lib/thinking_sphinx/real_time/attribute.rb +9 -1
  156. data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +17 -7
  157. data/lib/thinking_sphinx/real_time/field.rb +2 -0
  158. data/lib/thinking_sphinx/real_time/index/template.rb +19 -1
  159. data/lib/thinking_sphinx/real_time/index.rb +11 -5
  160. data/lib/thinking_sphinx/real_time/interpreter.rb +10 -6
  161. data/lib/thinking_sphinx/real_time/populator.rb +12 -14
  162. data/lib/thinking_sphinx/real_time/processor.rb +36 -0
  163. data/lib/thinking_sphinx/real_time/property.rb +3 -5
  164. data/lib/thinking_sphinx/real_time/transcribe_instance.rb +38 -0
  165. data/lib/thinking_sphinx/real_time/transcriber.rb +63 -15
  166. data/lib/thinking_sphinx/real_time/translator.rb +39 -0
  167. data/lib/thinking_sphinx/real_time.rb +22 -1
  168. data/lib/thinking_sphinx/scopes.rb +6 -0
  169. data/lib/thinking_sphinx/search/batch_inquirer.rb +2 -0
  170. data/lib/thinking_sphinx/search/context.rb +11 -0
  171. data/lib/thinking_sphinx/search/glaze.rb +2 -0
  172. data/lib/thinking_sphinx/search/merger.rb +2 -0
  173. data/lib/thinking_sphinx/search/query.rb +9 -1
  174. data/lib/thinking_sphinx/search/stale_ids_exception.rb +7 -3
  175. data/lib/thinking_sphinx/search.rb +30 -2
  176. data/lib/thinking_sphinx/settings.rb +128 -0
  177. data/lib/thinking_sphinx/sinatra.rb +3 -1
  178. data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +19 -5
  179. data/lib/thinking_sphinx/tasks.rb +55 -28
  180. data/lib/thinking_sphinx/test.rb +3 -1
  181. data/lib/thinking_sphinx/utf8.rb +2 -0
  182. data/lib/thinking_sphinx/wildcard.rb +3 -1
  183. data/lib/thinking_sphinx/with_output.rb +13 -0
  184. data/lib/thinking_sphinx.rb +35 -4
  185. data/spec/acceptance/association_scoping_spec.rb +7 -5
  186. data/spec/acceptance/attribute_access_spec.rb +29 -12
  187. data/spec/acceptance/attribute_updates_spec.rb +5 -3
  188. data/spec/acceptance/batch_searching_spec.rb +6 -4
  189. data/spec/acceptance/big_integers_spec.rb +9 -7
  190. data/spec/acceptance/excerpts_spec.rb +11 -10
  191. data/spec/acceptance/facets_spec.rb +29 -16
  192. data/spec/acceptance/geosearching_spec.rb +22 -10
  193. data/spec/acceptance/grouping_by_attributes_spec.rb +30 -28
  194. data/spec/acceptance/index_options_spec.rb +18 -16
  195. data/spec/acceptance/indexing_spec.rb +5 -3
  196. data/spec/acceptance/merging_spec.rb +90 -0
  197. data/spec/acceptance/paginating_search_results_spec.rb +21 -3
  198. data/spec/acceptance/real_time_updates_spec.rb +103 -5
  199. data/spec/acceptance/remove_deleted_records_spec.rb +64 -8
  200. data/spec/acceptance/search_counts_spec.rb +4 -2
  201. data/spec/acceptance/search_for_just_ids_spec.rb +4 -2
  202. data/spec/acceptance/searching_across_models_spec.rb +15 -6
  203. data/spec/acceptance/searching_across_schemas_spec.rb +12 -10
  204. data/spec/acceptance/searching_on_fields_spec.rb +11 -9
  205. data/spec/acceptance/searching_with_filters_spec.rb +35 -20
  206. data/spec/acceptance/searching_with_sti_spec.rb +11 -9
  207. data/spec/acceptance/searching_within_a_model_spec.rb +44 -17
  208. data/spec/acceptance/sorting_search_results_spec.rb +21 -19
  209. data/spec/acceptance/spec_helper.rb +2 -13
  210. data/spec/acceptance/specifying_sql_spec.rb +118 -62
  211. data/spec/acceptance/sphinx_scopes_spec.rb +30 -21
  212. data/spec/acceptance/sql_deltas_spec.rb +35 -9
  213. data/spec/acceptance/support/database_cleaner.rb +3 -1
  214. data/spec/acceptance/support/sphinx_controller.rb +25 -9
  215. data/spec/acceptance/support/sphinx_helpers.rb +17 -2
  216. data/spec/acceptance/suspended_deltas_spec.rb +14 -12
  217. data/spec/fixtures/database.yml +1 -1
  218. data/spec/internal/app/indices/admin_person_index.rb +6 -0
  219. data/spec/internal/app/indices/album_index.rb +9 -0
  220. data/spec/internal/app/indices/animal_index.rb +2 -0
  221. data/spec/internal/app/indices/article_index.rb +10 -3
  222. data/spec/internal/app/indices/bird_index.rb +2 -0
  223. data/spec/internal/app/indices/book_index.rb +4 -1
  224. data/spec/internal/app/indices/car_index.rb +2 -0
  225. data/spec/internal/app/indices/city_index.rb +2 -0
  226. data/spec/internal/app/indices/colour_index.rb +7 -0
  227. data/spec/internal/app/indices/product_index.rb +3 -2
  228. data/spec/internal/app/indices/tee_index.rb +3 -1
  229. data/spec/internal/app/indices/user_index.rb +2 -0
  230. data/spec/internal/app/models/admin/person.rb +6 -0
  231. data/spec/internal/app/models/album.rb +25 -0
  232. data/spec/internal/app/models/animal.rb +3 -0
  233. data/spec/internal/app/models/article.rb +4 -0
  234. data/spec/internal/app/models/bird.rb +3 -0
  235. data/spec/internal/app/models/book.rb +9 -5
  236. data/spec/internal/app/models/car.rb +3 -1
  237. data/spec/internal/app/models/categorisation.rb +10 -1
  238. data/spec/internal/app/models/category.rb +2 -0
  239. data/spec/internal/app/models/city.rb +4 -0
  240. data/spec/internal/app/models/colour.rb +4 -0
  241. data/spec/internal/app/models/event.rb +2 -0
  242. data/spec/internal/app/models/flightless_bird.rb +2 -0
  243. data/spec/internal/app/models/genre.rb +2 -0
  244. data/spec/internal/app/models/hardcover.rb +2 -0
  245. data/spec/internal/app/models/mammal.rb +2 -0
  246. data/spec/internal/app/models/manufacturer.rb +2 -0
  247. data/spec/internal/app/models/product.rb +3 -1
  248. data/spec/internal/app/models/tag.rb +2 -0
  249. data/spec/internal/app/models/tagging.rb +2 -0
  250. data/spec/internal/app/models/tee.rb +7 -0
  251. data/spec/internal/app/models/tweet.rb +2 -0
  252. data/spec/internal/app/models/user.rb +4 -0
  253. data/spec/internal/config/database.yml +12 -1
  254. data/spec/internal/db/schema.rb +13 -1
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/json_column.rb +35 -0
  257. data/spec/support/multi_schema.rb +5 -1
  258. data/spec/support/mysql.rb +25 -0
  259. data/spec/support/sphinx_yaml_helpers.rb +8 -1
  260. data/spec/thinking_sphinx/active_record/association_spec.rb +3 -1
  261. data/spec/thinking_sphinx/active_record/attribute/type_spec.rb +43 -38
  262. data/spec/thinking_sphinx/active_record/base_spec.rb +31 -29
  263. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +81 -10
  264. data/spec/thinking_sphinx/active_record/callbacks/delta_callbacks_spec.rb +56 -29
  265. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +27 -10
  266. data/spec/thinking_sphinx/active_record/column_spec.rb +15 -13
  267. data/spec/thinking_sphinx/active_record/column_sql_presenter_spec.rb +39 -0
  268. data/spec/thinking_sphinx/active_record/database_adapters/abstract_adapter_spec.rb +7 -5
  269. data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +26 -12
  270. data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +15 -13
  271. data/spec/thinking_sphinx/active_record/database_adapters_spec.rb +50 -48
  272. data/spec/thinking_sphinx/active_record/field_spec.rb +9 -7
  273. data/spec/thinking_sphinx/active_record/filter_reflection_spec.rb +113 -80
  274. data/spec/thinking_sphinx/active_record/index_spec.rb +41 -37
  275. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +62 -59
  276. data/spec/thinking_sphinx/active_record/polymorpher_spec.rb +16 -14
  277. data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +69 -67
  278. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +146 -154
  279. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +185 -109
  280. data/spec/thinking_sphinx/attribute_types_spec.rb +52 -0
  281. data/spec/thinking_sphinx/commands/clear_real_time_spec.rb +46 -0
  282. data/spec/thinking_sphinx/commands/clear_sql_spec.rb +52 -0
  283. data/spec/thinking_sphinx/commands/configure_spec.rb +31 -0
  284. data/spec/thinking_sphinx/commands/index_real_time_spec.rb +33 -0
  285. data/spec/thinking_sphinx/commands/index_sql_spec.rb +86 -0
  286. data/spec/thinking_sphinx/commands/merge_and_update_spec.rb +106 -0
  287. data/spec/thinking_sphinx/commands/merge_spec.rb +48 -0
  288. data/spec/thinking_sphinx/commands/prepare_spec.rb +31 -0
  289. data/spec/thinking_sphinx/commands/running_spec.rb +30 -0
  290. data/spec/thinking_sphinx/commands/start_detached_spec.rb +67 -0
  291. data/spec/thinking_sphinx/commands/stop_spec.rb +63 -0
  292. data/spec/thinking_sphinx/configuration/minimum_fields_spec.rb +60 -0
  293. data/spec/thinking_sphinx/configuration_spec.rb +247 -94
  294. data/spec/thinking_sphinx/connection/mri_spec.rb +49 -0
  295. data/spec/thinking_sphinx/connection_spec.rb +15 -13
  296. data/spec/thinking_sphinx/deletion_spec.rb +17 -17
  297. data/spec/thinking_sphinx/deltas/default_delta_spec.rb +28 -21
  298. data/spec/thinking_sphinx/deltas_spec.rb +14 -12
  299. data/spec/thinking_sphinx/errors_spec.rb +59 -27
  300. data/spec/thinking_sphinx/excerpter_spec.rb +12 -9
  301. data/spec/thinking_sphinx/facet_search_spec.rb +24 -22
  302. data/spec/thinking_sphinx/hooks/guard_presence_spec.rb +30 -0
  303. data/spec/thinking_sphinx/index_set_spec.rb +59 -18
  304. data/spec/thinking_sphinx/index_spec.rb +28 -26
  305. data/spec/thinking_sphinx/interfaces/daemon_spec.rb +60 -0
  306. data/spec/thinking_sphinx/interfaces/real_time_spec.rb +109 -0
  307. data/spec/thinking_sphinx/interfaces/sql_spec.rb +122 -0
  308. data/spec/thinking_sphinx/masks/pagination_mask_spec.rb +22 -20
  309. data/spec/thinking_sphinx/masks/scopes_mask_spec.rb +20 -18
  310. data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +45 -21
  311. data/spec/thinking_sphinx/middlewares/geographer_spec.rb +11 -9
  312. data/spec/thinking_sphinx/middlewares/glazier_spec.rb +12 -9
  313. data/spec/thinking_sphinx/middlewares/inquirer_spec.rb +9 -7
  314. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +60 -58
  315. data/spec/thinking_sphinx/middlewares/stale_id_checker_spec.rb +7 -4
  316. data/spec/thinking_sphinx/middlewares/stale_id_filter_spec.rb +39 -17
  317. data/spec/thinking_sphinx/middlewares/valid_options_spec.rb +51 -0
  318. data/spec/thinking_sphinx/panes/attributes_pane_spec.rb +3 -1
  319. data/spec/thinking_sphinx/panes/distance_pane_spec.rb +6 -4
  320. data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +8 -6
  321. data/spec/thinking_sphinx/panes/weight_pane_spec.rb +4 -2
  322. data/spec/thinking_sphinx/rake_interface_spec.rb +17 -235
  323. data/spec/thinking_sphinx/real_time/attribute_spec.rb +15 -13
  324. data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +114 -28
  325. data/spec/thinking_sphinx/real_time/field_spec.rb +16 -14
  326. data/spec/thinking_sphinx/real_time/index_spec.rb +83 -32
  327. data/spec/thinking_sphinx/real_time/interpreter_spec.rb +44 -42
  328. data/spec/thinking_sphinx/real_time/transcribe_instance_spec.rb +35 -0
  329. data/spec/thinking_sphinx/real_time/transcriber_spec.rb +109 -0
  330. data/spec/thinking_sphinx/real_time/translator_spec.rb +17 -0
  331. data/spec/thinking_sphinx/scopes_spec.rb +11 -5
  332. data/spec/thinking_sphinx/search/glaze_spec.rb +17 -15
  333. data/spec/thinking_sphinx/search/query_spec.rb +19 -10
  334. data/spec/thinking_sphinx/search_spec.rb +30 -28
  335. data/spec/thinking_sphinx/wildcard_spec.rb +18 -11
  336. data/spec/thinking_sphinx_spec.rb +10 -7
  337. data/thinking-sphinx.gemspec +17 -10
  338. metadata +157 -49
  339. data/HISTORY +0 -248
  340. data/gemfiles/.gitignore +0 -1
  341. data/gemfiles/rails_3_2.gemfile +0 -11
  342. data/gemfiles/rails_4_0.gemfile +0 -11
  343. data/gemfiles/rails_4_1.gemfile +0 -11
  344. data/gemfiles/rails_4_2.gemfile +0 -11
  345. data/lib/thinking_sphinx/controller.rb +0 -10
  346. data/lib/thinking_sphinx/middlewares/utf8.rb +0 -27
  347. data/lib/thinking_sphinx/sphinxql.rb +0 -23
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Connection::JRuby < ThinkingSphinx::Connection::Client
4
+ attr_reader :address, :options
5
+
6
+ def initialize(options)
7
+ options.delete :socket
8
+
9
+ super
10
+
11
+ @address = "jdbc:mysql://#{@options[:host]}:#{@options[:port]}/?allowMultiQueries=true"
12
+ end
13
+
14
+ def base_error
15
+ Java::JavaSql::SQLException
16
+ end
17
+
18
+ private
19
+
20
+ def client
21
+ @client ||= Java::ComMysqlJdbc::Driver.new.connect address, properties
22
+ rescue base_error => error
23
+ raise ThinkingSphinx::SphinxError.new_from_mysql error
24
+ end
25
+
26
+ def properties
27
+ object = Java::JavaUtil::Properties.new
28
+ object.setProperty "user", options[:username] if options[:username]
29
+ object.setProperty "password", options[:password] if options[:password]
30
+ object
31
+ end
32
+
33
+ def results_for(statements)
34
+ statement = client.createStatement
35
+ statement.execute statements
36
+
37
+ results = [set_to_array(statement.getResultSet)]
38
+ results << set_to_array(statement.getResultSet) while statement.getMoreResults
39
+ results.compact
40
+ end
41
+
42
+ def set_to_array(set)
43
+ return nil if set.nil?
44
+
45
+ meta = set.getMetaData
46
+ rows = []
47
+
48
+ while set.next
49
+ rows << (1..meta.getColumnCount).inject({}) do |row, index|
50
+ name = meta.getColumnName index
51
+ row[name] = set.getObject(index)
52
+ row
53
+ end
54
+ end
55
+
56
+ rows
57
+ end
58
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Connection::MRI < ThinkingSphinx::Connection::Client
4
+ def base_error
5
+ Mysql2::Error
6
+ end
7
+
8
+ private
9
+
10
+ attr_reader :options
11
+
12
+ def client
13
+ @client ||= Mysql2::Client.new({
14
+ :flags => Mysql2::Client::MULTI_STATEMENTS,
15
+ :connect_timeout => 5
16
+ }.merge(options))
17
+ rescue base_error => error
18
+ raise ThinkingSphinx::SphinxError.new_from_mysql error
19
+ end
20
+
21
+ def results_for(statements)
22
+ results = [client.query(statements)]
23
+ results << client.store_result while client.next_result
24
+ results
25
+ end
26
+ end
@@ -1,22 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Connection
2
4
  MAXIMUM_RETRIES = 3
3
5
 
4
6
  def self.new
5
7
  configuration = ThinkingSphinx::Configuration.instance
6
- # If you use localhost, MySQL insists on a socket connection, but Sphinx
7
- # requires a TCP connection. Using 127.0.0.1 fixes that.
8
- address = configuration.searchd.address || '127.0.0.1'
9
- address = '127.0.0.1' if address == 'localhost'
10
8
 
11
9
  options = {
12
- :host => address,
10
+ :host => configuration.searchd.address,
13
11
  :port => configuration.searchd.mysql41,
12
+ :socket => configuration.searchd.socket,
14
13
  :reconnect => true
15
14
  }.merge(configuration.settings['connection_options'] || {})
16
15
 
17
16
  connection_class.new options
18
17
  end
19
18
 
19
+ def self.clear
20
+ @pool = nil
21
+ end
22
+
20
23
  def self.connection_class
21
24
  return ThinkingSphinx::Connection::JRuby if RUBY_PLATFORM == 'java'
22
25
 
@@ -26,7 +29,7 @@ module ThinkingSphinx::Connection
26
29
  def self.pool
27
30
  @pool ||= Innertube::Pool.new(
28
31
  Proc.new { ThinkingSphinx::Connection.new },
29
- Proc.new { |connection| connection.close }
32
+ Proc.new { |connection| connection.close! }
30
33
  )
31
34
  end
32
35
 
@@ -63,112 +66,8 @@ module ThinkingSphinx::Connection
63
66
  end
64
67
 
65
68
  @persistent = true
66
-
67
- class Client
68
- def close
69
- client.close unless ThinkingSphinx::Connection.persistent?
70
- end
71
-
72
- def execute(statement)
73
- query(statement).first
74
- end
75
-
76
- def query_all(*statements)
77
- query *statements
78
- end
79
-
80
- private
81
-
82
- def close_and_clear
83
- client.close
84
- @client = nil
85
- end
86
-
87
- def query(*statements)
88
- results_for *statements
89
- rescue => error
90
- message = "#{error.message} - #{statements.join('; ')}"
91
- wrapper = ThinkingSphinx::QueryExecutionError.new message
92
- wrapper.statement = statements.join('; ')
93
- raise wrapper
94
- ensure
95
- close_and_clear unless ThinkingSphinx::Connection.persistent?
96
- end
97
- end
98
-
99
- class MRI < Client
100
- def initialize(options)
101
- @options = options
102
- end
103
-
104
- def base_error
105
- Mysql2::Error
106
- end
107
-
108
- private
109
-
110
- attr_reader :options
111
-
112
- def client
113
- @client ||= Mysql2::Client.new({
114
- :flags => Mysql2::Client::MULTI_STATEMENTS
115
- }.merge(options))
116
- rescue base_error => error
117
- raise ThinkingSphinx::SphinxError.new_from_mysql error
118
- end
119
-
120
- def results_for(*statements)
121
- results = [client.query(statements.join('; '))]
122
- results << client.store_result while client.next_result
123
- results
124
- end
125
- end
126
-
127
- class JRuby < Client
128
- attr_reader :address, :options
129
-
130
- def initialize(options)
131
- @address = "jdbc:mysql://#{options[:host]}:#{options[:port]}/?allowMultiQueries=true"
132
- @options = options
133
- end
134
-
135
- def base_error
136
- Java::JavaSql::SQLException
137
- end
138
-
139
- private
140
-
141
- def client
142
- @client ||= java.sql.DriverManager.getConnection address,
143
- options[:username], options[:password]
144
- rescue base_error => error
145
- raise ThinkingSphinx::SphinxError.new_from_mysql error
146
- end
147
-
148
- def results_for(*statements)
149
- statement = client.createStatement
150
- statement.execute statements.join('; ')
151
-
152
- results = [set_to_array(statement.getResultSet)]
153
- results << set_to_array(statement.getResultSet) while statement.getMoreResults
154
- results.compact
155
- end
156
-
157
- def set_to_array(set)
158
- return nil if set.nil?
159
-
160
- meta = set.meta_data
161
- rows = []
162
-
163
- while set.next
164
- rows << (1..meta.column_count).inject({}) do |row, index|
165
- name = meta.column_name index
166
- row[name] = set.get_object(index)
167
- row
168
- end
169
- end
170
-
171
- rows
172
- end
173
- end
174
69
  end
70
+
71
+ require 'thinking_sphinx/connection/client'
72
+ require 'thinking_sphinx/connection/jruby'
73
+ require 'thinking_sphinx/connection/mri'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Core::Field
2
4
  def infixing?
3
5
  options[:infixes]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Core::Index
2
4
  extend ActiveSupport::Concern
3
5
  include ThinkingSphinx::Core::Settings
@@ -9,7 +11,6 @@ module ThinkingSphinx::Core::Index
9
11
 
10
12
  def initialize(reference, options = {})
11
13
  @reference = reference.to_sym
12
- @docinfo = :extern
13
14
  @options = options
14
15
  @offset = config.next_offset(options[:offset_as] || reference)
15
16
  @type = 'plain'
@@ -25,11 +26,22 @@ module ThinkingSphinx::Core::Index
25
26
  false
26
27
  end
27
28
 
29
+ def document_id_for_instance(instance)
30
+ document_id_for_key instance.public_send(primary_key)
31
+ end
32
+
28
33
  def document_id_for_key(key)
34
+ return nil if key.nil?
35
+
29
36
  key * config.indices.count + offset
30
37
  end
31
38
 
32
39
  def interpret_definition!
40
+ table_exists = model.table_exists?
41
+ unless table_exists
42
+ Rails.logger.info "No table exists for #{model}. Index can not be created"
43
+ return
44
+ end
33
45
  return if @interpreted_definition
34
46
 
35
47
  apply_defaults!
@@ -47,6 +59,11 @@ module ThinkingSphinx::Core::Index
47
59
  @options
48
60
  end
49
61
 
62
+ def primary_key
63
+ @primary_key ||= @options[:primary_key] ||
64
+ config.settings['primary_key'] || model.primary_key || :id
65
+ end
66
+
50
67
  def render
51
68
  pre_render
52
69
  set_path
@@ -84,7 +101,10 @@ module ThinkingSphinx::Core::Index
84
101
  end
85
102
 
86
103
  def set_path
87
- FileUtils.mkdir_p path_prefix
104
+ unless config.settings['skip_directory_creation']
105
+ FileUtils.mkdir_p path_prefix
106
+ end
107
+
88
108
  @path = File.join path_prefix, name
89
109
  end
90
110
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Core::Interpreter < BasicObject
2
4
  def self.translate!(index, block)
3
5
  new(index, block).translate!
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Core::Property
2
4
  def facet?
3
5
  options[:facet]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Core::Settings
2
4
  private
3
5
  def apply_defaults!(defaults = self.class.settings)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Core
2
4
  #
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Deletion
2
4
  delegate :name, :to => :index
3
5
 
@@ -20,31 +22,49 @@ class ThinkingSphinx::Deletion
20
22
 
21
23
  attr_reader :index, :ids
22
24
 
23
- def document_ids_for_keys
24
- ids.collect { |id| index.document_id_for_key id }
25
- end
26
-
27
25
  def execute(statement)
28
- ThinkingSphinx::Connection.take do |connection|
29
- connection.execute statement
30
- end
31
- end
26
+ statement = statement.gsub(/\s*\n\s*/, ' ').strip
32
27
 
33
- class RealtimeDeletion < ThinkingSphinx::Deletion
34
- def perform
35
- execute Riddle::Query::Delete.new(name, document_ids_for_keys).to_sql
28
+ ThinkingSphinx::Logger.log :query, statement do
29
+ ThinkingSphinx::Connection.take do |connection|
30
+ connection.execute statement
31
+ end
36
32
  end
37
33
  end
38
34
 
39
35
  class PlainDeletion < ThinkingSphinx::Deletion
40
36
  def perform
41
- document_ids_for_keys.each_slice(1000) do |document_ids|
37
+ ids.each_slice(1000) do |some_ids|
42
38
  execute <<-SQL
43
39
  UPDATE #{name}
44
40
  SET sphinx_deleted = 1
45
- WHERE id IN (#{document_ids.join(', ')})
41
+ WHERE sphinx_internal_id IN (#{some_ids.join(', ')})
42
+ SQL
43
+ end
44
+ end
45
+ end
46
+
47
+ class RealtimeDeletion < ThinkingSphinx::Deletion
48
+ def perform
49
+ return unless callbacks_enabled?
50
+
51
+ ids.each_slice(1000) do |some_ids|
52
+ execute <<-SQL
53
+ DELETE FROM #{name}
54
+ WHERE sphinx_internal_id IN (#{some_ids.join(', ')})
46
55
  SQL
47
56
  end
48
57
  end
58
+
59
+ private
60
+
61
+ def callbacks_enabled?
62
+ setting = configuration.settings['real_time_callbacks']
63
+ setting.nil? || setting
64
+ end
65
+
66
+ def configuration
67
+ ThinkingSphinx::Configuration.instance
68
+ end
49
69
  end
50
70
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Deltas::DefaultDelta
2
4
  attr_reader :adapter, :options
3
5
 
@@ -13,7 +15,7 @@ class ThinkingSphinx::Deltas::DefaultDelta
13
15
 
14
16
  def delete(index, instance)
15
17
  ThinkingSphinx::Deltas::DeleteJob.new(
16
- index.name, index.document_id_for_key(instance.id)
18
+ index.name, index.document_id_for_instance(instance)
17
19
  ).perform
18
20
  end
19
21
 
@@ -1,15 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Deltas::DeleteJob
2
4
  def initialize(index_name, document_id)
3
5
  @index_name, @document_id = index_name, document_id
4
6
  end
5
7
 
6
8
  def perform
7
- ThinkingSphinx::Connection.take do |connection|
8
- connection.execute Riddle::Query.update(
9
- @index_name, @document_id, :sphinx_deleted => true
10
- )
9
+ return if @document_id.nil?
10
+
11
+ ThinkingSphinx::Logger.log :query, statement do
12
+ ThinkingSphinx::Connection.take do |connection|
13
+ connection.execute statement
14
+ end
11
15
  end
12
16
  rescue ThinkingSphinx::ConnectionError => error
13
17
  # This isn't vital, so don't raise the error.
14
18
  end
19
+
20
+ private
21
+
22
+ def statement
23
+ @statement ||= Riddle::Query.update(
24
+ @index_name, @document_id, :sphinx_deleted => true
25
+ )
26
+ end
15
27
  end
@@ -1,16 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Deltas::IndexJob
2
4
  def initialize(index_name)
3
5
  @index_name = index_name
4
6
  end
5
7
 
6
8
  def perform
7
- configuration.controller.index @index_name,
8
- :verbose => !configuration.settings['quiet_deltas']
9
+ ThinkingSphinx::Commander.call(
10
+ :index_sql, configuration,
11
+ :indices => [index_name],
12
+ :verbose => !quiet_deltas?
13
+ )
9
14
  end
10
15
 
11
16
  private
12
17
 
18
+ attr_reader :index_name
19
+
13
20
  def configuration
14
21
  @configuration ||= ThinkingSphinx::Configuration.instance
15
22
  end
23
+
24
+ def quiet_deltas?
25
+ configuration.settings['quiet_deltas'].nil? ||
26
+ configuration.settings['quiet_deltas']
27
+ end
16
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Deltas
2
4
  def self.config
3
5
  ThinkingSphinx::Configuration.instance
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Distributed::Index <
2
4
  Riddle::Configuration::DistributedIndex
3
5
 
4
- attr_reader :reference, :options
6
+ attr_reader :reference, :options, :local_index_objects
5
7
 
6
8
  def initialize(reference)
7
- @reference = reference
8
- @options = {}
9
+ @reference = reference
10
+ @options = {}
11
+ @local_index_objects = []
9
12
 
10
13
  super reference.to_s.gsub('/', '_')
11
14
  end
@@ -18,7 +21,26 @@ class ThinkingSphinx::Distributed::Index <
18
21
  true
19
22
  end
20
23
 
24
+ def facets
25
+ local_index_objects.collect(&:facets).flatten
26
+ end
27
+
28
+ def local_index_objects=(indices)
29
+ self.local_indices = indices.collect(&:name)
30
+ @local_index_objects = indices
31
+ end
32
+
21
33
  def model
22
34
  @model ||= reference.to_s.camelize.constantize
23
35
  end
36
+
37
+ def primary_key
38
+ @primary_key ||= configuration.settings['primary_key'] || :id
39
+ end
40
+
41
+ private
42
+
43
+ def configuration
44
+ ThinkingSphinx::Configuration.instance
45
+ end
24
46
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Distributed
2
4
  #
3
5
  end
@@ -1,18 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::SphinxError < StandardError
2
4
  attr_accessor :statement
3
5
 
4
6
  def self.new_from_mysql(error)
5
7
  case error.message
6
- when /parse error/
8
+ when /parse error/, /query is non-computable/
7
9
  replacement = ThinkingSphinx::ParseError.new(error.message)
8
10
  when /syntax error/
9
11
  replacement = ThinkingSphinx::SyntaxError.new(error.message)
10
- when /query error/
12
+ when /query error/, /unknown column/
11
13
  replacement = ThinkingSphinx::QueryError.new(error.message)
12
- when /Can't connect to MySQL server/, /Communications link failure/
14
+ when /Can't connect to( MySQL)? server/,
15
+ /Communications link failure/,
16
+ /Lost connection to( MySQL)? server/
13
17
  replacement = ThinkingSphinx::ConnectionError.new(
14
18
  "Error connecting to Sphinx via the MySQL protocol. #{error.message}"
15
19
  )
20
+ when /offset out of bounds/
21
+ replacement = ThinkingSphinx::OutOfBoundsError.new(error.message)
16
22
  else
17
23
  replacement = new(error.message)
18
24
  end
@@ -29,12 +35,28 @@ end
29
35
  class ThinkingSphinx::QueryError < ThinkingSphinx::SphinxError
30
36
  end
31
37
 
38
+ class ThinkingSphinx::QueryLengthError < ThinkingSphinx::SphinxError
39
+ def message
40
+ <<-MESSAGE
41
+ The supplied SphinxQL statement is #{statement.length} characters long. The maximum allowed length is #{ThinkingSphinx::Configuration.instance.settings['maximum_statement_length']}.
42
+
43
+ If this error has been raised during real-time index population, it's probably due to overly large batches of records being processed at once. The default is 1000, but you can lower it on a per-environment basis in config/thinking_sphinx.yml:
44
+
45
+ development:
46
+ batch_size: 500
47
+ MESSAGE
48
+ end
49
+ end
50
+
32
51
  class ThinkingSphinx::SyntaxError < ThinkingSphinx::QueryError
33
52
  end
34
53
 
35
54
  class ThinkingSphinx::ParseError < ThinkingSphinx::QueryError
36
55
  end
37
56
 
57
+ class ThinkingSphinx::OutOfBoundsError < ThinkingSphinx::QueryError
58
+ end
59
+
38
60
  class ThinkingSphinx::QueryExecutionError < StandardError
39
61
  attr_accessor :statement
40
62
  end
@@ -50,3 +72,25 @@ end
50
72
 
51
73
  class ThinkingSphinx::PopulatedResultsError < StandardError
52
74
  end
75
+
76
+ class ThinkingSphinx::DuplicateNameError < StandardError
77
+ end
78
+
79
+ class ThinkingSphinx::InvalidDatabaseAdapter < StandardError
80
+ end
81
+
82
+ class ThinkingSphinx::SphinxAlreadyRunning < StandardError
83
+ end
84
+
85
+ class ThinkingSphinx::UnknownDatabaseAdapter < StandardError
86
+ end
87
+
88
+ class ThinkingSphinx::UnknownAttributeType < StandardError
89
+ end
90
+
91
+ class ThinkingSphinx::TranscriptionError < StandardError
92
+ attr_accessor :inner_exception, :instance, :property
93
+ end
94
+
95
+ class ThinkingSphinx::UnknownCommand < StandardError
96
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Excerpter
2
4
  DefaultOptions = {
3
5
  :before_match => '<span class="match">',
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Facet
2
4
  attr_reader :name
3
5
 
@@ -11,7 +13,7 @@ class ThinkingSphinx::Facet
11
13
 
12
14
  def results_from(raw)
13
15
  raw.inject({}) { |hash, row|
14
- hash[row[group_column]] = row[ThinkingSphinx::SphinxQL.count[:column]]
16
+ hash[row[group_column]] = row["sphinx_internal_count"]
15
17
  hash
16
18
  }
17
19
  end
@@ -19,8 +21,7 @@ class ThinkingSphinx::Facet
19
21
  private
20
22
 
21
23
  def group_column
22
- @properties.any?(&:multi?) ?
23
- ThinkingSphinx::SphinxQL.group_by[:column] : name
24
+ @properties.any?(&:multi?) ? "sphinx_internal_group" : name
24
25
  end
25
26
 
26
27
  def use_field?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::FacetSearch
2
4
  include Enumerable
3
5
 
@@ -112,8 +114,8 @@ class ThinkingSphinx::FacetSearch
112
114
  def options_for(facet)
113
115
  options.merge(
114
116
  :select => [(options[:select] || '*'),
115
- "#{ThinkingSphinx::SphinxQL.group_by[:select]}",
116
- "#{ThinkingSphinx::SphinxQL.count[:select]}"
117
+ "groupby() AS sphinx_internal_group",
118
+ "id AS sphinx_document_id, count(DISTINCT sphinx_document_id) AS sphinx_internal_count"
117
119
  ].join(', '),
118
120
  :group_by => facet.name,
119
121
  :indices => index_names_for(facet),
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::FloatFormatter
2
4
  PATTERN = /(\d+)e\-(\d+)$/
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Frameworks::Plain
2
4
  attr_accessor :environment, :root
3
5