quickery 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|