atomically 1.0.3 → 1.0.4

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: 215319d576e714905af55fcb2ec48b1924c5971bf0e0bdaf3c61f75be3862a13
4
- data.tar.gz: 6171664742f2758011c79058fc2fd13850e11bb61c28604f24625e4ed107026f
3
+ metadata.gz: ec69d82cbee452fb2b61bd973aa1a885e5cffd5cdd8c5b5e6bb96ea066c8f7b1
4
+ data.tar.gz: be3720c0644d7c8228ea3b8ae88fd05c1850c66f629819977cc4758b8ec0d3d9
5
5
  SHA512:
6
- metadata.gz: 117f29a0644c694a25bef5ebd0d5bf8219e1a9e2521e0fad95f8ee5180ebad418d2651b10d3259a06735b495d23ceb4de1a75d3e928fe4dc725203aece924306
7
- data.tar.gz: 62482706f8a98ad1bc71041c323596a117e810330921b90e9395134c7d495c9c7e2b7f01378151178abd20b11dffad42678e9b2fdd0e851776d2de81537c10be
6
+ metadata.gz: 1b88cc95978a5c3cf4913d812e99e32aa4ef6b2bee12572e7e9eccea51edeadae8c78cdd42ad2f436ea9f3749832c1cacec0dd380a64900b0caa1936446b9a7a
7
+ data.tar.gz: ea519708c2c2d24b7876f810095adf0a9b599d713b3e7cb4091981d567886ce9c28644d42bca688790ada4d0e9e5b153bc3f9c6149fc060818768ec12493d437
@@ -19,8 +19,8 @@ matrix:
19
19
  - gemfile: gemfiles/3.2.gemfile
20
20
  rvm: 2.5
21
21
  before_install:
22
+ - gem i rubygems-update -v '<3' && update_rubygems
22
23
  - gem install bundler
23
- - gem update --system
24
24
  - gem --version
25
25
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
26
26
  - chmod +x ./cc-test-reporter
data/README.md CHANGED
@@ -10,6 +10,19 @@
10
10
 
11
11
  Supports Rails 3.2, 4.2, 5.0, 5.1, 5.2.
12
12
 
13
+ ## Table of contents
14
+
15
+ 1. [Installation](#installation)
16
+ 2. [Methods](#methods)
17
+ - [create_or_plus](#create_or_plus-columns-values-on_duplicate_update_columns)
18
+ - [pay_all](#pay_all-hash-update_columns-primary_key-id)
19
+ - [update_all](#update_all-expected_number-updates)
20
+ - [update](#update-attrs-from-not_set)
21
+ - [update_all_and_get_ids](#update_all_and_get_ids-updates)
22
+ 3. [Development](#development)
23
+ 4. [Contributing](#contributing)
24
+ 5. [License](#license)
25
+
13
26
  ## Installation
14
27
 
15
28
  Add this line to your application's Gemfile:
@@ -26,7 +39,9 @@ Or install it yourself as:
26
39
 
27
40
  $ gem install atomically
28
41
 
29
- ## Usage
42
+ ## Methods
43
+
44
+ Note: ActiveRecord validations and callbacks will **NOT** be triggered when calling below methods.
30
45
 
31
46
  ### create_or_plus _(columns, values, on_duplicate_update_columns)_
32
47
 
@@ -39,6 +54,7 @@ It is useful to add `items` to `user` when `user_items` may not exist.
39
54
  - `on_duplicate_update_columns` - The column that will be updated on duplicate.
40
55
 
41
56
  #### Example
57
+
42
58
  ```rb
43
59
  user = User.find(2)
44
60
  item1 = Item.find(1)
@@ -61,7 +77,16 @@ after
61
77
 
62
78
  ![image](https://user-images.githubusercontent.com/4011729/48999092-8d1ef100-f190-11e8-8372-86e2e99cbe08.png)
63
79
 
80
+ #### SQL queries
64
81
 
82
+ ```sql
83
+ INSERT INTO `user_items` (`user_id`,`item_id`,`quantity`,`created_at`,`updated_at`) VALUES
84
+ (2,1,3,'2018-11-27 03:44:25','2018-11-27 03:44:25'),
85
+ (2,2,2,'2018-11-27 03:44:25','2018-11-27 03:44:25')
86
+ ON DUPLICATE KEY UPDATE `quantity` = `quantity` + VALUES(`quantity`)
87
+ ```
88
+
89
+ ---
65
90
  ### pay_all _(hash, update_columns, primary_key: :id)_
66
91
 
67
92
  Reduce the quantity of items and return how many rows and updated if all of them is enough.
@@ -79,19 +104,20 @@ Do nothing and return zero if any of them is not enough.
79
104
  user.user_items.atomically.pay_all({ item1.id => 4, item2.id => 3 }, [:quantity], primary_key: :item_id)
80
105
  ```
81
106
 
107
+ #### SQL queries
108
+
82
109
  ```sql
83
- # generated sql
84
- UPDATE `user_items` SET `quantity` = `quantity` + (@change :=
110
+ UPDATE `user_items` SET `quantity` = `quantity` + (@change :=
85
111
  CASE `item_id`
86
112
  WHEN 1 THEN -4
87
113
  WHEN 2 THEN -3
88
- END)
114
+ END)
89
115
  WHERE `user_items`.`user_id` = 1 AND (
90
- `user_items`.`item_id` = 1 AND (`quantity` >= 4) OR `user_items`.`item_id` = -2 AND (`quantity` >= 3)
116
+ `user_items`.`item_id` = 1 AND (`quantity` >= 4) OR `user_items`.`item_id` = 2 AND (`quantity` >= 3)
91
117
  ) AND (
92
118
  (
93
119
  SELECT COUNT(*) FROM (
94
- SELECT `user_items`.* FROM `user_items`
120
+ SELECT `user_items`.* FROM `user_items`
95
121
  WHERE `user_items`.`user_id` = 1 AND (
96
122
  `user_items`.`item_id` = 1 AND (`quantity` >= 4) OR `user_items`.`item_id` = 2 AND (`quantity` >= 3)
97
123
  )
@@ -100,6 +126,7 @@ WHERE `user_items`.`user_id` = 1 AND (
100
126
  )
101
127
  ```
102
128
 
129
+ ---
103
130
  ### update_all _(expected_number, updates)_
104
131
 
105
132
  Behaves like [ActiveRecord::Relation#update_all](https://apidock.com/rails/ActiveRecord/Relation/update_all) but add an additional constrain that the number of affected rows equals to what you specify.
@@ -118,6 +145,20 @@ User.where(id: [1, 2, 3]).atomically.update_all(2, name: '')
118
145
  # => 0
119
146
  ```
120
147
 
148
+ #### SQL queries
149
+
150
+ ```sql
151
+ # User.where(id: [1, 2, 3]).atomically.update_all(2, name: '')
152
+ UPDATE `users` SET `users`.`name` = '' WHERE `users`.`id` IN (1, 2, 3) AND (
153
+ (
154
+ SELECT COUNT(*) FROM (
155
+ SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2, 3)
156
+ ) subquery
157
+ ) = 2
158
+ )
159
+ ```
160
+
161
+ ---
121
162
  ### update _(attrs, from: :not_set)_
122
163
 
123
164
  Updates the attributes of the model from the passed-in hash and saves the record. The difference between this method and [ActiveRecord#update](https://apidock.com/rails/ActiveRecord/Persistence/update) is that it will add extra WHERE conditions to prevent race condition.
@@ -126,8 +167,9 @@ Updates the attributes of the model from the passed-in hash and saves the record
126
167
 
127
168
  - `attrs` - Same with the first parameter of [ActiveRecord#update](https://apidock.com/rails/ActiveRecord/Persistence/update)
128
169
  - `from` - The value before update. If not set, use the attriutes of the model.
129
-
170
+
130
171
  #### Example
172
+
131
173
  ```rb
132
174
  class Arena < ApplicationRecord
133
175
  def atomically_close!
@@ -139,6 +181,9 @@ class Arena < ApplicationRecord
139
181
  end
140
182
  end
141
183
  ```
184
+
185
+ #### SQL queries
186
+
142
187
  ```sql
143
188
  # arena.atomically_close!
144
189
  UPDATE `arenas` SET `arenas`.`closed_at` = '2018-11-27 03:44:25', `updated_at` = '2018-11-27 03:44:25'
@@ -149,6 +194,33 @@ UPDATE `arenas` SET `arenas`.`closed_at` = '2018-11-27 03:44:25', `updated_at` =
149
194
  WHERE `arenas`.`id` = 1752
150
195
  ```
151
196
 
197
+ ---
198
+ ### update_all_and_get_ids _(updates)_
199
+
200
+ Behaves like [ActiveRecord::Relation#update_all](https://apidock.com/rails/ActiveRecord/Relation/update_all), but return the ids array of updated records instead of the number of updated records.
201
+
202
+
203
+ #### Parameters
204
+
205
+ - `updates` - A string, array, or hash representing the SET part of an SQL statement.
206
+
207
+ #### Example
208
+
209
+ ```rb
210
+ User.where(account: ['moon', 'wolf']).atomically.update_all_and_get_ids('money = money + 1')
211
+ # => [254, 371]
212
+ ```
213
+
214
+ #### SQL queries
215
+
216
+ ```sql
217
+ BEGIN
218
+ SET @ids := NULL
219
+ UPDATE `users` SET money = money + 1 WHERE `users`.`account` IN ('moon', 'wolf') AND ((SELECT @ids := CONCAT_WS(',', `users`.`id`, @ids)))
220
+ SELECT @ids FROM DUAL
221
+ COMMIT
222
+ ```
223
+
152
224
  ## Development
153
225
 
154
226
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test DB=mysql` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -48,6 +48,17 @@ class Atomically::QueryService
48
48
  return success
49
49
  end
50
50
 
51
+ def update_all_and_get_ids(*args)
52
+ ids = nil
53
+ id_column = "#{@klass.quoted_table_name}.#{quote_column(:id)}"
54
+ @klass.transaction do
55
+ @relation.connection.execute('SET @ids := NULL')
56
+ @relation.where("(SELECT @ids := CONCAT_WS(',', #{id_column}, @ids))").update_all(*args) # 撈出有真的被更新的 id,用逗號串在一起
57
+ ids = @klass.from(nil).pluck('@ids').first
58
+ end
59
+ return ids.try{|s| s.split(',').map(&:to_i).uniq.sort } || [] # 將 id 從字串取出來 @id 的格式範例: '1,4,12'
60
+ end
61
+
51
62
  private
52
63
 
53
64
  def on_duplicate_key_plus_sql(columns)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Atomically
4
- VERSION = '1.0.3'
4
+ VERSION = '1.0.4'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomically
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - khiav reoy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-28 00:00:00.000000000 Z
11
+ date: 2018-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler