atomically 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -1
- data/README.md +41 -1
- data/Rakefile +5 -5
- data/atomically.gemspec +26 -25
- data/bin/console +3 -3
- data/gemfiles/3.2.gemfile +8 -7
- data/gemfiles/4.2.gemfile +8 -7
- data/gemfiles/5.0.gemfile +8 -7
- data/gemfiles/5.1.gemfile +8 -7
- data/gemfiles/5.2.gemfile +8 -7
- data/lib/atomically.rb +2 -0
- data/lib/atomically/active_record/extension.rb +6 -0
- data/lib/atomically/patches/clear_attribute_changes.rb +17 -0
- data/lib/atomically/patches/from.rb +3 -1
- data/lib/atomically/patches/none.rb +2 -0
- data/lib/atomically/query_service.rb +34 -2
- data/lib/atomically/update_all_scope.rb +28 -0
- data/lib/atomically/version.rb +3 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94187458ce758fe40e71ba068f03dad4e2d8cebb0757099b8cecad1f1a8da017
|
4
|
+
data.tar.gz: c79301b92a0436536feb4f850ba7e1b5f8fb8d589c17e50b9b6c20a38022826c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5163ddf343c94d028e7ea2b6fadd62baccf65e49a9d57ef08ff0894a1a3113c14048af6a410c7129604292c5d9d39d3c81ff94a8293f24b0eb08c170039beb59
|
7
|
+
data.tar.gz: 5d02fedd2c17837517874714a23ffedd0a801a93cb12adf1217ed59cf8af483fd1793320f6fb7bbbf3aa35257ba9efda034245b194728949b8c526eaf07c07e2
|
data/.rubocop.yml
CHANGED
@@ -1013,7 +1013,8 @@ Layout/SpaceInsideBlockBraces:
|
|
1013
1013
|
Checks that block braces have or don't have surrounding space.
|
1014
1014
|
For blocks taking parameters, checks that the left brace has
|
1015
1015
|
or doesn't have trailing space.
|
1016
|
-
|
1016
|
+
SpaceBeforeBlockParameters: false
|
1017
|
+
Enabled: true
|
1017
1018
|
|
1018
1019
|
Layout/SpaceAroundBlockParameters:
|
1019
1020
|
Description: 'Checks the spacing inside and after block parameters pipes.'
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Atomically
|
2
2
|
|
3
3
|
[![Gem Version](https://img.shields.io/gem/v/atomically.svg?style=flat)](https://rubygems.org/gems/atomically)
|
4
|
-
[![Build Status](https://travis-ci.
|
4
|
+
[![Build Status](https://api.travis-ci.com/khiav223577/atomically.svg?branch=master)](https://travis-ci.com/khiav223577/atomically)
|
5
5
|
[![RubyGems](http://img.shields.io/gem/dt/atomically.svg?style=flat)](https://rubygems.org/gems/atomically)
|
6
6
|
[![Code Climate](https://codeclimate.com/github/khiav223577/atomically/badges/gpa.svg)](https://codeclimate.com/github/khiav223577/atomically)
|
7
7
|
[![Test Coverage](https://codeclimate.com/github/khiav223577/atomically/badges/coverage.svg)](https://codeclimate.com/github/khiav223577/atomically/coverage)
|
@@ -36,6 +36,12 @@ It is useful to add `items` to `user` when `user_items` may not exist.
|
|
36
36
|
First two args (columns, values) are the same with the [import](https://github.com/zdennis/activerecord-import#columns-and-arrays) method.
|
37
37
|
|
38
38
|
Example:
|
39
|
+
```rb
|
40
|
+
user = User.find(2)
|
41
|
+
item1 = Item.find(1)
|
42
|
+
item2 = Item.find(2)
|
43
|
+
```
|
44
|
+
|
39
45
|
```rb
|
40
46
|
columns = [:user_id, :item_id, :quantity]
|
41
47
|
values = [[user.id, item1.id, 3], [user.id, item2.id, 2]]
|
@@ -44,7 +50,15 @@ on_duplicate_update_columns = [:quantity]
|
|
44
50
|
UserItem.atomically.create_or_plus(columns, values, on_duplicate_update_columns)
|
45
51
|
```
|
46
52
|
|
53
|
+
#### before
|
54
|
+
![before](https://user-images.githubusercontent.com/4011729/48998921-ff430600-f18f-11e8-8eeb-e8a71bbf5802.png)
|
55
|
+
|
56
|
+
#### after
|
57
|
+
![image](https://user-images.githubusercontent.com/4011729/48999092-8d1ef100-f190-11e8-8372-86e2e99cbe08.png)
|
58
|
+
|
59
|
+
|
47
60
|
### pay_all
|
61
|
+
|
48
62
|
Reduce the quantity of items and return how many rows and updated if all of them is enough.
|
49
63
|
Do nothing and return zero if any of them is not enough.
|
50
64
|
|
@@ -53,6 +67,32 @@ Example:
|
|
53
67
|
user.user_items.atomically.pay_all({ item1.id => 4, item2.id => 3 }, [:quantity], primary_key: :item_id)
|
54
68
|
```
|
55
69
|
|
70
|
+
### update
|
71
|
+
|
72
|
+
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.
|
73
|
+
|
74
|
+
Example:
|
75
|
+
```rb
|
76
|
+
class Arena < ApplicationRecord
|
77
|
+
def atomically_close!
|
78
|
+
atomically.update(closed_at: Time.now)
|
79
|
+
end
|
80
|
+
|
81
|
+
def close!
|
82
|
+
update(closed_at: Time.now)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
```sql
|
87
|
+
# arena.atomically_close!
|
88
|
+
UPDATE `arenas` SET `arenas`.`closed_at` = '2018-11-27 03:44:25', `updated_at` = '2018-11-27 03:44:25'
|
89
|
+
WHERE `arenas`.`id` = 1752 AND `arenas`.`closed_at` IS NULL
|
90
|
+
|
91
|
+
# arena.close!
|
92
|
+
UPDATE `arenas` SET `arenas`.`closed_at` = '2018-11-27 03:44:25', `updated_at` = '2018-11-27 03:44:25'
|
93
|
+
WHERE `arenas`.`id` = 1752
|
94
|
+
```
|
95
|
+
|
56
96
|
## Development
|
57
97
|
|
58
98
|
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.
|
data/Rakefile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
3
|
|
4
4
|
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.libs <<
|
6
|
-
t.libs <<
|
5
|
+
t.libs << 'test'
|
6
|
+
t.libs << 'lib'
|
7
7
|
t.test_files = FileList['test/**/*_test.rb']
|
8
8
|
end
|
9
9
|
|
10
|
-
task :
|
10
|
+
task default: :test
|
data/atomically.gemspec
CHANGED
@@ -4,37 +4,38 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'atomically/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'atomically'
|
8
8
|
spec.version = Atomically::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['khiav reoy']
|
10
|
+
spec.email = ['mrtmrt15xn@yahoo.com.tw']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
12
|
+
spec.summary = ''
|
13
|
+
spec.description = ''
|
14
|
+
spec.homepage = 'https://github.com/khiav223577/atomically'
|
15
|
+
spec.license = 'MIT'
|
16
16
|
|
17
17
|
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
18
|
# delete this section to allow pushing this gem to any host.
|
19
|
-
#if spec.respond_to?(:metadata)
|
20
|
-
#
|
21
|
-
#else
|
22
|
-
#
|
23
|
-
#end
|
19
|
+
# if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
# else
|
22
|
+
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
# end
|
24
24
|
|
25
|
-
spec.files = `git ls-files -z`.split("\x0").reject
|
26
|
-
spec.bindir =
|
27
|
-
spec.executables = spec.files.grep(%r{^exe/})
|
28
|
-
spec.require_paths = [
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject{|f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = 'exe'
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}){|f| File.basename(f) }
|
28
|
+
spec.require_paths = ['lib']
|
29
29
|
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
35
|
-
spec.add_development_dependency
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
31
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
32
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3'
|
33
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
34
|
+
spec.add_development_dependency 'mysql2', '>= 0.3'
|
35
|
+
spec.add_development_dependency 'pluck_all', '>= 2.0.3'
|
36
|
+
spec.add_development_dependency 'timecop', '~> 0.9.1'
|
36
37
|
|
37
|
-
spec.add_dependency
|
38
|
-
spec.add_dependency
|
39
|
-
spec.add_dependency
|
38
|
+
spec.add_dependency 'activerecord', '>= 3'
|
39
|
+
spec.add_dependency 'activerecord-import', '>= 0.27.0'
|
40
|
+
spec.add_dependency 'rails_or', '>= 1.1.8'
|
40
41
|
end
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'atomically'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "atomically"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/gemfiles/3.2.gemfile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
3
|
+
gem 'activerecord', '~> 3.2.0'
|
4
4
|
|
5
5
|
group :test do
|
6
6
|
case ENV['DB']
|
7
|
-
when
|
8
|
-
when
|
7
|
+
when 'mysql' ; gem 'mysql2', '0.3.21'
|
8
|
+
when 'postgres' ; gem 'pg', '~> 0.18'
|
9
9
|
end
|
10
|
-
gem
|
11
|
-
gem
|
10
|
+
gem 'simplecov'
|
11
|
+
gem 'codeclimate-test-reporter', '~> 1.0.0'
|
12
|
+
gem 'pluck_all', '>= 2.0.3'
|
13
|
+
gem 'timecop', '~> 0.9.1'
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
15
|
-
|
16
|
+
gemspec path: '../'
|
data/gemfiles/4.2.gemfile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
3
|
+
gem 'activerecord', '~> 4.2.0'
|
4
4
|
|
5
5
|
group :test do
|
6
6
|
case ENV['DB']
|
7
|
-
when
|
8
|
-
when
|
7
|
+
when 'mysql' ; gem 'mysql2', '0.3.21'
|
8
|
+
when 'postgres' ; gem 'pg', '~> 0.18'
|
9
9
|
end
|
10
|
-
gem
|
11
|
-
gem
|
10
|
+
gem 'simplecov'
|
11
|
+
gem 'codeclimate-test-reporter', '~> 1.0.0'
|
12
|
+
gem 'pluck_all', '>= 2.0.3'
|
13
|
+
gem 'timecop', '~> 0.9.1'
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
15
|
-
|
16
|
+
gemspec path: '../'
|
data/gemfiles/5.0.gemfile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
3
|
+
gem 'activerecord', '~> 5.0.0'
|
4
4
|
|
5
5
|
group :test do
|
6
6
|
case ENV['DB']
|
7
|
-
when
|
8
|
-
when
|
7
|
+
when 'mysql' ; gem 'mysql2', '0.3.21'
|
8
|
+
when 'postgres' ; gem 'pg', '~> 0.18'
|
9
9
|
end
|
10
|
-
gem
|
11
|
-
gem
|
10
|
+
gem 'simplecov'
|
11
|
+
gem 'codeclimate-test-reporter', '~> 1.0.0'
|
12
|
+
gem 'pluck_all', '>= 2.0.3'
|
13
|
+
gem 'timecop', '~> 0.9.1'
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
15
|
-
|
16
|
+
gemspec path: '../'
|
data/gemfiles/5.1.gemfile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
3
|
+
gem 'activerecord', '~> 5.1.0'
|
4
4
|
|
5
5
|
group :test do
|
6
6
|
case ENV['DB']
|
7
|
-
when
|
8
|
-
when
|
7
|
+
when 'mysql' ; gem 'mysql2', '0.3.21'
|
8
|
+
when 'postgres' ; gem 'pg', '~> 0.18'
|
9
9
|
end
|
10
|
-
gem
|
11
|
-
gem
|
10
|
+
gem 'simplecov'
|
11
|
+
gem 'codeclimate-test-reporter', '~> 1.0.0'
|
12
|
+
gem 'pluck_all', '>= 2.0.3'
|
13
|
+
gem 'timecop', '~> 0.9.1'
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
15
|
-
|
16
|
+
gemspec path: '../'
|
data/gemfiles/5.2.gemfile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
3
|
+
gem 'activerecord', '~> 5.2.0'
|
4
4
|
|
5
5
|
group :test do
|
6
6
|
case ENV['DB']
|
7
|
-
when
|
8
|
-
when
|
7
|
+
when 'mysql' ; gem 'mysql2', '0.5.1'
|
8
|
+
when 'postgres' ; gem 'pg', '~> 0.18'
|
9
9
|
end
|
10
|
-
gem
|
11
|
-
gem
|
10
|
+
gem 'simplecov'
|
11
|
+
gem 'codeclimate-test-reporter', '~> 1.0.0'
|
12
|
+
gem 'pluck_all', '>= 2.0.3'
|
13
|
+
gem 'timecop', '~> 0.9.1'
|
12
14
|
end
|
13
15
|
|
14
|
-
gemspec :
|
15
|
-
|
16
|
+
gemspec path: '../'
|
data/lib/atomically.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
require 'atomically/query_service'
|
3
5
|
|
@@ -11,4 +13,8 @@ class ActiveRecord::Base
|
|
11
13
|
def self.atomically
|
12
14
|
Atomically::QueryService.new(self)
|
13
15
|
end
|
16
|
+
|
17
|
+
def atomically
|
18
|
+
Atomically::QueryService.new(self.class, model: self)
|
19
|
+
end
|
14
20
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Dirty
|
3
|
+
private
|
4
|
+
|
5
|
+
alias attributes_changed_by_setter changed_attributes # :nodoc:
|
6
|
+
|
7
|
+
# Force an attribute to have a particular "before" value
|
8
|
+
def set_attribute_was(attr, old_value)
|
9
|
+
attributes_changed_by_setter[attr] = old_value
|
10
|
+
end
|
11
|
+
|
12
|
+
# Remove changes information for the provided attributes.
|
13
|
+
def clear_attribute_changes(attributes) # :doc:
|
14
|
+
attributes_changed_by_setter.except!(*attributes)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
|
3
5
|
class << ActiveRecord::Base
|
4
6
|
def from(value) # For Rails 3
|
5
|
-
value = "(#{value.to_sql}) subquery" if value.is_a?(
|
7
|
+
value = "(#{value.to_sql}) subquery" if value.is_a?(ActiveRecord::Relation)
|
6
8
|
return super
|
7
9
|
end
|
8
10
|
end
|
@@ -2,13 +2,16 @@
|
|
2
2
|
|
3
3
|
require 'activerecord-import'
|
4
4
|
require 'rails_or'
|
5
|
+
require 'atomically/update_all_scope'
|
6
|
+
require 'atomically/patches/clear_attribute_changes' if not ActiveModel::Dirty.method_defined?(:clear_attribute_changes) and not ActiveModel::Dirty.private_method_defined?(:clear_attribute_changes)
|
5
7
|
require 'atomically/patches/none' if not ActiveRecord::Base.respond_to?(:none)
|
6
8
|
require 'atomically/patches/from' if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.0.0')
|
7
9
|
|
8
10
|
class Atomically::QueryService
|
9
|
-
def initialize(klass, relation: nil)
|
11
|
+
def initialize(klass, relation: nil, model: nil)
|
10
12
|
@klass = klass
|
11
13
|
@relation = relation || @klass
|
14
|
+
@model = model
|
12
15
|
end
|
13
16
|
|
14
17
|
def create_or_plus(columns, data, update_columns)
|
@@ -36,9 +39,15 @@ class Atomically::QueryService
|
|
36
39
|
.update_all(update_sqls.join(', '))
|
37
40
|
end
|
38
41
|
|
42
|
+
def update(attrs, from: :not_set)
|
43
|
+
success = update_and_return_number_of_updated_rows(attrs, from) == 1
|
44
|
+
assign_without_changes(attrs) if success
|
45
|
+
return success
|
46
|
+
end
|
47
|
+
|
39
48
|
private
|
40
49
|
|
41
|
-
def on_duplicate_key_plus_sql(
|
50
|
+
def on_duplicate_key_plus_sql(columns)
|
42
51
|
columns.lazy.map(&method(:quote_column)).map{|s| "#{s} = #{s} + VALUES(#{s})" }.force.join(', ')
|
43
52
|
end
|
44
53
|
|
@@ -49,4 +58,27 @@ class Atomically::QueryService
|
|
49
58
|
def sanitize(value)
|
50
59
|
@klass.connection.quote(value)
|
51
60
|
end
|
61
|
+
|
62
|
+
def update_and_return_number_of_updated_rows(attrs, from)
|
63
|
+
model = @model
|
64
|
+
return open_update_all_scope do
|
65
|
+
update(updated_at: Time.now)
|
66
|
+
attrs.each do |column, value|
|
67
|
+
old_value = (from == :not_set ? model[column] : from)
|
68
|
+
where(column => old_value).update(column => value) if old_value != value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def open_update_all_scope(&block)
|
74
|
+
return 0 if @model == nil
|
75
|
+
scope = UpdateAllScope.new(model: @model)
|
76
|
+
scope.instance_exec(&block)
|
77
|
+
return scope.do_query!
|
78
|
+
end
|
79
|
+
|
80
|
+
def assign_without_changes(attributes)
|
81
|
+
@model.assign_attributes(attributes)
|
82
|
+
@model.send(:clear_attribute_changes, attributes.keys)
|
83
|
+
end
|
52
84
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UpdateAllScope
|
4
|
+
def initialize(model: nil, relation: nil)
|
5
|
+
@queries = []
|
6
|
+
@relation = relation || model.class.where(id: model.id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def where(*args)
|
10
|
+
@relation = @relation.where(*args)
|
11
|
+
return self
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(query, *binding_values)
|
15
|
+
args = binding_values.size > 0 ? [[query, *binding_values]] : [query]
|
16
|
+
@queries << klass.send(:sanitize_sql_for_assignment, *args)
|
17
|
+
return self
|
18
|
+
end
|
19
|
+
|
20
|
+
def do_query!
|
21
|
+
return 0 if @queries.empty?
|
22
|
+
return @relation.update_all(@queries.join(','))
|
23
|
+
end
|
24
|
+
|
25
|
+
def klass
|
26
|
+
@relation.klass
|
27
|
+
end
|
28
|
+
end
|
data/lib/atomically/version.rb
CHANGED
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.
|
4
|
+
version: 1.0.2
|
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-
|
11
|
+
date: 2018-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 2.0.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timecop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.9.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.9.1
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: activerecord
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -163,9 +177,11 @@ files:
|
|
163
177
|
- gemfiles/5.2.gemfile
|
164
178
|
- lib/atomically.rb
|
165
179
|
- lib/atomically/active_record/extension.rb
|
180
|
+
- lib/atomically/patches/clear_attribute_changes.rb
|
166
181
|
- lib/atomically/patches/from.rb
|
167
182
|
- lib/atomically/patches/none.rb
|
168
183
|
- lib/atomically/query_service.rb
|
184
|
+
- lib/atomically/update_all_scope.rb
|
169
185
|
- lib/atomically/version.rb
|
170
186
|
homepage: https://github.com/khiav223577/atomically
|
171
187
|
licenses:
|