junk_drawer 1.2.1 → 1.3.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/.rubocop.yml +17 -0
- data/.travis.yml +4 -0
- data/README.md +76 -2
- data/junk_drawer.gemspec +8 -4
- data/lib/junk_drawer/rails/bulk_updatable.rb +77 -0
- data/lib/junk_drawer/rails.rb +4 -0
- data/lib/junk_drawer/version.rb +1 -1
- metadata +68 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa1bc248af13f000203f8c72364c382c44de203a
|
4
|
+
data.tar.gz: 05ad7bf2b95cb6630eab0129811dcb68cd8e1e6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99d8ee5d88bc1bcd7d3253fc4dbac3be7be6418ede9a772bc4963233b0785344429ee40a2f8b2078e8d51e678aead1701eeba99ed4227ade2206b79e7af1912b
|
7
|
+
data.tar.gz: 108ff56c6ed5bbe24e81edcfa099e0ae7a55cada04469a0e9900d76b4b3623b64f334f39eaa52ed048b18811bf7fd8e907b83e95f7169d9f745de2ee93f4673b
|
data/.rubocop.yml
CHANGED
@@ -5,6 +5,18 @@ AllCops:
|
|
5
5
|
TargetRubyVersion: 2.3
|
6
6
|
DisplayCopNames: true
|
7
7
|
|
8
|
+
Lint/AmbiguousBlockAssociation:
|
9
|
+
Exclude:
|
10
|
+
- spec/**/*_spec.rb
|
11
|
+
|
12
|
+
Metrics/AbcSize:
|
13
|
+
Exclude:
|
14
|
+
- lib/junk_drawer/rails/bulk_updatable.rb
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Exclude:
|
18
|
+
- lib/junk_drawer/rails/bulk_updatable.rb
|
19
|
+
|
8
20
|
Metrics/BlockLength:
|
9
21
|
Exclude:
|
10
22
|
- junk_drawer.gemspec
|
@@ -59,6 +71,11 @@ Style/OptionHash:
|
|
59
71
|
Style/Send:
|
60
72
|
Enabled: true
|
61
73
|
|
74
|
+
RSpec/FilePath:
|
75
|
+
IgnoreMethods: true
|
76
|
+
Exclude:
|
77
|
+
- spec/junk_drawer/rails/bulk_updatable_spec.rb
|
78
|
+
|
62
79
|
RSpec/VerifiedDoubles:
|
63
80
|
Enabled: true
|
64
81
|
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# JunkDrawer
|
2
2
|
|
3
|
-
`JunkDrawer` is a gem providing
|
4
|
-
|
3
|
+
`JunkDrawer` is a gem providing a handful of random utility that are commonly
|
4
|
+
useful across projects.
|
5
5
|
|
6
6
|
## Installation
|
7
7
|
|
@@ -19,6 +19,18 @@ Or install it yourself as:
|
|
19
19
|
|
20
20
|
$ gem install junk_drawer
|
21
21
|
|
22
|
+
If you want to include the Rails utilities, in your Gemfile you can instead use:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'junk_drawer', require: 'junk_drawer/rails'
|
26
|
+
```
|
27
|
+
|
28
|
+
### Contents
|
29
|
+
|
30
|
+
- [JunkDrawer::Callable](#junkdrawercallable)
|
31
|
+
- [JunkDrawer::Notifier](#junkdrawernotifier)
|
32
|
+
- [JunkDrawer::BulkUpdatable](#junkdrawerbulkupdatable)
|
33
|
+
|
22
34
|
## Usage
|
23
35
|
|
24
36
|
### JunkDrawer::Callable
|
@@ -134,6 +146,68 @@ config.after_initialize do
|
|
134
146
|
end
|
135
147
|
```
|
136
148
|
|
149
|
+
-------------------------------------------------------------------------------
|
150
|
+
|
151
|
+
## Rails
|
152
|
+
|
153
|
+
For Rails specific tools, instead of requiring `'junk_drawer'`, you can require
|
154
|
+
`'junk_drawer/rails'`. This will pull in both the plain Ruby and the Rails
|
155
|
+
specific utilities.
|
156
|
+
|
157
|
+
### JunkDrawer::BulkUpdatable
|
158
|
+
|
159
|
+
`JunkDrawer::BulkUpdatable` is a utility to enable bulk updating of
|
160
|
+
`ActiveRecord` models. To enable it, extend in your models:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class MyModel < ApplicationRecord
|
164
|
+
extend JunkDrawer::BulkUpdatable
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
If you want to enable it for all models, you can also add it to your
|
169
|
+
`ApplicationModel` class:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class ApplicationRecord
|
173
|
+
self.abstract_class = true
|
174
|
+
extend JunkDrawer::BulkUpdatable
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
To make use of it, you can pass an array of records into the `.bulk_update`
|
179
|
+
class method on your model:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
my_model_1 = MyModel.find(1)
|
183
|
+
my_model_1.name = 'Jabba'
|
184
|
+
my_model_2 = MyModel.find(2)
|
185
|
+
my_model_2.name = 'JarJar'
|
186
|
+
|
187
|
+
MyModel.bulk_update([my_model_1, my_model_2])
|
188
|
+
```
|
189
|
+
|
190
|
+
This will generate a single SQL query to update both of the records in the
|
191
|
+
database.
|
192
|
+
|
193
|
+
#### Caveats
|
194
|
+
|
195
|
+
- Right now this only supports PostgreSQL. PR's welcome!
|
196
|
+
- It also only supports basic data types (including `hstore` and `jsonb`) for
|
197
|
+
your columns, so if you've got something weird you may have a bad time. Also
|
198
|
+
PR's welcome!
|
199
|
+
- General advice: if you're updating many thousands of records at the same
|
200
|
+
time, you may still run into some performance bottlenecks. When you're
|
201
|
+
dealing with massive amounts of data, we suggest pairing
|
202
|
+
`JunkDrawer::BulkUpdatable` with Rails' built-in `find_in_batches`:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
MyModel.find_in_batches(batch_size: 250) do |batch|
|
206
|
+
batch.each { |my_model| my_model.name = 'Jar' * rand(100) }
|
207
|
+
MyModel.bulk_update(batch)
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
137
211
|
## Development
|
138
212
|
|
139
213
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
data/junk_drawer.gemspec
CHANGED
@@ -21,14 +21,18 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.required_ruby_version = '>= 2.1'
|
23
23
|
|
24
|
+
spec.add_development_dependency 'activerecord', '~> 4.2'
|
25
|
+
spec.add_development_dependency 'activesupport', '~> 4.2'
|
24
26
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
25
27
|
spec.add_development_dependency 'guard', '~> 2.14'
|
26
28
|
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
27
29
|
spec.add_development_dependency 'guard-rubocop', '~> 1.2'
|
30
|
+
spec.add_development_dependency 'pg', '~> 0.20'
|
28
31
|
spec.add_development_dependency 'pry', '~> 0.10'
|
29
|
-
spec.add_development_dependency 'rake', '~>
|
32
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
30
33
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
-
spec.add_development_dependency 'rubocop', '~> 0.
|
32
|
-
spec.add_development_dependency 'rubocop-rspec', '~> 1.
|
33
|
-
spec.add_development_dependency 'simplecov', '~> 0.
|
34
|
+
spec.add_development_dependency 'rubocop', '~> 0.48.1'
|
35
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.15.0'
|
36
|
+
spec.add_development_dependency 'simplecov', '~> 0.14'
|
37
|
+
spec.add_development_dependency 'with_model', '~> 2.0'
|
34
38
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'active_record'
|
5
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
6
|
+
|
7
|
+
module JunkDrawer
|
8
|
+
# module to allow bulk updates for `ActiveRecord` models
|
9
|
+
module BulkUpdatable
|
10
|
+
ATTRIBUTE_TYPE_TO_POSTGRES_CAST = {
|
11
|
+
datetime: '::timestamp',
|
12
|
+
hstore: '::hstore',
|
13
|
+
boolean: '::boolean',
|
14
|
+
jsonb: '::jsonb',
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
POSTGRES_VALUE_CASTERS = {
|
18
|
+
hstore: ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Hstore.new,
|
19
|
+
jsonb: ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb.new,
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def bulk_update(objects)
|
23
|
+
objects = objects.select(&:changed?)
|
24
|
+
return unless objects.any?
|
25
|
+
|
26
|
+
changed_attributes = extract_changed_attributes(objects)
|
27
|
+
query = build_query_for(objects, changed_attributes)
|
28
|
+
connection.execute(query)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def extract_changed_attributes(objects)
|
34
|
+
now = Time.zone.now
|
35
|
+
objects.each { |object| object.updated_at = now }
|
36
|
+
|
37
|
+
objects.flat_map(&:changed).uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_query_for(objects, attributes)
|
41
|
+
object_values = objects.map do |object|
|
42
|
+
sanitized_values(object, attributes)
|
43
|
+
end.join(', ')
|
44
|
+
|
45
|
+
assignment_query = attributes.map do |attribute|
|
46
|
+
quoted_column_name = connection.quote_column_name(attribute)
|
47
|
+
"#{quoted_column_name} = tmp_table.#{quoted_column_name}"
|
48
|
+
end.join(', ')
|
49
|
+
|
50
|
+
<<-SQL
|
51
|
+
UPDATE #{table_name}
|
52
|
+
SET #{assignment_query}
|
53
|
+
FROM (VALUES #{object_values}) AS tmp_table(id, #{attributes.join(', ')})
|
54
|
+
WHERE #{table_name}.id = tmp_table.id
|
55
|
+
SQL
|
56
|
+
end
|
57
|
+
|
58
|
+
def sanitized_values(object, attributes)
|
59
|
+
postgres_types = attributes.map do |attribute|
|
60
|
+
attribute_type = columns_hash[attribute.to_s].type
|
61
|
+
"?#{ATTRIBUTE_TYPE_TO_POSTGRES_CAST[attribute_type]}"
|
62
|
+
end
|
63
|
+
|
64
|
+
postgres_values = attributes.map do |attribute|
|
65
|
+
value = object[attribute]
|
66
|
+
attribute_type = columns_hash[attribute.to_s].type
|
67
|
+
caster = POSTGRES_VALUE_CASTERS[attribute_type]
|
68
|
+
|
69
|
+
caster ? caster.type_cast_for_database(value) : value
|
70
|
+
end
|
71
|
+
|
72
|
+
sanitize_sql_array(
|
73
|
+
["(?, #{postgres_types.join(', ')})", object.id, *postgres_values],
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/junk_drawer/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: junk_drawer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Fletcher
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.2'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '1.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pg
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.20'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.20'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: pry
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +128,14 @@ dependencies:
|
|
86
128
|
requirements:
|
87
129
|
- - "~>"
|
88
130
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
131
|
+
version: '12.0'
|
90
132
|
type: :development
|
91
133
|
prerelease: false
|
92
134
|
version_requirements: !ruby/object:Gem::Requirement
|
93
135
|
requirements:
|
94
136
|
- - "~>"
|
95
137
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
138
|
+
version: '12.0'
|
97
139
|
- !ruby/object:Gem::Dependency
|
98
140
|
name: rspec
|
99
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,42 +156,56 @@ dependencies:
|
|
114
156
|
requirements:
|
115
157
|
- - "~>"
|
116
158
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
159
|
+
version: 0.48.1
|
118
160
|
type: :development
|
119
161
|
prerelease: false
|
120
162
|
version_requirements: !ruby/object:Gem::Requirement
|
121
163
|
requirements:
|
122
164
|
- - "~>"
|
123
165
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
166
|
+
version: 0.48.1
|
125
167
|
- !ruby/object:Gem::Dependency
|
126
168
|
name: rubocop-rspec
|
127
169
|
requirement: !ruby/object:Gem::Requirement
|
128
170
|
requirements:
|
129
171
|
- - "~>"
|
130
172
|
- !ruby/object:Gem::Version
|
131
|
-
version: 1.
|
173
|
+
version: 1.15.0
|
132
174
|
type: :development
|
133
175
|
prerelease: false
|
134
176
|
version_requirements: !ruby/object:Gem::Requirement
|
135
177
|
requirements:
|
136
178
|
- - "~>"
|
137
179
|
- !ruby/object:Gem::Version
|
138
|
-
version: 1.
|
180
|
+
version: 1.15.0
|
139
181
|
- !ruby/object:Gem::Dependency
|
140
182
|
name: simplecov
|
141
183
|
requirement: !ruby/object:Gem::Requirement
|
142
184
|
requirements:
|
143
185
|
- - "~>"
|
144
186
|
- !ruby/object:Gem::Version
|
145
|
-
version: '0.
|
187
|
+
version: '0.14'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0.14'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: with_model
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '2.0'
|
146
202
|
type: :development
|
147
203
|
prerelease: false
|
148
204
|
version_requirements: !ruby/object:Gem::Requirement
|
149
205
|
requirements:
|
150
206
|
- - "~>"
|
151
207
|
- !ruby/object:Gem::Version
|
152
|
-
version: '0
|
208
|
+
version: '2.0'
|
153
209
|
description:
|
154
210
|
email:
|
155
211
|
- lobatifricha@gmail.com
|
@@ -176,6 +232,8 @@ files:
|
|
176
232
|
- lib/junk_drawer/notifier/honeybadger_strategy.rb
|
177
233
|
- lib/junk_drawer/notifier/null_strategy.rb
|
178
234
|
- lib/junk_drawer/notifier/raise_strategy.rb
|
235
|
+
- lib/junk_drawer/rails.rb
|
236
|
+
- lib/junk_drawer/rails/bulk_updatable.rb
|
179
237
|
- lib/junk_drawer/version.rb
|
180
238
|
homepage: https://github.com/mockdeep/junk_drawer
|
181
239
|
licenses:
|