quickery 1.1.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06ee53003d29d3dfa2bf9cd981c5d6f154bb49a8a07538d846acff0b30adb52d
4
- data.tar.gz: 2bf0d7a7e35f9d46ec2fabeacd11f36479f4165c1110b8e75d3336e91329d5f9
3
+ metadata.gz: 5caca192d074a8aa71dd2c6e9bc06a3051b6f7eaec2bc664618af356d41a250d
4
+ data.tar.gz: 9acfccf220d83aa4c0d521ba13b4040241273d513160a7e2c4b4afccdb32de8a
5
5
  SHA512:
6
- metadata.gz: 290d4b4ba0ee7b2f239619231c07ceb848a79d3b858b0b03287644459a67ed5ae826e426b6b291debee23afecaca4221434df1c21fda53bb8d662791f8afda0e
7
- data.tar.gz: 1c3b3f6b7817550bf272e342b88c70964ff8955f9bc73fe81e6c234f0036c19d4ed8278cf6b0ec45346d8dee010842060b51d3e17c8593a753ffb6943dc1fc35
6
+ metadata.gz: 3839d48b10faac8d58778c4373440496781a3c52765478b510b1918441fda4aae743097e65ec4a0841fad599f3dfea3b8279062eb406e4a2a0dd0272885774d5
7
+ data.tar.gz: 29cb2b61d408b61eee767267dce79538aa8039e1de9f85e111f62bb330ad1b395d7c810f4ae2e0065ef1ac37f70094c3e0f564ddda97cf46dde5921432cad066
data/README.md CHANGED
@@ -184,6 +184,168 @@ class Employee < ApplicationRecord
184
184
  end
185
185
  ```
186
186
 
187
+ ## Advanced Usage
188
+
189
+ Quickery defines the following for `ActiveRecord::Base` of which you can optionally override in any of your models for advanced usage, i.e:
190
+
191
+ ```ruby
192
+ class Employee < ApplicationRecord
193
+ belongs_to :branch
194
+
195
+ quickery branch: { company: { name: :branch_company_name } }
196
+
197
+ # this method will be called before an Employee gets created or updated
198
+ # new_values is a Hash of quickery-defined attribute changes; say: `{ :branch_company_name => 'Jollibee' }`
199
+ # i.e. when some_employee.update(branch: some_branch)
200
+ def self.quickery_before_create_or_update(employee, new_values)
201
+ employee.assign_attributes(new_values) # default behaviour of this method
202
+ end
203
+
204
+ # this method will be called before any updates happen on any of the association (that a quickery-defined attribute in this model depends on)
205
+ # i.e. when some_branch.update(company: some_company)
206
+ # i.e. when some_company.update(name: 'New Company Name')
207
+ def self.quickery_before_association_update(employees, record_to_be_updated, new_values)
208
+ employees.update_all(new_values) # default behaviour of this method
209
+ end
210
+
211
+ # this method will be called before any of the association gets destroyed (that a quickery-defined attribute in this model depends on)
212
+ # i.e. when some_branch.destroy
213
+ # i.e. when some_company.destroy
214
+ def self.quickery_before_association_destroy(employees, record_to_be_destroyed, new_values)
215
+ employees.update_all(new_values) # default behaviour of this method
216
+ end
217
+ end
218
+ ```
219
+
220
+ ### Advanced Usage: Background Job
221
+
222
+ ```ruby
223
+ class Employee < ApplicationRecord
224
+ belongs_to :branch
225
+
226
+ quickery branch: { company: { name: :branch_company_name } }
227
+
228
+ def self.quickery_before_create_or_update(employee, new_values)
229
+ employee.assign_attributes(new_values)
230
+ end
231
+
232
+ # because updates can be slow for a very big DB table, then you can move the update logic into a background job
233
+ # you can even batch the updates into a job like below
234
+
235
+ def self.quickery_before_association_update(employees, record_to_be_updated, new_values)
236
+ employees.find_in_batches(batch_size: 2000) do |grouped_employees|
237
+ BatchQuickeryUpdatesJob.perform_later(self.class.to_s, grouped_employees.pluck(:id), new_values)
238
+ end
239
+ end
240
+
241
+ def self.quickery_before_association_destroy(employees, record_to_be_destroyed, new_values)
242
+ employees.find_in_batches(batch_size: 2000) do |grouped_employees|
243
+ BatchQuickeryUpdatesJob.perform_later(self.class.to_s, grouped_employees.pluck(:id), new_values)
244
+ end
245
+ end
246
+ end
247
+
248
+ # app/jobs/batch_quickery_updates_job.rb
249
+ class BatchQuickeryUpdatesJob < ApplicationJob
250
+ # or probably you have a :low_priority queue?
251
+ queue_as :default
252
+
253
+ def perform(model_str, ids, new_values)
254
+ model = model_str.safe_constantize
255
+ model.where(id: ids).update_all(new_values)
256
+ end
257
+ end
258
+ ```
259
+
260
+ ### Advanced Usage: Formatting Values
261
+
262
+ ```ruby
263
+ class Employee < ApplicationRecord
264
+ belongs_to :branch
265
+
266
+ quickery branch: { company: { name: :branch_company_name } }
267
+
268
+ def self.quickery_before_create_or_update(employee, new_values)
269
+ employee.assign_attributes(quickery_format_values(new_values))
270
+ end
271
+
272
+ def self.quickery_before_association_update(employees, record_to_be_updated, new_values)
273
+ employees.update_all(quickery_format_values(new_values))
274
+ end
275
+
276
+ def self.quickery_before_association_destroy(employees, record_to_be_destroyed, new_values)
277
+ employees.update_all(quickery_format_values(new_values))
278
+ end
279
+
280
+ private
281
+
282
+ # example (you can rename this method):
283
+ def self.quickery_format_values(values)
284
+ formatted_values = {}
285
+
286
+ :branch_company_name.tap do |attr|
287
+ # remove trailing white spaces and force-single-space between words, and then capitalise all characters
288
+ formatted_values[attr] = values[attr].squish.upcase if values.has_key? attr
289
+ end
290
+
291
+ :user_first_name.tap do |attr|
292
+ # only save the first 30 characters of user_first_name string
293
+ formatted_values[attr] = values[attr][0...30] if values.has_key? attr
294
+ end
295
+
296
+ formatted_values
297
+ end
298
+ end
299
+ ```
300
+
301
+ ### Advanced Usage: Computed Attributes / Values
302
+
303
+ ```ruby
304
+ class Employee < ApplicationRecord
305
+ belongs_to :branch
306
+
307
+ quickery branch: { company: { name: :branch_company_name } }
308
+
309
+ def self.quickery_before_create_or_update(employee, new_values)
310
+ employee.assign_attributes(quickery_with_computed_values(employee, new_values))
311
+ end
312
+
313
+ def self.quickery_before_association_update(employees, record_to_be_updated, new_values)
314
+ employee.find_each do |employee|
315
+ employee.update!(quickery_with_computed_values(employee, new_values))
316
+ end
317
+ end
318
+
319
+ def self.quickery_before_association_destroy(employees, record_to_be_destroyed, new_values)
320
+ employee.find_each do |employee|
321
+ employee.update!(quickery_with_computed_values(employee, new_values))
322
+ end
323
+ end
324
+
325
+ private
326
+
327
+ # example (you can rename this method):
328
+ def self.quickery_with_computed_values(employee, values)
329
+ with_computed_values = {}
330
+
331
+ if values.has_key?(:user_first_name) || values.has_key?(:user_last_name)
332
+ # concatenate first name and last name
333
+ with_computed_values[:user_full_name] = "#{values[:user_first_name]} #{values[:user_last_name]}".strip
334
+ end
335
+
336
+ # you can add logic that specifically depends on the record like the following:
337
+ if employee.is_current_employee?
338
+ if values.has_key? :branch_company_name
339
+ # concatenate a unique code for the employee: i.e. a value of "11-5-1239"
340
+ with_computed_values[:unique_codename] = "#{employee.branch.company.id}-#{employee.branch.id}-#{employee.id}"
341
+ end
342
+ end
343
+
344
+ with_computed_values
345
+ end
346
+ end
347
+ ```
348
+
187
349
  ## Gotchas
188
350
  * Quickery makes use of Rails model callbacks such as `before_update`. This meant that data-integrity holds unless `update_columns` or `update_column` is used which bypasses model callbacks, or unless any manual SQL update is performed.
189
351
  * Quickery does not automatically update old records existing in the database that were created before you integrate Quickery, or before you add new/more Quickery-attributes for that model. One solution is [`recreate_quickery_cache!`](#recreate_quickery_cache) below.
@@ -194,6 +356,8 @@ end
194
356
 
195
357
  * defines a set of "hidden" Quickery `before_create`, `before_update`, and `before_destroy` callbacks needed by Quickery to perform the "syncing" of attribute values
196
358
 
359
+ * can override `self.quickery_before_create_or_update`, `self.quickery_before_association_update`, `self.quickery_before_association_destroy` for advanced usage such as moving the update logic into a background job, or formatting of the quickery-defined attributes, etc...
360
+
197
361
  #### Class Methods:
198
362
 
199
363
  ##### `quickery(mappings)`
@@ -248,7 +412,6 @@ end
248
412
  * Possibly support two-way mapping of attributes? So that you can do, say... `employee.update!(branch_company_name: 'somenewcompanyname')`
249
413
  * Support `has_many` as currently only `belongs_to` is supported. This would then allow us to cache Array of values.
250
414
  * Support custom-methods-values like [`persistize`](https://github.com/bebanjo/persistize), if it's easy enough to integrate something similar
251
- * Support background-processing like in [`flattery`](https://github.com/evendis/flattery)
252
415
 
253
416
  ## Other Similar Gems
254
417
  See [my detailed comparisons](other_similar_gems_comparison.md)
@@ -273,6 +436,8 @@ See [my detailed comparisons](other_similar_gems_comparison.md)
273
436
  5. Create new Pull Request
274
437
 
275
438
  ## Changelog
439
+ * 1.2.0
440
+ * DONE: (TODO) added overrideable methods for custom callback logic (i.e. move update logic instead into a background job)
276
441
  * 1.1.0
277
442
  * added helper method [`determine_quickery_values`](#determine_quickery_values)
278
443
  * fixed `recreate_quickery_cache!` raising `NilClass` error when the immediate association is nil
@@ -3,5 +3,4 @@ Dir[__dir__ + '/quickery/active_record_extensions/*.rb'].each {|file| require fi
3
3
  Dir[__dir__ + '/quickery/errors/*.rb'].each {|file| require file }
4
4
 
5
5
  module Quickery
6
- # Your code goes here...
7
6
  end
@@ -24,6 +24,8 @@ module Quickery
24
24
  changed_attributes = changes.keys
25
25
 
26
26
  if model.quickery_association_chain_dependers.present?
27
+ new_values = {}.with_indifferent_access
28
+
27
29
  model.quickery_association_chain_dependers.each do |association_chain_depender|
28
30
  quickery_builder = association_chain_depender.quickery_builder
29
31
  depender_column_name = quickery_builder.depender_column_name
@@ -37,9 +39,11 @@ module Quickery
37
39
  new_value = dependee_record.send(dependee_column_name)
38
40
  end
39
41
 
40
- assign_attributes(depender_column_name => new_value)
42
+ new_values[depender_column_name] = new_value
41
43
  end
42
44
  end
45
+
46
+ self.class.quickery_before_create_or_update(self, new_values)
43
47
  end
44
48
  end
45
49
 
@@ -48,6 +52,8 @@ module Quickery
48
52
  changed_attributes = changes.keys
49
53
 
50
54
  if model.quickery_association_chain_dependers.present?
55
+ new_values = {}.with_indifferent_access
56
+
51
57
  model.quickery_association_chain_dependers.each do |association_chain_depender|
52
58
  quickery_builder = association_chain_depender.quickery_builder
53
59
  depender_column_name = quickery_builder.depender_column_name
@@ -61,12 +67,14 @@ module Quickery
61
67
  new_value = dependee_record.send(dependee_column_name)
62
68
  end
63
69
 
64
- assign_attributes(depender_column_name => new_value)
70
+ new_values[depender_column_name] = new_value
65
71
  end
66
72
  end
73
+
74
+ self.class.quickery_before_create_or_update(self, new_values)
67
75
  end
68
76
 
69
- dependent_records_attributes_to_be_updated = {}
77
+ dependent_records_attributes_to_be_updated = {}.with_indifferent_access
70
78
 
71
79
  if model.quickery_association_chain_dependees.present?
72
80
  model.quickery_association_chain_dependees.each do |association_chain_dependee|
@@ -79,9 +87,9 @@ module Quickery
79
87
 
80
88
  dependent_records = association_chain_dependee.dependent_records(self)
81
89
  # use the SQL as the uniqueness identifier, so that multiple quickery-attributes dependent-records are updated in one go, instead of updating each
82
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}
90
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}.with_indifferent_access
83
91
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:dependent_records] ||= dependent_records
84
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}
92
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}.with_indifferent_access
85
93
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values][depender_column_name.to_sym] = new_value
86
94
  end
87
95
  end
@@ -103,9 +111,9 @@ module Quickery
103
111
 
104
112
  dependent_records = association_chain_intermediary.dependent_records(self)
105
113
  # use the SQL as the uniqueness identifier, so that multiple quickery-attributes dependent-records are updated in one go, instead of updating each
106
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}
114
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}.with_indifferent_access
107
115
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:dependent_records] ||= dependent_records
108
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}
116
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}.with_indifferent_access
109
117
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values][depender_column_name.to_sym] = new_value
110
118
  end
111
119
  end
@@ -115,14 +123,14 @@ module Quickery
115
123
  dependent_records = hash.fetch(:dependent_records)
116
124
  new_values = hash.fetch(:new_values)
117
125
 
118
- dependent_records.update_all(new_values)
126
+ dependent_records.model.quickery_before_association_update(dependent_records, self, new_values)
119
127
  end
120
128
  end
121
129
 
122
130
  def quickery_before_destroy_callback
123
131
  model = self.class
124
132
 
125
- dependent_records_attributes_to_be_updated = {}
133
+ dependent_records_attributes_to_be_updated = {}.with_indifferent_access
126
134
 
127
135
  if model.quickery_association_chain_dependees.present?
128
136
  model.quickery_association_chain_dependees.each do |association_chain_dependee|
@@ -135,9 +143,9 @@ module Quickery
135
143
 
136
144
  dependent_records = association_chain_dependee.dependent_records(self)
137
145
  # use the SQL as the uniqueness identifier, so that multiple quickery-attributes dependent-records are updated in one go, instead of updating each
138
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}
146
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}.with_indifferent_access
139
147
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:dependent_records] ||= dependent_records
140
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}
148
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}.with_indifferent_access
141
149
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values][depender_column_name.to_sym] = new_value
142
150
  end
143
151
  end
@@ -154,9 +162,9 @@ module Quickery
154
162
 
155
163
  dependent_records = association_chain_intermediary.dependent_records(self)
156
164
  # use the SQL as the uniqueness identifier, so that multiple quickery-attributes dependent-records are updated in one go, instead of updating each
157
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}
165
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym] ||= {}.with_indifferent_access
158
166
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:dependent_records] ||= dependent_records
159
- dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}
167
+ dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values] ||= {}.with_indifferent_access
160
168
  dependent_records_attributes_to_be_updated[dependent_records.to_sql.to_sym][:new_values][depender_column_name.to_sym] = new_value
161
169
  end
162
170
  end
@@ -166,7 +174,7 @@ module Quickery
166
174
  dependent_records = hash.fetch(:dependent_records)
167
175
  new_values = hash.fetch(:new_values)
168
176
 
169
- dependent_records.update_all(new_values)
177
+ dependent_records.model.quickery_before_association_destroy(dependent_records, self, new_values)
170
178
  end
171
179
  end
172
180
  end
@@ -18,6 +18,21 @@ module Quickery
18
18
  mappings_builder = MappingsBuilder.new(model: self, mappings: mappings.with_indifferent_access)
19
19
  mappings_builder.map_attributes
20
20
  end
21
+
22
+ # subclass overrideable
23
+ def quickery_before_create_or_update(dependent_record, new_values)
24
+ dependent_record.assign_attributes(new_values)
25
+ end
26
+
27
+ # subclass overrideable
28
+ def quickery_before_association_update(dependent_records, record_to_be_updated, new_values)
29
+ dependent_records.update_all(new_values)
30
+ end
31
+
32
+ # subclass overrideable
33
+ def quickery_before_association_destroy(dependent_records, record_to_be_destroyed, new_values)
34
+ dependent_records.update_all(new_values)
35
+ end
21
36
  end
22
37
 
23
38
  module InstanceMethods
@@ -1,3 +1,3 @@
1
1
  module Quickery
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -109,9 +109,6 @@
109
109
  * Rails 4 seems to be also not supported. I was getting `ArgumentError (wrong number of arguments (given 1, expected 0))` when doing `User.create` inside rails console.
110
110
 
111
111
  * [flattery v0.1.0](https://github.com/evendis/flattery) :
112
- * Pros against Quickery:
113
- * allows custom update method
114
- * allows "updates" as a background process
115
112
  * Cons against Quickery:
116
113
  * Rails 5 is not part of its supported list in their github page. And just to try it out on a Rails 5 app, `Flattery::ValueProvider` did not seem to work, because values are not pushed to the `:notes`'s `:category_name` values.
117
114
  * batch-update in one go for multiple quickery-defined attributes instead of updating each
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quickery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jules Roman Polidario
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-13 00:00:00.000000000 Z
11
+ date: 2018-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties