chewy 0.9.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (275) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +214 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +41 -19
  5. data/.rubocop_todo.yml +2 -2
  6. data/.yardopts +5 -0
  7. data/Appraisals +58 -28
  8. data/CHANGELOG.md +153 -12
  9. data/Gemfile +20 -12
  10. data/LEGACY_DSL.md +497 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +338 -528
  13. data/chewy.gemspec +11 -12
  14. data/gemfiles/rails.5.2.activerecord.gemfile +17 -0
  15. data/gemfiles/rails.5.2.mongoid.6.4.gemfile +17 -0
  16. data/gemfiles/rails.6.0.activerecord.gemfile +17 -0
  17. data/gemfiles/rails.6.1.activerecord.gemfile +19 -0
  18. data/gemfiles/ruby3.gemfile +10 -0
  19. data/gemfiles/sequel.4.45.gemfile +11 -0
  20. data/lib/chewy.rb +79 -44
  21. data/lib/chewy/backports/duplicable.rb +1 -1
  22. data/lib/chewy/config.rb +43 -17
  23. data/lib/chewy/errors.rb +2 -2
  24. data/lib/chewy/fields/base.rb +56 -31
  25. data/lib/chewy/fields/root.rb +44 -11
  26. data/lib/chewy/index.rb +237 -149
  27. data/lib/chewy/index/actions.rb +100 -35
  28. data/lib/chewy/index/aliases.rb +2 -1
  29. data/lib/chewy/index/settings.rb +11 -5
  30. data/lib/chewy/index/specification.rb +60 -0
  31. data/lib/chewy/journal.rb +40 -92
  32. data/lib/chewy/minitest/helpers.rb +6 -6
  33. data/lib/chewy/minitest/search_index_receiver.rb +17 -17
  34. data/lib/chewy/query.rb +182 -122
  35. data/lib/chewy/query/compose.rb +13 -13
  36. data/lib/chewy/query/criteria.rb +13 -13
  37. data/lib/chewy/query/filters.rb +21 -4
  38. data/lib/chewy/query/loading.rb +1 -2
  39. data/lib/chewy/query/nodes/and.rb +2 -2
  40. data/lib/chewy/query/nodes/bool.rb +1 -1
  41. data/lib/chewy/query/nodes/equal.rb +2 -2
  42. data/lib/chewy/query/nodes/exists.rb +1 -1
  43. data/lib/chewy/query/nodes/field.rb +1 -1
  44. data/lib/chewy/query/nodes/has_relation.rb +2 -2
  45. data/lib/chewy/query/nodes/match_all.rb +1 -1
  46. data/lib/chewy/query/nodes/missing.rb +1 -1
  47. data/lib/chewy/query/nodes/not.rb +2 -2
  48. data/lib/chewy/query/nodes/or.rb +2 -2
  49. data/lib/chewy/query/nodes/prefix.rb +1 -1
  50. data/lib/chewy/query/nodes/query.rb +2 -2
  51. data/lib/chewy/query/nodes/range.rb +4 -4
  52. data/lib/chewy/query/nodes/regexp.rb +4 -4
  53. data/lib/chewy/query/nodes/script.rb +3 -3
  54. data/lib/chewy/query/pagination.rb +10 -1
  55. data/lib/chewy/railtie.rb +4 -3
  56. data/lib/chewy/rake_helper.rb +265 -48
  57. data/lib/chewy/rspec/update_index.rb +33 -27
  58. data/lib/chewy/search.rb +79 -26
  59. data/lib/chewy/search/loader.rb +83 -0
  60. data/lib/chewy/{query → search}/pagination/kaminari.rb +13 -5
  61. data/lib/chewy/search/pagination/will_paginate.rb +43 -0
  62. data/lib/chewy/search/parameters.rb +168 -0
  63. data/lib/chewy/search/parameters/aggs.rb +16 -0
  64. data/lib/chewy/search/parameters/allow_partial_search_results.rb +27 -0
  65. data/lib/chewy/search/parameters/concerns/bool_storage.rb +24 -0
  66. data/lib/chewy/search/parameters/concerns/hash_storage.rb +23 -0
  67. data/lib/chewy/search/parameters/concerns/integer_storage.rb +14 -0
  68. data/lib/chewy/search/parameters/concerns/query_storage.rb +238 -0
  69. data/lib/chewy/search/parameters/concerns/string_array_storage.rb +23 -0
  70. data/lib/chewy/search/parameters/concerns/string_storage.rb +14 -0
  71. data/lib/chewy/search/parameters/docvalue_fields.rb +12 -0
  72. data/lib/chewy/search/parameters/explain.rb +16 -0
  73. data/lib/chewy/search/parameters/filter.rb +47 -0
  74. data/lib/chewy/search/parameters/highlight.rb +16 -0
  75. data/lib/chewy/search/parameters/indices.rb +123 -0
  76. data/lib/chewy/search/parameters/indices_boost.rb +52 -0
  77. data/lib/chewy/search/parameters/limit.rb +17 -0
  78. data/lib/chewy/search/parameters/load.rb +32 -0
  79. data/lib/chewy/search/parameters/min_score.rb +16 -0
  80. data/lib/chewy/search/parameters/none.rb +27 -0
  81. data/lib/chewy/search/parameters/offset.rb +17 -0
  82. data/lib/chewy/search/parameters/order.rb +64 -0
  83. data/lib/chewy/search/parameters/post_filter.rb +19 -0
  84. data/lib/chewy/search/parameters/preference.rb +16 -0
  85. data/lib/chewy/search/parameters/profile.rb +16 -0
  86. data/lib/chewy/search/parameters/query.rb +19 -0
  87. data/lib/chewy/search/parameters/request_cache.rb +27 -0
  88. data/lib/chewy/search/parameters/rescore.rb +29 -0
  89. data/lib/chewy/search/parameters/script_fields.rb +16 -0
  90. data/lib/chewy/search/parameters/search_after.rb +20 -0
  91. data/lib/chewy/search/parameters/search_type.rb +16 -0
  92. data/lib/chewy/search/parameters/source.rb +73 -0
  93. data/lib/chewy/search/parameters/storage.rb +95 -0
  94. data/lib/chewy/search/parameters/stored_fields.rb +63 -0
  95. data/lib/chewy/search/parameters/suggest.rb +16 -0
  96. data/lib/chewy/search/parameters/terminate_after.rb +16 -0
  97. data/lib/chewy/search/parameters/timeout.rb +16 -0
  98. data/lib/chewy/search/parameters/track_scores.rb +16 -0
  99. data/lib/chewy/search/parameters/types.rb +20 -0
  100. data/lib/chewy/search/parameters/version.rb +16 -0
  101. data/lib/chewy/search/query_proxy.rb +257 -0
  102. data/lib/chewy/search/request.rb +1046 -0
  103. data/lib/chewy/search/response.rb +119 -0
  104. data/lib/chewy/search/scoping.rb +50 -0
  105. data/lib/chewy/search/scrolling.rb +134 -0
  106. data/lib/chewy/stash.rb +79 -0
  107. data/lib/chewy/strategy.rb +10 -3
  108. data/lib/chewy/strategy/active_job.rb +2 -1
  109. data/lib/chewy/strategy/atomic.rb +2 -4
  110. data/lib/chewy/strategy/bypass.rb +1 -1
  111. data/lib/chewy/strategy/resque.rb +1 -0
  112. data/lib/chewy/strategy/shoryuken.rb +40 -0
  113. data/lib/chewy/strategy/sidekiq.rb +13 -3
  114. data/lib/chewy/type.rb +29 -7
  115. data/lib/chewy/type/actions.rb +26 -2
  116. data/lib/chewy/type/adapter/active_record.rb +44 -29
  117. data/lib/chewy/type/adapter/base.rb +27 -7
  118. data/lib/chewy/type/adapter/mongoid.rb +19 -10
  119. data/lib/chewy/type/adapter/object.rb +187 -26
  120. data/lib/chewy/type/adapter/orm.rb +59 -32
  121. data/lib/chewy/type/adapter/sequel.rb +33 -19
  122. data/lib/chewy/type/crutch.rb +1 -1
  123. data/lib/chewy/type/import.rb +146 -191
  124. data/lib/chewy/type/import/bulk_builder.rb +122 -0
  125. data/lib/chewy/type/import/bulk_request.rb +78 -0
  126. data/lib/chewy/type/import/journal_builder.rb +45 -0
  127. data/lib/chewy/type/import/routine.rb +138 -0
  128. data/lib/chewy/type/mapping.rb +51 -35
  129. data/lib/chewy/type/observe.rb +17 -13
  130. data/lib/chewy/type/syncer.rb +222 -0
  131. data/lib/chewy/type/witchcraft.rb +32 -16
  132. data/lib/chewy/type/wrapper.rb +30 -4
  133. data/lib/chewy/version.rb +1 -1
  134. data/lib/sequel/plugins/chewy_observe.rb +4 -19
  135. data/lib/tasks/chewy.rake +84 -26
  136. data/spec/chewy/config_spec.rb +98 -1
  137. data/spec/chewy/fields/base_spec.rb +170 -135
  138. data/spec/chewy/fields/root_spec.rb +124 -20
  139. data/spec/chewy/fields/time_fields_spec.rb +2 -3
  140. data/spec/chewy/index/actions_spec.rb +214 -52
  141. data/spec/chewy/index/aliases_spec.rb +2 -2
  142. data/spec/chewy/index/settings_spec.rb +67 -38
  143. data/spec/chewy/index/specification_spec.rb +169 -0
  144. data/spec/chewy/index_spec.rb +108 -64
  145. data/spec/chewy/journal_spec.rb +150 -55
  146. data/spec/chewy/minitest/helpers_spec.rb +4 -4
  147. data/spec/chewy/minitest/search_index_receiver_spec.rb +1 -1
  148. data/spec/chewy/query/criteria_spec.rb +179 -179
  149. data/spec/chewy/query/filters_spec.rb +16 -16
  150. data/spec/chewy/query/loading_spec.rb +22 -20
  151. data/spec/chewy/query/nodes/and_spec.rb +2 -2
  152. data/spec/chewy/query/nodes/bool_spec.rb +4 -4
  153. data/spec/chewy/query/nodes/equal_spec.rb +19 -19
  154. data/spec/chewy/query/nodes/exists_spec.rb +6 -6
  155. data/spec/chewy/query/nodes/has_child_spec.rb +19 -19
  156. data/spec/chewy/query/nodes/has_parent_spec.rb +19 -19
  157. data/spec/chewy/query/nodes/missing_spec.rb +5 -5
  158. data/spec/chewy/query/nodes/not_spec.rb +4 -2
  159. data/spec/chewy/query/nodes/or_spec.rb +2 -2
  160. data/spec/chewy/query/nodes/prefix_spec.rb +5 -5
  161. data/spec/chewy/query/nodes/query_spec.rb +2 -2
  162. data/spec/chewy/query/nodes/range_spec.rb +18 -18
  163. data/spec/chewy/query/nodes/raw_spec.rb +1 -1
  164. data/spec/chewy/query/nodes/regexp_spec.rb +14 -14
  165. data/spec/chewy/query/nodes/script_spec.rb +4 -4
  166. data/spec/chewy/query/pagination/kaminari_spec.rb +3 -55
  167. data/spec/chewy/query/pagination/will_paginate_spec.rb +5 -0
  168. data/spec/chewy/query/pagination_spec.rb +25 -21
  169. data/spec/chewy/query_spec.rb +503 -561
  170. data/spec/chewy/rake_helper_spec.rb +381 -0
  171. data/spec/chewy/repository_spec.rb +4 -4
  172. data/spec/chewy/rspec/update_index_spec.rb +89 -56
  173. data/spec/chewy/runtime_spec.rb +2 -2
  174. data/spec/chewy/search/loader_spec.rb +117 -0
  175. data/spec/chewy/search/pagination/kaminari_examples.rb +71 -0
  176. data/spec/chewy/search/pagination/kaminari_spec.rb +21 -0
  177. data/spec/chewy/search/pagination/will_paginate_examples.rb +63 -0
  178. data/spec/chewy/search/pagination/will_paginate_spec.rb +23 -0
  179. data/spec/chewy/search/parameters/aggs_spec.rb +5 -0
  180. data/spec/chewy/search/parameters/bool_storage_examples.rb +53 -0
  181. data/spec/chewy/search/parameters/docvalue_fields_spec.rb +5 -0
  182. data/spec/chewy/search/parameters/explain_spec.rb +5 -0
  183. data/spec/chewy/search/parameters/filter_spec.rb +5 -0
  184. data/spec/chewy/search/parameters/hash_storage_examples.rb +59 -0
  185. data/spec/chewy/search/parameters/highlight_spec.rb +5 -0
  186. data/spec/chewy/search/parameters/indices_spec.rb +191 -0
  187. data/spec/chewy/search/parameters/integer_storage_examples.rb +32 -0
  188. data/spec/chewy/search/parameters/limit_spec.rb +5 -0
  189. data/spec/chewy/search/parameters/load_spec.rb +60 -0
  190. data/spec/chewy/search/parameters/min_score_spec.rb +32 -0
  191. data/spec/chewy/search/parameters/none_spec.rb +5 -0
  192. data/spec/chewy/search/parameters/offset_spec.rb +5 -0
  193. data/spec/chewy/search/parameters/order_spec.rb +65 -0
  194. data/spec/chewy/search/parameters/post_filter_spec.rb +5 -0
  195. data/spec/chewy/search/parameters/preference_spec.rb +5 -0
  196. data/spec/chewy/search/parameters/profile_spec.rb +5 -0
  197. data/spec/chewy/search/parameters/query_spec.rb +5 -0
  198. data/spec/chewy/search/parameters/query_storage_examples.rb +388 -0
  199. data/spec/chewy/search/parameters/request_cache_spec.rb +67 -0
  200. data/spec/chewy/search/parameters/rescore_spec.rb +62 -0
  201. data/spec/chewy/search/parameters/script_fields_spec.rb +5 -0
  202. data/spec/chewy/search/parameters/search_after_spec.rb +32 -0
  203. data/spec/chewy/search/parameters/search_type_spec.rb +5 -0
  204. data/spec/chewy/search/parameters/source_spec.rb +156 -0
  205. data/spec/chewy/search/parameters/storage_spec.rb +60 -0
  206. data/spec/chewy/search/parameters/stored_fields_spec.rb +126 -0
  207. data/spec/chewy/search/parameters/string_array_storage_examples.rb +63 -0
  208. data/spec/chewy/search/parameters/string_storage_examples.rb +32 -0
  209. data/spec/chewy/search/parameters/suggest_spec.rb +5 -0
  210. data/spec/chewy/search/parameters/terminate_after_spec.rb +5 -0
  211. data/spec/chewy/search/parameters/timeout_spec.rb +5 -0
  212. data/spec/chewy/search/parameters/track_scores_spec.rb +5 -0
  213. data/spec/chewy/search/parameters/types_spec.rb +5 -0
  214. data/spec/chewy/search/parameters/version_spec.rb +5 -0
  215. data/spec/chewy/search/parameters_spec.rb +147 -0
  216. data/spec/chewy/search/query_proxy_spec.rb +68 -0
  217. data/spec/chewy/search/request_spec.rb +685 -0
  218. data/spec/chewy/search/response_spec.rb +198 -0
  219. data/spec/chewy/search/scrolling_spec.rb +169 -0
  220. data/spec/chewy/search_spec.rb +33 -16
  221. data/spec/chewy/stash_spec.rb +95 -0
  222. data/spec/chewy/strategy/active_job_spec.rb +21 -2
  223. data/spec/chewy/strategy/resque_spec.rb +6 -0
  224. data/spec/chewy/strategy/shoryuken_spec.rb +70 -0
  225. data/spec/chewy/strategy/sidekiq_spec.rb +13 -1
  226. data/spec/chewy/strategy_spec.rb +6 -6
  227. data/spec/chewy/type/actions_spec.rb +29 -10
  228. data/spec/chewy/type/adapter/active_record_spec.rb +203 -91
  229. data/spec/chewy/type/adapter/mongoid_spec.rb +112 -54
  230. data/spec/chewy/type/adapter/object_spec.rb +101 -28
  231. data/spec/chewy/type/adapter/sequel_spec.rb +149 -82
  232. data/spec/chewy/type/import/bulk_builder_spec.rb +279 -0
  233. data/spec/chewy/type/import/bulk_request_spec.rb +102 -0
  234. data/spec/chewy/type/import/journal_builder_spec.rb +95 -0
  235. data/spec/chewy/type/import/routine_spec.rb +110 -0
  236. data/spec/chewy/type/import_spec.rb +356 -271
  237. data/spec/chewy/type/mapping_spec.rb +96 -29
  238. data/spec/chewy/type/observe_spec.rb +9 -5
  239. data/spec/chewy/type/syncer_spec.rb +123 -0
  240. data/spec/chewy/type/witchcraft_spec.rb +61 -29
  241. data/spec/chewy/type/wrapper_spec.rb +63 -23
  242. data/spec/chewy/type_spec.rb +28 -7
  243. data/spec/chewy_spec.rb +75 -7
  244. data/spec/spec_helper.rb +17 -3
  245. data/spec/support/active_record.rb +5 -1
  246. data/spec/support/class_helpers.rb +0 -14
  247. data/spec/support/mongoid.rb +15 -3
  248. data/spec/support/sequel.rb +6 -1
  249. metadata +219 -58
  250. data/.travis.yml +0 -36
  251. data/gemfiles/rails.3.2.activerecord.gemfile +0 -16
  252. data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +0 -15
  253. data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +0 -15
  254. data/gemfiles/rails.4.2.activerecord.gemfile +0 -17
  255. data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +0 -16
  256. data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +0 -16
  257. data/gemfiles/rails.4.2.mongoid.4.0.gemfile +0 -16
  258. data/gemfiles/rails.4.2.mongoid.4.0.kaminari.gemfile +0 -15
  259. data/gemfiles/rails.4.2.mongoid.4.0.will_paginate.gemfile +0 -15
  260. data/gemfiles/rails.4.2.mongoid.5.1.gemfile +0 -16
  261. data/gemfiles/rails.4.2.mongoid.5.1.kaminari.gemfile +0 -15
  262. data/gemfiles/rails.4.2.mongoid.5.1.will_paginate.gemfile +0 -15
  263. data/gemfiles/rails.5.0.activerecord.gemfile +0 -17
  264. data/gemfiles/rails.5.0.activerecord.kaminari.gemfile +0 -16
  265. data/gemfiles/rails.5.0.activerecord.will_paginate.gemfile +0 -16
  266. data/gemfiles/sequel.4.38.gemfile +0 -14
  267. data/lib/chewy/journal/apply.rb +0 -31
  268. data/lib/chewy/journal/clean.rb +0 -24
  269. data/lib/chewy/journal/entry.rb +0 -83
  270. data/lib/chewy/journal/query.rb +0 -87
  271. data/lib/chewy/query/pagination/will_paginate.rb +0 -27
  272. data/lib/chewy/query/scoping.rb +0 -20
  273. data/spec/chewy/journal/apply_spec.rb +0 -120
  274. data/spec/chewy/journal/entry_spec.rb +0 -237
  275. data/spec/chewy/query/pagination/will_paginage_spec.rb +0 -59
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Stash::Journal, :orm do
4
+ def fetch_deleted_number(response)
5
+ response['deleted'] || response['_indices']['_all']['deleted']
6
+ end
7
+
8
+ before { Chewy.massacre }
9
+
10
+ before do
11
+ stub_model(:city)
12
+ stub_index(:places) do
13
+ define_type City
14
+ define_type :country
15
+ end
16
+ stub_index(:users) { define_type :user }
17
+ stub_index(:borogoves)
18
+ end
19
+
20
+ before { Timecop.freeze }
21
+ after { Timecop.return }
22
+
23
+ before do
24
+ PlacesIndex::City.import!(City.new(id: 1, name: 'City'), journal: true)
25
+ Timecop.travel(Time.now + 1.minute) do
26
+ PlacesIndex::Country.import!([id: 2, name: 'Country'], journal: true)
27
+ end
28
+ Timecop.travel(Time.now + 2.minutes) do
29
+ UsersIndex::User.import!([id: 3, name: 'User'], journal: true)
30
+ end
31
+ end
32
+
33
+ describe '.entries' do
34
+ specify do
35
+ expect(described_class.entries(Time.now - 30.seconds).map(&:references))
36
+ .to contain_exactly([1], [{'id' => 2, 'name' => 'Country'}], [{'id' => 3, 'name' => 'User'}])
37
+ end
38
+ specify do
39
+ expect(described_class.entries(Time.now + 30.seconds).map(&:references))
40
+ .to contain_exactly([{'id' => 2, 'name' => 'Country'}], [{'id' => 3, 'name' => 'User'}])
41
+ end
42
+ specify do
43
+ expect(described_class.entries(Time.now + 90.seconds).map(&:references))
44
+ .to contain_exactly([{'id' => 3, 'name' => 'User'}])
45
+ end
46
+
47
+ specify do
48
+ expect(described_class.entries(Time.now - 30.seconds, only: UsersIndex).map(&:references))
49
+ .to contain_exactly([{'id' => 3, 'name' => 'User'}])
50
+ end
51
+ specify do
52
+ expect(described_class.entries(Time.now - 30.seconds, only: [PlacesIndex::City, UsersIndex]).map(&:references))
53
+ .to contain_exactly([1], [{'id' => 3, 'name' => 'User'}])
54
+ end
55
+ specify do
56
+ expect(described_class.entries(Time.now + 30.seconds, only: [PlacesIndex::City, UsersIndex]).map(&:references))
57
+ .to contain_exactly([{'id' => 3, 'name' => 'User'}])
58
+ end
59
+ specify do
60
+ expect(described_class.entries(Time.now + 30.seconds, only: [BorogovesIndex])).to eq([])
61
+ end
62
+ end
63
+
64
+ describe '.clean' do
65
+ specify { expect(fetch_deleted_number(described_class.clean)).to eq(3) }
66
+ specify { expect(fetch_deleted_number(described_class.clean(Time.now - 30.seconds))).to eq(0) }
67
+ specify { expect(fetch_deleted_number(described_class.clean(Time.now + 30.seconds))).to eq(1) }
68
+ specify { expect(fetch_deleted_number(described_class.clean(Time.now + 90.seconds))).to eq(2) }
69
+ specify { expect(fetch_deleted_number(described_class.clean(only: BorogovesIndex))).to eq(0) }
70
+ specify { expect(fetch_deleted_number(described_class.clean(only: UsersIndex))).to eq(1) }
71
+ specify { expect(fetch_deleted_number(described_class.clean(only: [PlacesIndex::City, UsersIndex]))).to eq(2) }
72
+
73
+ specify { expect(fetch_deleted_number(described_class.clean(Time.now + 30.seconds, only: PlacesIndex::Country))).to eq(0) }
74
+ specify { expect(fetch_deleted_number(described_class.clean(Time.now + 30.seconds, only: PlacesIndex::City))).to eq(1) }
75
+ end
76
+
77
+ describe '.for' do
78
+ specify { expect(described_class.for(UsersIndex).map(&:index_name)).to eq(['users']) }
79
+ specify { expect(described_class.for(PlacesIndex).map(&:type_name)).to contain_exactly('city', 'country') }
80
+ specify { expect(described_class.for(PlacesIndex::City, UsersIndex).map(&:index_name)).to contain_exactly('places', 'users') }
81
+ end
82
+
83
+ describe '#type' do
84
+ let(:index_name) { 'users' }
85
+ let(:type_name) { 'city' }
86
+ subject { described_class::Journal.new('index_name' => index_name, 'type_name' => type_name).type }
87
+
88
+ specify { expect { subject }.to raise_error(Chewy::UnderivableType) }
89
+
90
+ context do
91
+ let(:index_name) { 'places' }
92
+ it { is_expected.to eq(PlacesIndex::City) }
93
+ end
94
+ end
95
+ end
@@ -2,7 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  if defined?(::ActiveJob)
4
4
  describe Chewy::Strategy::ActiveJob do
5
- around { |example| Chewy.strategy(:bypass) { example.run } }
5
+ around do |example|
6
+ active_job_settings = Chewy.settings[:active_job]
7
+ Chewy.settings[:active_job] = {queue: 'low'}
8
+ Chewy.strategy(:bypass) { example.run }
9
+ Chewy.settings[:active_job] = active_job_settings
10
+ end
6
11
  before(:all) do
7
12
  ::ActiveJob::Base.logger = Chewy.logger
8
13
  end
@@ -36,7 +41,15 @@ if defined?(::ActiveJob)
36
41
  end
37
42
  enqueued_job = ::ActiveJob::Base.queue_adapter.enqueued_jobs.first
38
43
  expect(enqueued_job[:job]).to eq(Chewy::Strategy::ActiveJob::Worker)
39
- expect(enqueued_job[:queue]).to eq('chewy')
44
+ expect(enqueued_job[:queue]).to eq('low')
45
+ end
46
+
47
+ specify do
48
+ Chewy.strategy(:active_job) do
49
+ [city, other_city].map(&:save!)
50
+ end
51
+ enqueued_job = ::ActiveJob::Base.queue_adapter.enqueued_jobs.first
52
+ expect(enqueued_job[:queue]).to eq('low')
40
53
  end
41
54
 
42
55
  specify do
@@ -50,5 +63,11 @@ if defined?(::ActiveJob)
50
63
  expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601')
51
64
  Chewy::Strategy::ActiveJob::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601')
52
65
  end
66
+
67
+ specify do
68
+ allow(Chewy).to receive(:disable_refresh_async).and_return(true)
69
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false)
70
+ Chewy::Strategy::ActiveJob::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601')
71
+ end
53
72
  end
54
73
  end
@@ -36,5 +36,11 @@ if defined?(::Resque)
36
36
  expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601')
37
37
  Chewy::Strategy::Resque::Worker.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601')
38
38
  end
39
+
40
+ specify do
41
+ allow(Chewy).to receive(:disable_refresh_async).and_return(true)
42
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false)
43
+ Chewy::Strategy::Resque::Worker.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601')
44
+ end
39
45
  end
40
46
  end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(::Shoryuken)
4
+ require 'aws-sdk-sqs'
5
+
6
+ describe Chewy::Strategy::Shoryuken do
7
+ around do |example|
8
+ shoryuken_settings = Chewy.settings[:shoryuken]
9
+ Chewy.settings[:shoryuken] = {queue: 'low'}
10
+ Chewy.strategy(:bypass) { example.run }
11
+ Chewy.settings[:shoryuken] = shoryuken_settings
12
+ end
13
+ before { ::Shoryuken.groups.clear }
14
+ before do
15
+ stub_model(:city) do
16
+ update_index('cities#city') { self }
17
+ end
18
+
19
+ stub_index(:cities) do
20
+ define_type City
21
+ end
22
+ end
23
+
24
+ let(:city) { City.create!(name: 'hello') }
25
+ let(:other_city) { City.create!(name: 'world') }
26
+ let(:queue) { instance_double(::Shoryuken::Queue) }
27
+
28
+ before do
29
+ allow(::Shoryuken::Queue).to receive(:new).and_return(queue)
30
+ allow(queue).to receive(:send_message).and_return(nil)
31
+ end
32
+
33
+ specify do
34
+ expect { [city, other_city].map(&:save!) }
35
+ .not_to update_index(CitiesIndex::City, strategy: :shoryuken)
36
+ end
37
+
38
+ specify do
39
+ expect(Chewy::Strategy::Shoryuken::Worker).to receive(:perform_async)
40
+ .with(hash_including(type: 'CitiesIndex::City', ids: [city.id, other_city.id]), hash_including(queue: 'low'))
41
+ Chewy.strategy(:shoryuken) do
42
+ [city, other_city].map(&:save!)
43
+ end
44
+ end
45
+
46
+ let(:body) do
47
+ {
48
+ 'type' => 'CitiesIndex::City',
49
+ 'ids' => [city.id, other_city.id],
50
+ 'options' => {'suffix' => '201601'}
51
+ }
52
+ end
53
+ let(:sqs_msg) do
54
+ double id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e',
55
+ body: body,
56
+ delete: nil
57
+ end
58
+
59
+ specify do
60
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601')
61
+ Chewy::Strategy::Shoryuken::Worker.new.perform(sqs_msg, body)
62
+ end
63
+
64
+ specify do
65
+ allow(Chewy).to receive(:disable_refresh_async).and_return(true)
66
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false)
67
+ Chewy::Strategy::Shoryuken::Worker.new.perform(sqs_msg, body)
68
+ end
69
+ end
70
+ end
@@ -4,7 +4,12 @@ if defined?(::Sidekiq)
4
4
  require 'sidekiq/testing'
5
5
 
6
6
  describe Chewy::Strategy::Sidekiq do
7
- around { |example| Chewy.strategy(:bypass) { example.run } }
7
+ around do |example|
8
+ sidekiq_settings = Chewy.settings[:sidekiq]
9
+ Chewy.settings[:sidekiq] = {queue: 'low'}
10
+ Chewy.strategy(:bypass) { example.run }
11
+ Chewy.settings[:sidekiq] = sidekiq_settings
12
+ end
8
13
  before { ::Sidekiq::Worker.clear_all }
9
14
  before do
10
15
  stub_model(:city) do
@@ -25,6 +30,7 @@ if defined?(::Sidekiq)
25
30
  end
26
31
 
27
32
  specify do
33
+ expect(::Sidekiq::Client).to receive(:push).with(hash_including('queue' => 'low')).and_call_original
28
34
  ::Sidekiq::Testing.inline! do
29
35
  expect { [city, other_city].map(&:save!) }
30
36
  .to update_index(CitiesIndex::City, strategy: :sidekiq)
@@ -36,5 +42,11 @@ if defined?(::Sidekiq)
36
42
  expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601')
37
43
  Chewy::Strategy::Sidekiq::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601')
38
44
  end
45
+
46
+ specify do
47
+ allow(Chewy).to receive(:disable_refresh_async).and_return(true)
48
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false)
49
+ Chewy::Strategy::Sidekiq::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601')
50
+ end
39
51
  end
40
52
  end
@@ -64,12 +64,12 @@ describe Chewy::Strategy do
64
64
  around { |example| Chewy.strategy(:bypass) { example.run } }
65
65
 
66
66
  specify do
67
- expect(CitiesIndex::City).not_to receive(:import)
67
+ expect(CitiesIndex::City).not_to receive(:import!)
68
68
  [city, other_city].map(&:save!)
69
69
  end
70
70
 
71
71
  specify do
72
- expect(CitiesIndex::City).to receive(:import).with([city.id, other_city.id]).once
72
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id]).once
73
73
  Chewy.strategy(:atomic) { [city, other_city].map(&:save!) }
74
74
  end
75
75
  end
@@ -78,12 +78,12 @@ describe Chewy::Strategy do
78
78
  around { |example| Chewy.strategy(:urgent) { example.run } }
79
79
 
80
80
  specify do
81
- expect(CitiesIndex::City).to receive(:import).at_least(2).times
81
+ expect(CitiesIndex::City).to receive(:import!).at_least(2).times
82
82
  [city, other_city].map(&:save!)
83
83
  end
84
84
 
85
85
  specify do
86
- expect(CitiesIndex::City).to receive(:import).with([city.id, other_city.id]).once
86
+ expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id]).once
87
87
  Chewy.strategy(:atomic) { [city, other_city].map(&:save!) }
88
88
  end
89
89
 
@@ -94,7 +94,7 @@ describe Chewy::Strategy do
94
94
  end
95
95
 
96
96
  stub_model(:city) do
97
- update_index('cities#city') { { name: name } }
97
+ update_index('cities#city') { {name: name} }
98
98
  end
99
99
  end
100
100
 
@@ -106,7 +106,7 @@ describe Chewy::Strategy do
106
106
  context do
107
107
  before do
108
108
  stub_model(:city) do
109
- update_index('cities#city') { { id: id.to_s, name: name } }
109
+ update_index('cities#city') { {id: id.to_s, name: name} }
110
110
  end
111
111
  end
112
112
 
@@ -5,27 +5,46 @@ describe Chewy::Type::Actions, :orm do
5
5
 
6
6
  before do
7
7
  stub_model(:city)
8
- end
9
-
10
- before do
11
8
  stub_index(:cities) do
12
9
  define_type City do
13
10
  field :name
11
+ field :updated_at, type: 'date'
14
12
  end
15
13
  end
16
14
  end
17
15
 
18
- let!(:dummy_cities) { Array.new(3) { |i| City.create(name: "name#{i}") } }
19
- let(:city) { CitiesIndex::City }
16
+ let!(:cities) { Array.new(3) { |i| City.create!(name: "Name#{i + 1}") } }
17
+ before { CitiesIndex::City.import }
20
18
 
21
- before do
22
- city.import
19
+ describe '.reset' do
20
+ specify do
21
+ expect { CitiesIndex::City.reset }.to update_index(CitiesIndex::City)
22
+ end
23
23
  end
24
24
 
25
- describe '.reset' do
25
+ describe '.sync' do
26
+ before do
27
+ cities.first.destroy
28
+ sleep(1) if ActiveSupport::VERSION::STRING < '4.1.0'
29
+ cities.last.update(name: 'Name5')
30
+ end
31
+ let!(:additional_city) { City.create!(name: 'Name4') }
32
+
26
33
  specify do
27
- skip_on_plugin_missing_from_version('delete-by-query', '2.0')
28
- expect { city.reset }.to update_index(city)
34
+ expect(CitiesIndex::City.sync).to match(
35
+ count: 3,
36
+ missing: contain_exactly(cities.first.id.to_s, additional_city.id.to_s),
37
+ outdated: [cities.last.id.to_s]
38
+ )
29
39
  end
40
+ specify do
41
+ expect { CitiesIndex::City.sync }.to update_index(CitiesIndex::City)
42
+ .and_reindex(additional_city, cities.last)
43
+ .and_delete(cities.first).only
44
+ end
45
+ end
46
+
47
+ describe '.journal' do
48
+ specify { expect(CitiesIndex::City.journal).to be_a(Chewy::Journal) }
30
49
  end
31
50
  end
@@ -1,7 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Chewy::Type::Adapter::ActiveRecord, :active_record do
4
- before { stub_model(:city) }
4
+ before do
5
+ stub_model(:city)
6
+ stub_model(:country)
7
+ City.belongs_to :country
8
+ Country.has_many :cities
9
+ end
5
10
 
6
11
  describe '#name' do
7
12
  specify { expect(described_class.new(City).name).to eq('City') }
@@ -72,73 +77,102 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
72
77
  let!(:deleted) { Array.new(4) { City.create!.tap(&:destroy) } }
73
78
  subject { described_class.new(City) }
74
79
 
75
- specify { expect(import).to eq([{ index: cities }]) }
80
+ specify { expect(import).to eq([{index: cities}]) }
76
81
  specify { expect(import(nil)).to eq([]) }
77
82
 
78
- specify { expect(import(City.order(:id))).to eq([{ index: cities }]) }
83
+ specify { expect(import(City.order(:id))).to eq([{index: cities}]) }
79
84
  specify do
80
85
  expect(import(City.order(:id), batch_size: 2))
81
- .to eq([{ index: cities.first(2) }, { index: cities.last(1) }])
86
+ .to eq([{index: cities.first(2)}, {index: cities.last(1)}])
82
87
  end
83
88
 
84
- specify { expect(import(cities)).to eq([{ index: cities }]) }
89
+ specify { expect(import(cities)).to eq([{index: cities}]) }
85
90
  specify do
86
91
  expect(import(cities, batch_size: 2))
87
- .to eq([{ index: cities.first(2) }, { index: cities.last(1) }])
92
+ .to eq([{index: cities.first(2)}, {index: cities.last(1)}])
88
93
  end
89
94
  specify do
90
95
  expect(import(cities, deleted))
91
- .to eq([{ index: cities }, { delete: deleted }])
96
+ .to eq([{index: cities}, {delete: deleted}])
92
97
  end
93
98
  specify do
94
99
  expect(import(cities, deleted, batch_size: 2)).to eq([
95
- { index: cities.first(2) },
96
- { index: cities.last(1) },
97
- { delete: deleted.first(2) },
98
- { delete: deleted.last(2) }
100
+ {index: cities.first(2)},
101
+ {index: cities.last(1)},
102
+ {delete: deleted.first(2)},
103
+ {delete: deleted.last(2)}
99
104
  ])
100
105
  end
101
106
 
102
- specify { expect(import(cities.map(&:id))).to eq([{ index: cities }]) }
103
- specify { expect(import(deleted.map(&:id))).to eq([{ delete: deleted.map(&:id) }]) }
107
+ specify { expect(import(cities.map(&:id))).to eq([{index: cities}]) }
108
+ specify { expect(import(deleted.map(&:id))).to eq([{delete: deleted.map(&:id)}]) }
104
109
  specify do
105
110
  expect(import(cities.map(&:id), batch_size: 2))
106
- .to eq([{ index: cities.first(2) }, { index: cities.last(1) }])
111
+ .to eq([{index: cities.first(2)}, {index: cities.last(1)}])
107
112
  end
108
113
  specify do
109
114
  expect(import(cities.map(&:id), deleted.map(&:id)))
110
- .to eq([{ index: cities }, { delete: deleted.map(&:id) }])
115
+ .to eq([{index: cities}, {delete: deleted.map(&:id)}])
111
116
  end
112
117
  specify do
113
118
  expect(import(cities.map(&:id), deleted.map(&:id), batch_size: 2)).to eq([
114
- { index: cities.first(2) },
115
- { index: cities.last(1) },
116
- { delete: deleted.first(2).map(&:id) },
117
- { delete: deleted.last(2).map(&:id) }
119
+ {index: cities.first(2)},
120
+ {index: cities.last(1)},
121
+ {delete: deleted.first(2).map(&:id)},
122
+ {delete: deleted.last(2).map(&:id)}
118
123
  ])
119
124
  end
120
125
 
121
- specify { expect(import(cities.first, nil)).to eq([{ index: [cities.first] }]) }
122
- specify { expect(import(cities.first.id, nil)).to eq([{ index: [cities.first] }]) }
126
+ specify { expect(import(cities.first, nil)).to eq([{index: [cities.first]}]) }
127
+ specify { expect(import(cities.first.id, nil)).to eq([{index: [cities.first]}]) }
123
128
 
124
129
  context 'raw_import' do
125
- let(:probe) { double }
126
- let(:converter) { ->(raw_hash) { probe.call(raw_hash) } }
127
- let(:moscow) { OpenStruct.new(id: 1, name: 'Moscow') }
128
- let(:warsaw) { OpenStruct.new(id: 2, name: 'Warsaw') }
129
- let(:madrid) { OpenStruct.new(id: 3, name: 'Madrid') }
130
130
  before do
131
- @one, @two, @three = City.all.to_a
131
+ stub_class(:dummy_city) do
132
+ def initialize(attributes = {})
133
+ @attributes = attributes
134
+ end
135
+
136
+ def method_missing(name, *args, &block)
137
+ if @attributes.key?(name.to_s)
138
+ @attributes[name.to_s]
139
+ else
140
+ super
141
+ end
142
+ end
143
+
144
+ def respond_to_missing?(name, _)
145
+ @attributes.key?(name.to_s)
146
+ end
147
+ end
132
148
  end
149
+ let!(:cities) { Array.new(3) { |i| City.create!(id: i + 1, name: "City#{i + 1}") } }
150
+ let(:converter) { ->(hash) { DummyCity.new(hash) } }
133
151
 
134
152
  it 'uses the raw import converter to make objects out of raw hashes from the database' do
135
153
  expect(City).not_to receive(:new)
136
154
 
137
- expect(probe).to receive(:call).with(a_hash_including('id' => @one.id, 'name' => @one.name)).and_return(moscow)
138
- expect(probe).to receive(:call).with(a_hash_including('id' => @two.id, 'name' => @one.name)).and_return(warsaw)
139
- expect(probe).to receive(:call).with(a_hash_including('id' => @three.id, 'name' => @three.name)).and_return(madrid)
155
+ expect(import(City.where(nil), raw_import: converter)).to match([{index: match_array([
156
+ an_instance_of(DummyCity).and(have_attributes(id: 1, name: 'City1')),
157
+ an_instance_of(DummyCity).and(have_attributes(id: 2, name: 'City2')),
158
+ an_instance_of(DummyCity).and(have_attributes(id: 3, name: 'City3'))
159
+ ])}])
160
+ end
161
+
162
+ specify do
163
+ expect(import([1, 2, 3], raw_import: converter)).to match([{index: match_array([
164
+ an_instance_of(DummyCity).and(have_attributes(id: 1, name: 'City1')),
165
+ an_instance_of(DummyCity).and(have_attributes(id: 2, name: 'City2')),
166
+ an_instance_of(DummyCity).and(have_attributes(id: 3, name: 'City3'))
167
+ ])}])
168
+ end
140
169
 
141
- expect(import(City.where(nil), raw_import: converter)).to eq([{ index: [moscow, warsaw, madrid] }])
170
+ specify do
171
+ expect(import(cities, raw_import: converter)).to match([{index: match_array([
172
+ an_instance_of(DummyCity).and(have_attributes(id: 1, name: 'City1')),
173
+ an_instance_of(DummyCity).and(have_attributes(id: 2, name: 'City2')),
174
+ an_instance_of(DummyCity).and(have_attributes(id: 3, name: 'City3'))
175
+ ])}])
142
176
  end
143
177
  end
144
178
  end
@@ -159,19 +193,19 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
159
193
 
160
194
  specify do
161
195
  expect(import(City.where(nil))).to eq([
162
- { index: [cities[0]], delete: [cities[1]] }
196
+ {index: [cities[0]], delete: [cities[1]]}
163
197
  ])
164
198
  end
165
199
  specify do
166
200
  expect(import(cities)).to eq([
167
- { index: [cities[0]], delete: [cities[1]] },
168
- { delete: cities.last(2) }
201
+ {index: [cities[0]], delete: [cities[1]]},
202
+ {delete: cities.last(2)}
169
203
  ])
170
204
  end
171
205
  specify do
172
206
  expect(import(cities.map(&:id))).to eq([
173
- { index: [cities[0]], delete: [cities[1]] },
174
- { delete: cities.last(2).map(&:id) }
207
+ {index: [cities[0]], delete: [cities[1]]},
208
+ {delete: cities.last(2).map(&:id)}
175
209
  ])
176
210
  end
177
211
  end
@@ -182,47 +216,47 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
182
216
  let!(:deleted) { Array.new(3) { |i| City.create! { |c| c.rating = i + 10 }.tap(&:destroy) } }
183
217
  subject { described_class.new(City) }
184
218
 
185
- specify { expect(import).to eq([{ index: cities }]) }
219
+ specify { expect(import).to eq([{index: cities}]) }
186
220
 
187
- specify { expect(import(City.order(:rating))).to eq([{ index: cities }]) }
221
+ specify { expect(import(City.order(:rating))).to eq([{index: cities}]) }
188
222
  specify do
189
223
  expect(import(City.order(:rating), batch_size: 2))
190
- .to eq([{ index: cities.first(2) }, { index: cities.last(1) }])
224
+ .to eq([{index: cities.first(2)}, {index: cities.last(1)}])
191
225
  end
192
226
 
193
- specify { expect(import(cities)).to eq([{ index: cities }]) }
227
+ specify { expect(import(cities)).to eq([{index: cities}]) }
194
228
  specify do
195
229
  expect(import(cities, batch_size: 2))
196
- .to eq([{ index: cities.first(2) }, { index: cities.last(1) }])
230
+ .to eq([{index: cities.first(2)}, {index: cities.last(1)}])
197
231
  end
198
232
  specify do
199
233
  expect(import(cities, deleted))
200
- .to eq([{ index: cities }, { delete: deleted }])
234
+ .to eq([{index: cities}, {delete: deleted}])
201
235
  end
202
236
  specify do
203
237
  expect(import(cities, deleted, batch_size: 2)).to eq([
204
- { index: cities.first(2) },
205
- { index: cities.last(1) },
206
- { delete: deleted.first(2) },
207
- { delete: deleted.last(1) }
238
+ {index: cities.first(2)},
239
+ {index: cities.last(1)},
240
+ {delete: deleted.first(2)},
241
+ {delete: deleted.last(1)}
208
242
  ])
209
243
  end
210
244
 
211
- specify { expect(import(cities.map(&:id))).to eq([{ index: cities }]) }
245
+ specify { expect(import(cities.map(&:id))).to eq([{index: cities}]) }
212
246
  specify do
213
247
  expect(import(cities.map(&:id), batch_size: 2))
214
- .to eq([{ index: cities.first(2) }, { index: cities.last(1) }])
248
+ .to eq([{index: cities.first(2)}, {index: cities.last(1)}])
215
249
  end
216
250
  specify do
217
251
  expect(import(cities.map(&:id), deleted.map(&:id)))
218
- .to eq([{ index: cities }, { delete: deleted.map(&:id) }])
252
+ .to eq([{index: cities}, {delete: deleted.map(&:id)}])
219
253
  end
220
254
  specify do
221
255
  expect(import(cities.map(&:id), deleted.map(&:id), batch_size: 2)).to eq([
222
- { index: cities.first(2) },
223
- { index: cities.last(1) },
224
- { delete: deleted.first(2).map(&:id) },
225
- { delete: deleted.last(1).map(&:id) }
256
+ {index: cities.first(2)},
257
+ {index: cities.last(1)},
258
+ {delete: deleted.first(2).map(&:id)},
259
+ {delete: deleted.last(1).map(&:id)}
226
260
  ])
227
261
  end
228
262
  end
@@ -232,67 +266,67 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
232
266
  let!(:deleted) { Array.new(3) { City.create!.tap(&:destroy) } }
233
267
  subject { described_class.new(City.where(rating: 0)) }
234
268
 
235
- specify { expect(import).to eq([{ index: cities.first(3) }]) }
269
+ specify { expect(import).to eq([{index: cities.first(3)}]) }
236
270
 
237
271
  specify do
238
272
  expect(import(City.where('rating < 2')))
239
- .to eq([{ index: cities.first(3) }])
273
+ .to eq([{index: cities.first(3)}])
240
274
  end
241
275
  specify do
242
276
  expect(import(City.where('rating < 2'), batch_size: 2))
243
- .to eq([{ index: cities.first(2) }, { index: [cities[2]] }])
277
+ .to eq([{index: cities.first(2)}, {index: [cities[2]]}])
244
278
  end
245
279
  specify do
246
280
  expect(import(City.where('rating < 1')))
247
- .to eq([{ index: cities.first(3) }])
281
+ .to eq([{index: cities.first(3)}])
248
282
  end
249
283
  specify { expect(import(City.where('rating > 1'))).to eq([]) }
250
284
 
251
285
  specify do
252
286
  expect(import(cities.first(2)))
253
- .to eq([{ index: cities.first(2) }])
287
+ .to eq([{index: cities.first(2)}])
254
288
  end
255
289
  specify do
256
290
  expect(import(cities))
257
- .to eq([{ index: cities.first(3) }, { delete: cities.last(1) }])
291
+ .to eq([{index: cities.first(3)}, {delete: cities.last(1)}])
258
292
  end
259
293
  specify do
260
294
  expect(import(cities, batch_size: 2))
261
- .to eq([{ index: cities.first(2) }, { index: [cities[2]] }, { delete: cities.last(1) }])
295
+ .to eq([{index: cities.first(2)}, {index: [cities[2]]}, {delete: cities.last(1)}])
262
296
  end
263
297
  specify do
264
298
  expect(import(cities, deleted))
265
- .to eq([{ index: cities.first(3) }, { delete: cities.last(1) + deleted }])
299
+ .to eq([{index: cities.first(3)}, {delete: cities.last(1) + deleted}])
266
300
  end
267
301
  specify do
268
302
  expect(import(cities, deleted, batch_size: 3)).to eq([
269
- { index: cities.first(3) },
270
- { delete: cities.last(1) + deleted.first(2) },
271
- { delete: deleted.last(1) }
303
+ {index: cities.first(3)},
304
+ {delete: cities.last(1) + deleted.first(2)},
305
+ {delete: deleted.last(1)}
272
306
  ])
273
307
  end
274
308
 
275
309
  specify do
276
310
  expect(import(cities.first(2).map(&:id)))
277
- .to eq([{ index: cities.first(2) }])
311
+ .to eq([{index: cities.first(2)}])
278
312
  end
279
313
  specify do
280
314
  expect(import(cities.map(&:id)))
281
- .to eq([{ index: cities.first(3) }, { delete: [cities.last.id] }])
315
+ .to eq([{index: cities.first(3)}, {delete: [cities.last.id]}])
282
316
  end
283
317
  specify do
284
318
  expect(import(cities.map(&:id), batch_size: 2))
285
- .to eq([{ index: cities.first(2) }, { index: [cities[2]] }, { delete: [cities.last.id] }])
319
+ .to eq([{index: cities.first(2)}, {index: [cities[2]]}, {delete: [cities.last.id]}])
286
320
  end
287
321
  specify do
288
322
  expect(import(cities.map(&:id), deleted.map(&:id)))
289
- .to eq([{ index: cities.first(3) }, { delete: [cities.last.id] + deleted.map(&:id) }])
323
+ .to eq([{index: cities.first(3)}, {delete: [cities.last.id] + deleted.map(&:id)}])
290
324
  end
291
325
  specify do
292
326
  expect(import(cities.map(&:id), deleted.map(&:id), batch_size: 3)).to eq([
293
- { index: cities.first(3) },
294
- { delete: [cities.last.id] + deleted.first(2).map(&:id) },
295
- { delete: deleted.last(1).map(&:id) }
327
+ {index: cities.first(3)},
328
+ {delete: [cities.last.id] + deleted.first(2).map(&:id)},
329
+ {delete: deleted.last(1).map(&:id)}
296
330
  ])
297
331
  end
298
332
  end
@@ -354,35 +388,111 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
354
388
  end
355
389
  end
356
390
 
391
+ describe '#import_fields' do
392
+ subject { described_class.new(Country) }
393
+ let!(:countries) { Array.new(3) { |i| Country.create!(rating: i) { |c| c.id = i + 1 } } }
394
+ let!(:cities) { Array.new(6) { |i| City.create!(rating: i + 3, country_id: (i + 4) / 2) { |c| c.id = i + 3 } } }
395
+
396
+ specify { expect(subject.import_fields).to match([contain_exactly(1, 2, 3)]) }
397
+ specify { expect(subject.import_fields(fields: [:rating])).to match([contain_exactly([1, 0], [2, 1], [3, 2])]) }
398
+
399
+ context 'scopes' do
400
+ context do
401
+ subject { described_class.new(Country.includes(:cities)) }
402
+
403
+ specify { expect(subject.import_fields).to match([contain_exactly(1, 2, 3)]) }
404
+ specify { expect(subject.import_fields(fields: [:rating])).to match([contain_exactly([1, 0], [2, 1], [3, 2])]) }
405
+ end
406
+
407
+ context do
408
+ subject { described_class.new(Country.joins(:cities)) }
409
+
410
+ specify { expect(subject.import_fields).to match([contain_exactly(2, 3)]) }
411
+ specify { expect(subject.import_fields(fields: [:rating])).to match([contain_exactly([2, 1], [3, 2])]) }
412
+ end
413
+
414
+ context 'ignores default scope if another scope is passed' do
415
+ subject { described_class.new(Country.joins(:cities)) }
416
+
417
+ specify { expect(subject.import_fields(Country.where('rating < 2'))).to match([contain_exactly(1, 2)]) }
418
+ specify { expect(subject.import_fields(Country.where('rating < 2'), fields: [:rating])).to match([contain_exactly([1, 0], [2, 1])]) }
419
+ end
420
+ end
421
+
422
+ context 'objects/ids' do
423
+ specify { expect(subject.import_fields(1, 2)).to match([contain_exactly(1, 2)]) }
424
+ specify { expect(subject.import_fields(1, 2, fields: [:rating])).to match([contain_exactly([1, 0], [2, 1])]) }
425
+
426
+ specify { expect(subject.import_fields(countries.first(2))).to match([contain_exactly(1, 2)]) }
427
+ specify { expect(subject.import_fields(countries.first(2), fields: [:rating])).to match([contain_exactly([1, 0], [2, 1])]) }
428
+ end
429
+
430
+ context 'batch_size' do
431
+ specify { expect(subject.import_fields(batch_size: 2)).to match([contain_exactly(1, 2), [3]]) }
432
+ specify { expect(subject.import_fields(batch_size: 2, fields: [:rating])).to match([contain_exactly([1, 0], [2, 1]), [[3, 2]]]) }
433
+
434
+ specify { expect(subject.import_fields(Country.where('rating < 2'), batch_size: 2)).to match([contain_exactly(1, 2)]) }
435
+ specify { expect(subject.import_fields(Country.where('rating < 2'), batch_size: 2, fields: [:rating])).to match([contain_exactly([1, 0], [2, 1])]) }
436
+
437
+ specify { expect(subject.import_fields(1, 2, batch_size: 1)).to match([[1], [2]]) }
438
+ specify { expect(subject.import_fields(1, 2, batch_size: 1, fields: [:rating])).to match([[[1, 0]], [[2, 1]]]) }
439
+
440
+ specify { expect(subject.import_fields(countries.first(2), batch_size: 1)).to match([[1], [2]]) }
441
+ specify { expect(subject.import_fields(countries.first(2), batch_size: 1, fields: [:rating])).to match([[[1, 0]], [[2, 1]]]) }
442
+ end
443
+
444
+ context 'typecast' do
445
+ specify { expect(subject.import_fields(typecast: false)).to match([contain_exactly(1, 2, 3)]) }
446
+ specify do
447
+ expect(subject.import_fields(fields: [:updated_at]).to_a)
448
+ .to match([contain_exactly(
449
+ [1, an_instance_of(Time)],
450
+ [2, an_instance_of(Time)],
451
+ [3, an_instance_of(Time)]
452
+ )])
453
+ end
454
+ specify do
455
+ expect(subject.import_fields(fields: [:updated_at], typecast: false))
456
+ .to match([contain_exactly(
457
+ [1, match(/#{Time.now.utc.strftime('%Y-%m-%d')}/)],
458
+ [2, match(/#{Time.now.utc.strftime('%Y-%m-%d')}/)],
459
+ [3, match(/#{Time.now.utc.strftime('%Y-%m-%d')}/)]
460
+ )])
461
+ end
462
+ end
463
+ end
464
+
357
465
  describe '#load' do
358
466
  context do
359
467
  let!(:cities) { Array.new(3) { |i| City.create!(rating: i / 2) } }
360
468
  let!(:deleted) { Array.new(2) { City.create!.tap(&:destroy) } }
469
+ let(:city_ids) { cities.map(&:id) }
470
+ let(:deleted_ids) { deleted.map(&:id) }
361
471
 
362
472
  let(:type) { double(type_name: 'user') }
363
473
 
364
474
  subject { described_class.new(City) }
365
475
 
366
- specify { expect(subject.load(cities.map { |c| double(id: c.id) }, _type: type)).to eq(cities) }
367
- specify { expect(subject.load(cities.map { |c| double(id: c.id) }.reverse, _type: type)).to eq(cities.reverse) }
368
- specify { expect(subject.load(deleted.map { |c| double(id: c.id) }, _type: type)).to eq([nil, nil]) }
369
- specify { expect(subject.load((cities + deleted).map { |c| double(id: c.id) }, _type: type)).to eq([*cities, nil, nil]) }
476
+ specify { expect(subject.load(city_ids, _type: type)).to eq(cities) }
477
+ specify { expect(subject.load(city_ids.reverse, _type: type)).to eq(cities.reverse) }
478
+ specify { expect(subject.load(deleted_ids, _type: type)).to eq([nil, nil]) }
479
+ specify { expect(subject.load(city_ids + deleted_ids, _type: type)).to eq([*cities, nil, nil]) }
370
480
  specify do
371
- expect(subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: -> { where(rating: 0) }))
481
+ expect(subject.load(city_ids, _type: type, scope: -> { where(rating: 0) }))
372
482
  .to eq(cities.first(2) + [nil])
373
483
  end
374
484
  specify do
375
- expect(subject.load(cities.map { |c| double(id: c.id) },
376
- _type: type, scope: -> { where(rating: 0) }, user: { scope: -> { where(rating: 1) } }))
485
+ expect(subject.load(city_ids,
486
+ _type: type, scope: -> { where(rating: 0) }, user: {scope: -> { where(rating: 1) }}))
377
487
  .to eq([nil, nil] + cities.last(1))
378
488
  end
379
489
  specify do
380
- expect(subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: City.where(rating: 1)))
490
+ expect(subject.load(city_ids, _type: type, scope: City.where(rating: 1)))
381
491
  .to eq([nil, nil] + cities.last(1))
382
492
  end
383
493
  specify do
384
- expect(subject.load(cities.map { |c| double(id: c.id) },
385
- _type: type, scope: City.where(rating: 1), user: { scope: -> { where(rating: 0) } }))
494
+ expect(subject.load(city_ids,
495
+ _type: type, scope: City.where(rating: 1), user: {scope: -> { where(rating: 0) }}))
386
496
  .to eq(cities.first(2) + [nil])
387
497
  end
388
498
  end
@@ -391,31 +501,33 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
391
501
  before { stub_model(:city) { self.primary_key = 'rating' } }
392
502
  let!(:cities) { Array.new(3) { |i| City.create!(country_id: i / 2) { |c| c.rating = i + 7 } } }
393
503
  let!(:deleted) { Array.new(2) { |i| City.create! { |c| c.rating = i + 10 }.tap(&:destroy) } }
504
+ let(:city_ids) { cities.map(&:rating) }
505
+ let(:deleted_ids) { deleted.map(&:rating) }
394
506
 
395
507
  let(:type) { double(type_name: 'user') }
396
508
 
397
509
  subject { described_class.new(City) }
398
510
 
399
- specify { expect(subject.load(cities.map { |c| double(id: c.id) }, _type: type)).to eq(cities) }
400
- specify { expect(subject.load(cities.map { |c| double(id: c.id) }.reverse, _type: type)).to eq(cities.reverse) }
401
- specify { expect(subject.load(deleted.map { |c| double(id: c.id) }, _type: type)).to eq([nil, nil]) }
402
- specify { expect(subject.load((cities + deleted).map { |c| double(id: c.id) }, _type: type)).to eq([*cities, nil, nil]) }
511
+ specify { expect(subject.load(city_ids, _type: type)).to eq(cities) }
512
+ specify { expect(subject.load(city_ids.reverse, _type: type)).to eq(cities.reverse) }
513
+ specify { expect(subject.load(deleted_ids, _type: type)).to eq([nil, nil]) }
514
+ specify { expect(subject.load(city_ids + deleted_ids, _type: type)).to eq([*cities, nil, nil]) }
403
515
  specify do
404
- expect(subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: -> { where(country_id: 0) }))
516
+ expect(subject.load(city_ids, _type: type, scope: -> { where(country_id: 0) }))
405
517
  .to eq(cities.first(2) + [nil])
406
518
  end
407
519
  specify do
408
- expect(subject.load(cities.map { |c| double(id: c.id) },
409
- _type: type, scope: -> { where(country_id: 0) }, user: { scope: -> { where(country_id: 1) } }))
520
+ expect(subject.load(city_ids,
521
+ _type: type, scope: -> { where(country_id: 0) }, user: {scope: -> { where(country_id: 1) }}))
410
522
  .to eq([nil, nil] + cities.last(1))
411
523
  end
412
524
  specify do
413
- expect(subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: City.where(country_id: 1)))
525
+ expect(subject.load(city_ids, _type: type, scope: City.where(country_id: 1)))
414
526
  .to eq([nil, nil] + cities.last(1))
415
527
  end
416
528
  specify do
417
- expect(subject.load(cities.map { |c| double(id: c.id) },
418
- _type: type, scope: City.where(country_id: 1), user: { scope: -> { where(country_id: 0) } }))
529
+ expect(subject.load(city_ids,
530
+ _type: type, scope: City.where(country_id: 1), user: {scope: -> { where(country_id: 0) }}))
419
531
  .to eq(cities.first(2) + [nil])
420
532
  end
421
533
  end