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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Middlewares::StaleIdFilter <
2
4
  ThinkingSphinx::Middlewares::Middleware
3
5
 
@@ -11,7 +13,7 @@ class ThinkingSphinx::Middlewares::StaleIdFilter <
11
13
  rescue ThinkingSphinx::Search::StaleIdsException => error
12
14
  raise error if @retries <= 0
13
15
 
14
- append_stale_ids error.ids
16
+ append_stale_ids error.ids, error.context
15
17
  ThinkingSphinx::Logger.log :message, log_message
16
18
 
17
19
  @retries -= 1 and retry
@@ -20,7 +22,7 @@ class ThinkingSphinx::Middlewares::StaleIdFilter <
20
22
 
21
23
  private
22
24
 
23
- def append_stale_ids(ids)
25
+ def append_stale_ids(ids, context)
24
26
  @stale_ids |= ids
25
27
 
26
28
  context.search.options[:without_ids] ||= []
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Middlewares::ValidOptions <
4
+ ThinkingSphinx::Middlewares::Middleware
5
+
6
+ def call(contexts)
7
+ contexts.each { |context| check_options context.search.options }
8
+
9
+ app.call contexts
10
+ end
11
+
12
+ private
13
+
14
+ def check_options(options)
15
+ unknown = invalid_keys options.keys
16
+ return if unknown.empty?
17
+
18
+ ThinkingSphinx::Logger.log :caution,
19
+ "Unexpected search options: #{unknown.inspect}"
20
+ end
21
+
22
+ def invalid_keys(keys)
23
+ keys - ThinkingSphinx::Search.valid_options
24
+ end
25
+ end
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Middlewares; end
2
4
 
3
- %w[middleware active_record_translator geographer glazier ids_only inquirer
4
- sphinxql stale_id_checker stale_id_filter utf8].each do |middleware|
5
+ %w[
6
+ middleware active_record_translator geographer glazier ids_only inquirer
7
+ sphinxql stale_id_checker stale_id_filter valid_options
8
+ ].each do |middleware|
5
9
  require "thinking_sphinx/middlewares/#{middleware}"
6
10
  end
7
11
 
@@ -10,7 +14,7 @@ module ThinkingSphinx::Middlewares
10
14
  middlewares.each { |m| builder.use m }
11
15
  end
12
16
 
13
- BASE_MIDDLEWARES = [SphinxQL, Geographer, Inquirer]
17
+ BASE_MIDDLEWARES = [ValidOptions, SphinxQL, Geographer, Inquirer]
14
18
 
15
19
  DEFAULT = ::Middleware::Builder.new do
16
20
  use StaleIdFilter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Panes::AttributesPane
2
4
  def initialize(context, object, raw)
3
5
  @raw = raw
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Panes::DistancePane
2
4
  def initialize(context, object, raw)
3
5
  @raw = raw
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Panes::ExcerptsPane
2
4
  def initialize(context, object, raw)
3
5
  @context, @object = context, object
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Panes::WeightPane
2
4
  def initialize(context, object, raw)
3
5
  @raw = raw
4
6
  end
5
7
 
6
8
  def weight
7
- @raw[ThinkingSphinx::SphinxQL.weight[:column]]
9
+ @raw["weight()"]
8
10
  end
9
11
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Panes
2
4
  #
3
5
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::Processor
4
+ # @param instance [ActiveRecord::Base] an ActiveRecord object
5
+ # @param model [Class] the ActiveRecord model of the instance
6
+ # @param id [Integer] the instance indices primary key (might be different from model primary key)
7
+ def initialize(instance: nil, model: nil, id: nil)
8
+ raise ArgumentError if instance.nil? && (model.nil? || id.nil?)
9
+
10
+ @instance = instance
11
+ @model = model || instance.class
12
+ @id = id
13
+ end
14
+
15
+ def delete
16
+ return if instance&.new_record?
17
+
18
+ indices.each { |index| perform_deletion(index) }
19
+ end
20
+
21
+ # Will insert instance into all matching indices
22
+ def upsert
23
+ real_time_indices.each do |index|
24
+ found = loaded_instance(index)
25
+ ThinkingSphinx::RealTime::Transcriber.new(index).copy found if found
26
+ end
27
+ end
28
+
29
+ # Will upsert or delete instance into all matching indices based on index scope
30
+ def sync
31
+ real_time_indices.each do |index|
32
+ found = find_in(index)
33
+
34
+ if found
35
+ ThinkingSphinx::RealTime::Transcriber.new(index).copy found
36
+ else
37
+ ThinkingSphinx::Deletion.perform(index, index_id(index))
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :instance, :model, :id
45
+
46
+ def indices
47
+ ThinkingSphinx::Configuration.instance.index_set_class.new(
48
+ :instances => [instance].compact, :classes => [model]
49
+ ).to_a
50
+ end
51
+
52
+ def find_in(index)
53
+ index.scope.find_by(index.primary_key => index_id(index))
54
+ end
55
+
56
+ def loaded_instance(index)
57
+ instance || find_in(index)
58
+ end
59
+
60
+ def real_time_indices
61
+ indices.select { |index| index.is_a? ThinkingSphinx::RealTime::Index }
62
+ end
63
+
64
+ def perform_deletion(index)
65
+ ThinkingSphinx::Deletion.perform(index, index_id(index))
66
+ end
67
+
68
+ def index_id(index)
69
+ id || instance.public_send(index.primary_key)
70
+ end
71
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ThinkingSphinx::Query
2
4
  def self.escape(query)
3
5
  Riddle::Query.escape query
@@ -1,11 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::Railtie < Rails::Railtie
4
+ config.to_prepare do
5
+ ThinkingSphinx::Configuration.reset
6
+ end
7
+
8
+ config.after_initialize do
9
+ require 'thinking_sphinx/active_record'
10
+ end
11
+
2
12
  initializer 'thinking_sphinx.initialisation' do
3
- if defined?(ActiveRecord::Base)
4
- ActiveRecord::Base.send :include, ThinkingSphinx::ActiveRecord::Base
13
+ ActiveSupport.on_load(:active_record) do
14
+ require 'thinking_sphinx/active_record'
5
15
  end
16
+
17
+ if zeitwerk?
18
+ ActiveSupport::Dependencies.autoload_paths.delete(
19
+ Rails.root.join("app", "indices").to_s
20
+ )
21
+ end
22
+
23
+ Rails.application.config.eager_load_paths -=
24
+ ThinkingSphinx::Configuration.instance.index_paths
25
+ Rails.application.config.eager_load_paths.freeze
6
26
  end
7
27
 
8
28
  rake_tasks do
9
29
  load File.expand_path('../tasks.rb', __FILE__)
10
30
  end
31
+
32
+ def zeitwerk?
33
+ return true if ActiveSupport::VERSION::MAJOR >= 7
34
+ return false if ActiveSupport::VERSION::MAJOR <= 5
35
+
36
+ Rails.application.config.autoloader == :zeitwerk
37
+ end
11
38
  end
@@ -1,87 +1,32 @@
1
- class ThinkingSphinx::RakeInterface
2
- def clear_all
3
- [
4
- configuration.indices_location,
5
- configuration.searchd.binlog_path
6
- ].each do |path|
7
- FileUtils.rm_r(path) if File.exists?(path)
8
- end
9
- end
1
+ # frozen_string_literal: true
10
2
 
11
- def clear_real_time
12
- indices = configuration.indices.select { |index| index.type == 'rt' }
13
- indices.each do |index|
14
- index.render
15
- Dir["#{index.path}.*"].each { |path| FileUtils.rm path }
16
- end
3
+ class ThinkingSphinx::RakeInterface
4
+ DEFAULT_OPTIONS = {:verbose => true}
17
5
 
18
- path = configuration.searchd.binlog_path
19
- FileUtils.rm_r(path) if File.exists?(path)
6
+ def initialize(options = {})
7
+ @options = DEFAULT_OPTIONS.merge options
8
+ @options[:verbose] = false if @options[:silent]
20
9
  end
21
10
 
22
11
  def configure
23
- puts "Generating configuration to #{configuration.configuration_file}"
24
- configuration.render_to_file
25
- end
26
-
27
- def generate
28
- indices = configuration.indices.select { |index| index.type == 'rt' }
29
- indices.each do |index|
30
- ThinkingSphinx::RealTime::Populator.populate index
31
- end
32
- end
33
-
34
- def index(reconfigure = true, verbose = true)
35
- configure if reconfigure
36
- FileUtils.mkdir_p configuration.indices_location
37
- ThinkingSphinx.before_index_hooks.each { |hook| hook.call }
38
- controller.index :verbose => verbose
12
+ ThinkingSphinx::Commander.call :configure, configuration, options
39
13
  end
40
14
 
41
- def prepare
42
- configuration.preload_indices
43
- configuration.render
44
-
45
- FileUtils.mkdir_p configuration.indices_location
15
+ def daemon
16
+ @daemon ||= ThinkingSphinx::Interfaces::Daemon.new configuration, options
46
17
  end
47
18
 
48
- def start
49
- raise RuntimeError, 'searchd is already running' if controller.running?
50
-
51
- FileUtils.mkdir_p configuration.indices_location
52
- controller.start
53
-
54
- if controller.running?
55
- puts "Started searchd successfully (pid: #{controller.pid})."
56
- else
57
- puts "Failed to start searchd. Check the log files for more information."
58
- end
19
+ def rt
20
+ @rt ||= ThinkingSphinx::Interfaces::RealTime.new configuration, options
59
21
  end
60
22
 
61
- def status
62
- if controller.running?
63
- puts "The Sphinx daemon searchd is currently running."
64
- else
65
- puts "The Sphinx daemon searchd is not currently running."
66
- end
67
- end
68
-
69
- def stop
70
- unless controller.running?
71
- puts 'searchd is not currently running.' and return
72
- end
73
-
74
- pid = controller.pid
75
- until controller.stop do
76
- sleep(0.5)
77
- end
78
-
79
- puts "Stopped searchd daemon (pid: #{pid})."
23
+ def sql
24
+ @sql ||= ThinkingSphinx::Interfaces::SQL.new configuration, options
80
25
  end
81
26
 
82
27
  private
83
28
 
84
- delegate :controller, :to => :configuration
29
+ attr_reader :options
85
30
 
86
31
  def configuration
87
32
  ThinkingSphinx::Configuration.instance
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Attribute < ThinkingSphinx::RealTime::Property
2
4
  def multi?
3
5
  @options[:multi]
@@ -8,7 +10,9 @@ class ThinkingSphinx::RealTime::Attribute < ThinkingSphinx::RealTime::Property
8
10
  end
9
11
 
10
12
  def translate(object)
11
- super || default_value
13
+ output = super || default_value
14
+
15
+ json? ? output.to_json : output
12
16
  end
13
17
 
14
18
  private
@@ -16,4 +20,8 @@ class ThinkingSphinx::RealTime::Attribute < ThinkingSphinx::RealTime::Property
16
20
  def default_value
17
21
  type == :string ? '' : 0
18
22
  end
23
+
24
+ def json?
25
+ type == :json
26
+ end
19
27
  end
@@ -1,16 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks
2
4
  def initialize(reference, path = [], &block)
3
5
  @reference, @path, @block = reference, path, block
4
6
  end
5
7
 
6
- def after_save(instance)
7
- return unless real_time_indices? && callbacks_enabled?
8
+ def after_commit(instance)
9
+ persist_changes instance
10
+ end
8
11
 
9
- real_time_indices.each do |index|
10
- objects_for(instance).each do |object|
11
- ThinkingSphinx::RealTime::Transcriber.new(index).copy object
12
- end
13
- end
12
+ def after_save(instance)
13
+ persist_changes instance
14
14
  end
15
15
 
16
16
  private
@@ -40,6 +40,16 @@ class ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks
40
40
  Array results
41
41
  end
42
42
 
43
+ def persist_changes(instance)
44
+ return unless real_time_indices? && callbacks_enabled?
45
+
46
+ real_time_indices.each do |index|
47
+ objects_for(instance).each do |object|
48
+ ThinkingSphinx::RealTime::Transcriber.new(index).copy object
49
+ end
50
+ end
51
+ end
52
+
43
53
  def real_time_indices?
44
54
  real_time_indices.any?
45
55
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Field < ThinkingSphinx::RealTime::Property
2
4
  include ThinkingSphinx::Core::Field
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Index::Template
2
4
  attr_reader :index
3
5
 
@@ -8,9 +10,13 @@ class ThinkingSphinx::RealTime::Index::Template
8
10
  def apply
9
11
  add_field class_column, :sphinx_internal_class_name
10
12
 
11
- add_attribute :id, :sphinx_internal_id, :bigint
13
+ add_attribute primary_key, :sphinx_internal_id, :bigint
12
14
  add_attribute class_column, :sphinx_internal_class, :string, :facet => true
13
15
  add_attribute 0, :sphinx_deleted, :integer
16
+
17
+ if tidying?
18
+ add_attribute -> (_) { Time.current.to_i }, :sphinx_updated_at, :timestamp
19
+ end
14
20
  end
15
21
 
16
22
  private
@@ -31,4 +37,16 @@ class ThinkingSphinx::RealTime::Index::Template
31
37
  def class_column
32
38
  [:class, :name]
33
39
  end
40
+
41
+ def config
42
+ ThinkingSphinx::Configuration.instance
43
+ end
44
+
45
+ def primary_key
46
+ index.primary_key.to_sym
47
+ end
48
+
49
+ def tidying?
50
+ config.settings["real_time_tidy"]
51
+ end
34
52
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
2
4
  include ThinkingSphinx::Core::Index
3
5
 
@@ -8,16 +10,20 @@ class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
8
10
  @attributes = []
9
11
  @conditions = []
10
12
 
11
- Template.new(self).apply
12
-
13
13
  super reference, options
14
+
15
+ Template.new(self).apply
14
16
  end
15
17
 
16
18
  def add_attribute(attribute)
19
+ @attributes.delete_if { |existing| existing.name == attribute.name }
20
+
17
21
  @attributes << attribute
18
22
  end
19
23
 
20
24
  def add_field(field)
25
+ @fields.delete_if { |existing| existing.name == field.name }
26
+
21
27
  @fields << field
22
28
  end
23
29
 
@@ -59,16 +65,16 @@ class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
59
65
 
60
66
  def collection_for(attribute)
61
67
  case attribute.type
62
- when :integer, :boolean
68
+ when :integer, :boolean, :timestamp
63
69
  attribute.multi? ? @rt_attr_multi : @rt_attr_uint
64
70
  when :string
65
71
  @rt_attr_string
66
- when :timestamp
67
- @rt_attr_timestamp
68
72
  when :float
69
73
  @rt_attr_float
70
74
  when :bigint
71
75
  attribute.multi? ? @rt_attr_multi_64 : @rt_attr_bigint
76
+ when :json
77
+ @rt_attr_json
72
78
  else
73
79
  raise "Unknown attribute type '#{attribute.type}'"
74
80
  end
@@ -1,18 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Interpreter <
2
4
  ::ThinkingSphinx::Core::Interpreter
3
5
 
4
6
  def has(*columns)
5
7
  options = columns.extract_options!
6
- @index.attributes += columns.collect { |column|
8
+
9
+ columns.collect { |column|
7
10
  ::ThinkingSphinx::RealTime::Attribute.new column, options
8
- }
11
+ }.each { |attribute| @index.add_attribute attribute }
9
12
  end
10
13
 
11
14
  def indexes(*columns)
12
15
  options = columns.extract_options!
13
- @index.fields += columns.collect { |column|
16
+
17
+ columns.collect { |column|
14
18
  ::ThinkingSphinx::RealTime::Field.new column, options
15
- }
19
+ }.each { |field| @index.add_field field }
16
20
 
17
21
  append_sortable_attributes columns, options if options[:sortable]
18
22
  end
@@ -37,7 +41,7 @@ class ThinkingSphinx::RealTime::Interpreter <
37
41
  def append_sortable_attributes(columns, options)
38
42
  options = options.except(:sortable).merge(:type => :string)
39
43
 
40
- @index.attributes += columns.collect { |column|
44
+ columns.collect { |column|
41
45
  aliased_name = options[:as]
42
46
  aliased_name ||= column.__name.to_sym if column.respond_to?(:__name)
43
47
  aliased_name ||= column
@@ -45,6 +49,6 @@ class ThinkingSphinx::RealTime::Interpreter <
45
49
  options[:as] = "#{aliased_name}_sort".to_sym
46
50
 
47
51
  ::ThinkingSphinx::RealTime::Attribute.new column, options
48
- }
52
+ }.each { |attribute| @index.add_attribute attribute }
49
53
  end
50
54
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Populator
2
4
  def self.populate(index)
3
5
  new(index).populate
@@ -5,28 +7,28 @@ class ThinkingSphinx::RealTime::Populator
5
7
 
6
8
  def initialize(index)
7
9
  @index = index
10
+ @started_at = Time.current
8
11
  end
9
12
 
10
- def populate(&block)
13
+ def populate
11
14
  instrument 'start_populating'
12
15
 
13
- remove_files
14
-
15
- scope.find_each do |instance|
16
- transcriber.copy instance
17
- instrument 'populated', :instance => instance
16
+ scope.find_in_batches(:batch_size => batch_size) do |instances|
17
+ transcriber.copy *instances
18
+ instrument 'populated', :instances => instances
18
19
  end
19
20
 
20
- controller.rotate
21
+ transcriber.clear_before(started_at) if configuration.settings["real_time_tidy"]
22
+
21
23
  instrument 'finish_populating'
22
24
  end
23
25
 
24
26
  private
25
27
 
26
- attr_reader :index
28
+ attr_reader :index, :started_at
27
29
 
28
- delegate :controller, :to => :configuration
29
- delegate :scope, :to => :index
30
+ delegate :controller, :batch_size, :to => :configuration
31
+ delegate :scope, :to => :index
30
32
 
31
33
  def configuration
32
34
  ThinkingSphinx::Configuration.instance
@@ -38,10 +40,6 @@ class ThinkingSphinx::RealTime::Populator
38
40
  )
39
41
  end
40
42
 
41
- def remove_files
42
- Dir["#{index.path}*"].each { |file| FileUtils.rm file }
43
- end
44
-
45
43
  def transcriber
46
44
  @transcriber ||= ThinkingSphinx::RealTime::Transcriber.new index
47
45
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThinkingSphinx::RealTime::Processor
4
+ def self.call(indices, &block)
5
+ new(indices).call(&block)
6
+ end
7
+
8
+ def initialize(indices)
9
+ @indices = indices
10
+ end
11
+
12
+ def call(&block)
13
+ subscribe_to_progress
14
+
15
+ indices.each do |index|
16
+ ThinkingSphinx::RealTime.populator.populate index
17
+
18
+ block.call
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :indices
25
+
26
+ def command
27
+ ThinkingSphinx::Commander.call(
28
+ command, configuration, options, stream
29
+ )
30
+ end
31
+
32
+ def subscribe_to_progress
33
+ ThinkingSphinx::Subscribers::PopulatorSubscriber.
34
+ attach_to 'thinking_sphinx.real_time'
35
+ end
36
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ThinkingSphinx::RealTime::Property
2
4
  include ThinkingSphinx::Core::Property
3
5
 
@@ -14,10 +16,6 @@ class ThinkingSphinx::RealTime::Property
14
16
  end
15
17
 
16
18
  def translate(object)
17
- return @column.__name unless @column.__name.is_a?(Symbol)
18
-
19
- base = @column.__stack.inject(object) { |base, node| base.try(node) }
20
- base = base.try(@column.__name)
21
- base.is_a?(String) ? base.gsub("\u0000", '') : base
19
+ ThinkingSphinx::RealTime::Translator.call(object, @column)
22
20
  end
23
21
  end