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 +4 -4
- data/README.md +166 -1
- data/lib/quickery.rb +0 -1
- data/lib/quickery/active_record_extensions/callbacks.rb +22 -14
- data/lib/quickery/active_record_extensions/dsl.rb +15 -0
- data/lib/quickery/version.rb +1 -1
- data/other_similar_gems_comparison.md +0 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5caca192d074a8aa71dd2c6e9bc06a3051b6f7eaec2bc664618af356d41a250d
|
4
|
+
data.tar.gz: 9acfccf220d83aa4c0d521ba13b4040241273d513160a7e2c4b4afccdb32de8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/quickery.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
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.
|
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
|
data/lib/quickery/version.rb
CHANGED
@@ -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.
|
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-
|
11
|
+
date: 2018-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|