chewy 0.8.2 → 0.8.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 465b8f0c2bff702e61235bbd9b6782f3f57296e6
4
- data.tar.gz: 60827a551d9b4338ee87a91c346b84cfbcca6aa2
3
+ metadata.gz: 366a83f019d42b9edab700ebfa189d949f5937fb
4
+ data.tar.gz: 65c60e5fa14b6f96b9cd13e49ba752ad64b7a271
5
5
  SHA512:
6
- metadata.gz: e0efb611f827fa291ea9f3765d14e50450e6787f0541e9c04f034d79e20189cdb8dd718e2c8c8074e7736ac3575018e13fe9317f880d7b6d0429ac1ab060e6b2
7
- data.tar.gz: 0270d359d7831c3f1c0217369f32d5017c7b90e7871780a5e79f7945414c7a3aaadf39f1505ab85c7a3c61a1f2fbb28ae49c183034f43c12d1ed065cf66e9acf
6
+ metadata.gz: dbee3534a08ddc5c0a3080d159602c1d1d334657abc321f5ef87fdfdc2729e13a97febd9686193f578b1845b083556893865df05dbd058cfa9874819af46d3b8
7
+ data.tar.gz: 0257820ba35319dc0b8a1bd9b21f15acec62ca57701abbf35a49f0fc37a5999ee0875fc8dc043d1bc844a44ca0b7f36b636c3408ff2db01d35e78124e229b4e1
@@ -29,7 +29,7 @@ gemfile:
29
29
  - gemfiles/rails.4.2.mongoid.gemfile
30
30
  - gemfiles/rails.4.2.mongoid.kaminari.gemfile
31
31
  - gemfiles/rails.4.2.mongoid.will_paginate.gemfile
32
- - gemfiles/sequel.4.23.gemfile
32
+ - gemfiles/sequel.4.28.gemfile
33
33
  matrix:
34
34
  exclude:
35
35
  - rvm: 2.2
data/Appraisals CHANGED
@@ -43,7 +43,7 @@ end
43
43
  end
44
44
  end
45
45
 
46
- appraise "sequel.4.23" do
47
- gem 'sequel', "~> 4.23.0"
46
+ appraise "sequel.4.28" do
47
+ gem 'sequel', "~> 4.28.0"
48
48
  gem 'activesupport', '~> 4.2.0'
49
49
  end
@@ -1,5 +1,27 @@
1
1
  # master
2
2
 
3
+ ## Changes
4
+
5
+ * Sequel support completely reworked to use common ORM implementations + better sequel specs covarage.
6
+
7
+ ## Bugfixes
8
+
9
+ * Sequel objects transactional destruction fix
10
+
11
+ * Correct Rspec mocking framework checking (@mainameiz)
12
+
13
+ * Atomic strategy is now compatible with custom ids proc.
14
+
15
+ * Safe unsubscribe on import (@marshall-lee)
16
+
17
+ * Correct custom assets path silencer (@davekaro)
18
+
19
+ ## Incompatible changes:
20
+
21
+ * `Chewy.atomic` and `Chewy.urgent_update=` methods was removed from the codebase, use `Chewy.strategy` block instead.
22
+
23
+ * `delete_from_index?` hook is removed from the codebase.
24
+
3
25
  # Version 0.8.2
4
26
 
5
27
  ## Changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sequel", "~> 4.23.0"
5
+ gem "sequel", "~> 4.28.0"
6
6
  gem "activesupport", "~> 4.2.0"
7
7
 
8
8
  group :test do
@@ -179,20 +179,6 @@ module Chewy
179
179
  end
180
180
  end
181
181
 
182
- def urgent_update= value
183
- ActiveSupport::Deprecation.warn('`Chewy.urgent_update = value` is deprecated and will be removed soon, use `Chewy.strategy(:urgent)` block instead')
184
- if value
185
- strategy(:urgent)
186
- else
187
- strategy.pop
188
- end
189
- end
190
-
191
- def atomic &block
192
- ActiveSupport::Deprecation.warn('`Chewy.atomic` block is deprecated and will be removed soon, use `Chewy.strategy(:atomic)` block instead')
193
- strategy(:atomic, &block)
194
- end
195
-
196
182
  def config
197
183
  Chewy::Config.instance
198
184
  end
@@ -10,7 +10,7 @@ module Chewy
10
10
  end
11
11
 
12
12
  def call(env)
13
- if env['PATH_INFO'].start_with?('/assets/')
13
+ if env['PATH_INFO'].start_with?(Rails.application.config.assets.prefix)
14
14
  @app.call(env)
15
15
  else
16
16
  Chewy.strategy(Chewy.request_strategy) { @app.call(env) }
@@ -194,7 +194,7 @@ RSpec::Matchers.define :update_index do |type_name, options = {}|
194
194
  end
195
195
 
196
196
  def agnostic_stub
197
- if defined? Mocha
197
+ if defined?(Mocha) && RSpec.configuration.mock_framework.to_s == 'RSpec::Core::MockingAdapters::Mocha'
198
198
  "type.stubs(:bulk).with"
199
199
  else
200
200
  "allow(type).to receive(:bulk)"
@@ -20,7 +20,7 @@ module Chewy
20
20
  ActiveSupport::Deprecation.warn("`urgent: true` option is deprecated and is not effective inside `:atomic` strategy, use `Chewy.strategy(:urgent)` strategy instead") if options.key?(:urgent)
21
21
 
22
22
  @stash[type] ||= []
23
- @stash[type] |= type.adapter.identify(objects)
23
+ @stash[type] |= type.send(:build_root).id ? Array.wrap(objects) : type.adapter.identify(objects)
24
24
  end
25
25
 
26
26
  def leave
@@ -37,6 +37,10 @@ module Chewy
37
37
  result
38
38
  end
39
39
 
40
+ def target_id
41
+ target.arel_table[target.primary_key]
42
+ end
43
+
40
44
  def pluck_ids(scope)
41
45
  scope.except(:includes).uniq.pluck(target.primary_key.to_sym)
42
46
  end
@@ -45,14 +49,6 @@ module Chewy
45
49
  scope.where(target_id.in(Array.wrap(ids)))
46
50
  end
47
51
 
48
- def all_scope
49
- target.where(nil)
50
- end
51
-
52
- def target_id
53
- target.arel_table[target.primary_key]
54
- end
55
-
56
52
  def relation_class
57
53
  ::ActiveRecord::Relation
58
54
  end
@@ -60,17 +60,12 @@ module Chewy
60
60
  private
61
61
 
62
62
  def grouped_objects(objects)
63
- objects.group_by do |object|
63
+ objects.to_a.group_by do |object|
64
64
  delete_from_index?(object) ? :delete : :index
65
65
  end
66
66
  end
67
67
 
68
68
  def delete_from_index?(object)
69
- if object.respond_to?(:delete_from_index?)
70
- ActiveSupport::Deprecation.warn('`delete_from_index?` method in models is deprecated and will be removed soon. Use per-type `delete_if` option for `define_type`')
71
- delete = object.delete_from_index?
72
- end
73
-
74
69
  delete_if = options[:delete_if]
75
70
  delete ||= case delete_if
76
71
  when Symbol, String
@@ -22,9 +22,9 @@ module Chewy
22
22
  Chewy.logger.warn('Default type scope order, limit and offset are ignored and will be nullified')
23
23
  end
24
24
 
25
- @default_scope = @default_scope.reorder(nil)
26
25
  @default_scope.options.delete(:limit)
27
26
  @default_scope.options.delete(:skip)
27
+ @default_scope = @default_scope.reorder(nil)
28
28
  end
29
29
 
30
30
  def import_scope(scope, batch_size)
@@ -10,7 +10,7 @@ module Chewy
10
10
  @options = args.extract_options!
11
11
  class_or_relation = args.first
12
12
  if class_or_relation.is_a?(relation_class)
13
- @target = class_or_relation.klass
13
+ @target = model_of_relation(class_or_relation)
14
14
  @default_scope = class_or_relation
15
15
  else
16
16
  @target = class_or_relation
@@ -20,7 +20,7 @@ module Chewy
20
20
  end
21
21
 
22
22
  def name
23
- @name ||= (options[:name].present? ? options[:name].to_s.camelize : target.model_name.to_s).demodulize
23
+ @name ||= (options[:name].presence || target.name).to_s.camelize.demodulize
24
24
  end
25
25
 
26
26
  def identify collection
@@ -28,7 +28,7 @@ module Chewy
28
28
  pluck_ids(collection)
29
29
  else
30
30
  Array.wrap(collection).map do |entity|
31
- entity.is_a?(object_class) ? entity.id : entity
31
+ entity.is_a?(object_class) ? entity.public_send(primary_key) : entity
32
32
  end
33
33
  end
34
34
  end
@@ -93,23 +93,23 @@ module Chewy
93
93
 
94
94
  additional_scope = load_options[load_options[:_type].type_name.to_sym].try(:[], :scope) || load_options[:scope]
95
95
 
96
- scope = all_scope_where_ids_in(objects.map(&:id))
96
+ scope = all_scope_where_ids_in(objects.map(&primary_key))
97
97
  loaded_objects = if additional_scope.is_a?(Proc)
98
98
  scope.instance_exec(&additional_scope)
99
- elsif additional_scope.is_a?(relation_class)
99
+ elsif additional_scope.is_a?(relation_class) && scope.respond_to?(:merge)
100
100
  scope.merge(additional_scope)
101
101
  else
102
102
  scope
103
- end.index_by { |object| object.id.to_s }
103
+ end.index_by { |object| object.public_send(primary_key).to_s }
104
104
 
105
- objects.map { |object| loaded_objects[object.id.to_s] }
105
+ objects.map { |object| loaded_objects[object.public_send(primary_key).to_s] }
106
106
  end
107
107
 
108
108
  private
109
109
 
110
110
  def import_objects(collection, batch_size)
111
111
  hash = collection.index_by do |entity|
112
- entity.is_a?(object_class) ? entity.id : entity
112
+ entity.is_a?(object_class) ? entity.public_send(primary_key) : entity
113
113
  end
114
114
 
115
115
  indexed = hash.keys.each_slice(batch_size).map do |ids|
@@ -117,7 +117,7 @@ module Chewy
117
117
  if batch.empty?
118
118
  true
119
119
  else
120
- batch.each { |object| hash.delete(object.id) }
120
+ batch.each { |object| hash.delete(object.public_send(primary_key)) }
121
121
  yield grouped_objects(batch)
122
122
  end
123
123
  end.all?
@@ -129,6 +129,10 @@ module Chewy
129
129
  indexed && deleted
130
130
  end
131
131
 
132
+ def primary_key
133
+ :id
134
+ end
135
+
132
136
  def default_scope_where_ids_in(ids)
133
137
  scope_where_ids_in(default_scope, ids)
134
138
  end
@@ -136,6 +140,14 @@ module Chewy
136
140
  def all_scope_where_ids_in(ids)
137
141
  scope_where_ids_in(all_scope, ids)
138
142
  end
143
+
144
+ def all_scope
145
+ target.where(nil)
146
+ end
147
+
148
+ def model_of_relation relation
149
+ relation.klass
150
+ end
139
151
  end
140
152
  end
141
153
  end
@@ -3,121 +3,73 @@ require 'chewy/type/adapter/base'
3
3
  module Chewy
4
4
  class Type
5
5
  module Adapter
6
- class Sequel < Base
7
-
8
- attr_reader :default_dataset
6
+ class Sequel < Orm
7
+ attr_reader :default_scope
8
+ alias_method :default_dataset, :default_scope
9
9
 
10
10
  def self.accepts?(target)
11
11
  defined?(::Sequel::Model) && (
12
- target.is_a?(Class) && target < ::Sequel::Model || target.is_a?(::Sequel::Dataset))
12
+ target.is_a?(Class) && target < ::Sequel::Model ||
13
+ target.is_a?(::Sequel::Dataset))
13
14
  end
14
15
 
15
- def initialize(*args)
16
- @options = args.extract_options!
17
-
18
- if dataset? args.first
19
- dataset = args.first
20
- @target = dataset.model
21
- @default_dataset = dataset.unordered.unlimited
16
+ def identify collection
17
+ if collection.is_a?(relation_class)
18
+ pluck_ids(collection)
22
19
  else
23
- model = args.first
24
- @target = model
25
- @default_dataset = model.where(nil)
26
- end
27
- end
28
-
29
- def name
30
- @name ||= (options[:name].presence || target.name).camelize.demodulize
31
- end
32
-
33
- def identify(obj)
34
- if dataset? obj
35
- obj.select_map(target_pk)
36
- else
37
- Array.wrap(obj).map do |item|
38
- model?(item) ? item.pk : item
20
+ Array.wrap(collection).map do |entity|
21
+ entity.is_a?(object_class) ? entity.public_send(primary_key) : entity
39
22
  end
40
23
  end
41
24
  end
42
25
 
43
- def import(*args, &block)
44
- import_options = args.extract_options!
45
- batch_size = import_options[:batch_size] || BATCH_SIZE
26
+ private
46
27
 
47
- if args.empty?
48
- import_dataset(default_dataset, batch_size, &block)
49
- elsif args.one? && dataset?(args.first)
50
- import_dataset(args.first, batch_size, &block)
51
- else
52
- import_models(args.flatten.compact, batch_size, &block)
28
+ def cleanup_default_scope!
29
+ if Chewy.logger && @default_scope != @default_scope.unordered.unlimited
30
+ Chewy.logger.warn('Default type scope order, limit and offset are ignored and will be nullified')
53
31
  end
54
- end
55
-
56
- def load(*args)
57
- load_options = args.extract_options!
58
- index_ids = args.flatten.map(&:id) # args contains index instances
59
-
60
- type_name = load_options[:_type].type_name.to_sym
61
- additional_scope = load_options[type_name].try(:[], :scope) || load_options[:scope]
62
-
63
- dataset = select_by_ids(target, index_ids)
64
32
 
65
- if additional_scope.is_a?(Proc)
66
- index_ids.map!(&:to_s)
67
- dataset.instance_exec(&additional_scope).to_a.select do |model|
68
- index_ids.include? model.pk.to_s
69
- end
70
- else
71
- dataset.to_a
72
- end
33
+ @default_scope = @default_scope.unordered.unlimited
73
34
  end
74
35
 
75
- private
36
+ def import_scope(scope, batch_size)
37
+ scope = scope.unordered.order(::Sequel.asc(primary_key)).limit(batch_size)
76
38
 
77
- def import_dataset(dataset, batch_size)
78
- dataset = dataset.limit(batch_size)
79
-
80
- dataset.db.transaction(isolation: :committed) do
81
- 0.step(Float::INFINITY, batch_size).lazy
82
- .map { |offset| dataset.offset(offset).to_a }
83
- .take_while(&:any?)
84
- .map { |items| yield grouped_objects(items) }
85
- .reduce(:&)
86
- end
87
- end
39
+ ids = pluck_ids(scope)
40
+ result = true
88
41
 
89
- def import_models(objects, batch_size)
90
- objects_by_id = objects.index_by do |item|
91
- model?(item) ? item.pk : item
42
+ while ids.any?
43
+ result &= yield grouped_objects(default_scope_where_ids_in(ids))
44
+ break if ids.size < batch_size
45
+ ids = pluck_ids(scope.where { |o| o.__send__(primary_key) > ids.last })
92
46
  end
93
47
 
94
- indexed = objects_by_id.keys.each_slice(batch_size).map do |ids|
95
- models = select_by_ids(default_dataset, ids).to_a
96
- models.each { |model| objects_by_id.delete(model.pk) }
97
- models.empty? || yield(grouped_objects(models))
98
- end
48
+ result
49
+ end
99
50
 
100
- deleted = objects_by_id.keys.each_slice(batch_size).map do |ids|
101
- yield delete: objects_by_id.values_at(*ids)
102
- end
51
+ def primary_key
52
+ target.primary_key
53
+ end
103
54
 
104
- indexed.all? && deleted.all?
55
+ def pluck_ids(scope)
56
+ scope.distinct.select_map(primary_key)
105
57
  end
106
58
 
107
- def select_by_ids(dataset, ids)
108
- dataset.where(target_pk => Array.wrap(ids))
59
+ def scope_where_ids_in(scope, ids)
60
+ scope.where(primary_key => Array.wrap(ids))
109
61
  end
110
62
 
111
- def target_pk
112
- target.primary_key
63
+ def model_of_relation relation
64
+ relation.model
113
65
  end
114
66
 
115
- def dataset?(obj)
116
- obj.is_a? ::Sequel::Dataset
67
+ def relation_class
68
+ ::Sequel::Dataset
117
69
  end
118
70
 
119
- def model?(obj)
120
- obj.is_a? ::Sequel::Model
71
+ def object_class
72
+ ::Sequel::Model
121
73
  end
122
74
  end
123
75
  end
@@ -22,11 +22,10 @@ module Chewy
22
22
  bulk_options = import_options.reject { |k, v| ![:refresh, :suffix].include?(k) }.reverse_merge!(refresh: true)
23
23
 
24
24
  index.create!(bulk_options.slice(:suffix)) unless index.exists?
25
- build_root unless self.root_object
26
25
 
27
26
  ActiveSupport::Notifications.instrument 'import_objects.chewy', type: self do |payload|
28
27
  adapter.import(*args, import_options) do |action_objects|
29
- indexed_objects = self.root_object.parent_id && fetch_indexed_objects(action_objects.values.flatten)
28
+ indexed_objects = build_root.parent_id && fetch_indexed_objects(action_objects.values.flatten)
30
29
  body = bulk_body(action_objects, indexed_objects)
31
30
 
32
31
  errors = bulk(bulk_options.merge(body: body)) if body.any?
@@ -57,9 +56,10 @@ module Chewy
57
56
  errors = args.last[:errors]
58
57
  end
59
58
  import *args
60
- ActiveSupport::Notifications.unsubscribe(subscriber)
61
59
  raise Chewy::ImportFailed.new(self, errors) if errors.present?
62
60
  true
61
+ ensure
62
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
63
63
  end
64
64
 
65
65
  # Wraps elasticsearch-ruby client indices bulk method.
@@ -86,8 +86,8 @@ module Chewy
86
86
  def delete_bulk_entry(object, indexed_objects = nil, crutches = nil)
87
87
  entry = {}
88
88
 
89
- if self.root_object.id
90
- entry[:_id] = self.root_object.compose_id(object)
89
+ if root_object.id
90
+ entry[:_id] = root_object.compose_id(object)
91
91
  else
92
92
  entry[:_id] = object.id if object.respond_to?(:id)
93
93
  entry[:_id] ||= object[:id] || object['id'] if object.is_a?(Hash)
@@ -95,7 +95,7 @@ module Chewy
95
95
  entry[:_id] = entry[:_id].to_s if defined?(BSON) && entry[:_id].is_a?(BSON::ObjectId)
96
96
  end
97
97
 
98
- if self.root_object.parent_id
98
+ if root_object.parent_id
99
99
  existing_object = entry[:_id].present? && indexed_objects && indexed_objects[entry[:_id].to_s]
100
100
  entry.merge!(parent: existing_object[:parent]) if existing_object
101
101
  end
@@ -106,8 +106,8 @@ module Chewy
106
106
  def index_bulk_entry(object, indexed_objects = nil, crutches = nil)
107
107
  entry = {}
108
108
 
109
- if self.root_object.id
110
- entry[:_id] = self.root_object.compose_id(object)
109
+ if root_object.id
110
+ entry[:_id] = root_object.compose_id(object)
111
111
  else
112
112
  entry[:_id] = object.id if object.respond_to?(:id)
113
113
  entry[:_id] ||= object[:id] || object['id'] if object.is_a?(Hash)
@@ -115,8 +115,8 @@ module Chewy
115
115
  end
116
116
  entry.delete(:_id) if entry[:_id].blank?
117
117
 
118
- if self.root_object.parent_id
119
- entry[:parent] = self.root_object.compose_parent(object)
118
+ if root_object.parent_id
119
+ entry[:parent] = root_object.compose_parent(object)
120
120
  existing_object = entry[:_id].present? && indexed_objects && indexed_objects[entry[:_id].to_s]
121
121
  end
122
122
 
@@ -150,7 +150,7 @@ module Chewy
150
150
  end
151
151
 
152
152
  def object_data object, crutches = nil
153
- (self.root_object ||= build_root).compose(object, crutches)[type_name.to_sym]
153
+ build_root.compose(object, crutches)[type_name.to_sym]
154
154
  end
155
155
 
156
156
  def extract_errors result