active-record-atomic 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d47216327709a5093e8b577ba123a903fbe4f99e
4
+ data.tar.gz: 14dc6addbad5fe40bb8dbf09e6566ec5d9675d8c
5
+ SHA512:
6
+ metadata.gz: 7227668d02bb75669956c66f12f547fb50ca96594bf69514d33025493f783ef8c9758ca245e3f8d44d77b92f17f0e821e3265b60ddbd50c26da633e7730591e3
7
+ data.tar.gz: 29dee1330d0edba6f1801b3e5f7a768c2f47650a1080de218a2b1774421567caad7228231d88390478e5099aae478a7fe01f13b9f256eb649ff0bd938fab044f
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
@@ -0,0 +1 @@
1
+ *.gem
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.4
6
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ active-record-atomic (0.2.0)
5
+ activerecord (>= 4.2, < 5.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (4.2.4)
11
+ activesupport (= 4.2.4)
12
+ builder (~> 3.1)
13
+ activerecord (4.2.4)
14
+ activemodel (= 4.2.4)
15
+ activesupport (= 4.2.4)
16
+ arel (~> 6.0)
17
+ activesupport (4.2.4)
18
+ i18n (~> 0.7)
19
+ json (~> 1.7, >= 1.7.7)
20
+ minitest (~> 5.1)
21
+ thread_safe (~> 0.3, >= 0.3.4)
22
+ tzinfo (~> 1.1)
23
+ arel (6.0.3)
24
+ builder (3.2.2)
25
+ database_cleaner (1.5.0)
26
+ diff-lcs (1.2.5)
27
+ i18n (0.7.0)
28
+ json (1.8.3)
29
+ minitest (5.8.1)
30
+ rake (10.4.2)
31
+ rspec (3.3.0)
32
+ rspec-core (~> 3.3.0)
33
+ rspec-expectations (~> 3.3.0)
34
+ rspec-mocks (~> 3.3.0)
35
+ rspec-core (3.3.2)
36
+ rspec-support (~> 3.3.0)
37
+ rspec-expectations (3.3.1)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.3.0)
40
+ rspec-mocks (3.3.2)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.3.0)
43
+ rspec-support (3.3.0)
44
+ sqlite3 (1.3.10)
45
+ thread_safe (0.3.5)
46
+ tzinfo (1.2.2)
47
+ thread_safe (~> 0.1)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ active-record-atomic!
54
+ bundler
55
+ database_cleaner
56
+ rake
57
+ rspec
58
+ sqlite3
59
+
60
+ BUNDLED WITH
61
+ 1.10.6
@@ -0,0 +1,44 @@
1
+ # ActiveRecord Atomic
2
+
3
+ Convenience methods for updating your ActiveRecord resources atomically, using pessimistic locking.
4
+
5
+ ## Usage
6
+
7
+ Install via gem:
8
+
9
+ ```shell
10
+ $ gem install active-record-atomic
11
+ ```
12
+
13
+ Use it:
14
+
15
+ ```ruby
16
+ Post.save_atomically(1) do |post|
17
+ post.title = "new title"
18
+ end
19
+ ```
20
+
21
+ ## Licence
22
+
23
+ ```
24
+ Copyright (c) 2015 Black Square Media
25
+
26
+ Permission is hereby granted, free of charge, to any person obtaining
27
+ a copy of this software and associated documentation files (the
28
+ "Software"), to deal in the Software without restriction, including
29
+ without limitation the rights to use, copy, modify, merge, publish,
30
+ distribute, sublicense, and/or sell copies of the Software, and to
31
+ permit persons to whom the Software is furnished to do so, subject to
32
+ the following conditions:
33
+
34
+ The above copyright notice and this permission notice shall be
35
+ included in all copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
41
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
42
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
43
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44
+ ```
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task default: :spec
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'active-record-atomic'
5
+ s.version = '0.2.0'
6
+ s.authors = ['Black Square Media Ltd']
7
+ s.email = ['info@blacksquaremedia.com']
8
+ s.summary = %{Atomic updates to ActiveRecord models}
9
+ s.description = %{}
10
+ s.homepage = 'https://github.com/bsm/active-record-atomic'
11
+ s.license = 'MIT'
12
+
13
+ s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^spec/}) }
14
+ s.test_files = `git ls-files -z -- spec/*`.split("\x0")
15
+ s.require_paths = ['lib']
16
+ s.required_ruby_version = '>= 1.9.3'
17
+
18
+ s.add_dependency 'activerecord', '>= 4.2', '< 5.0'
19
+ s.add_development_dependency 'bundler'
20
+ s.add_development_dependency 'sqlite3'
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'rspec'
23
+ s.add_development_dependency 'database_cleaner'
24
+
25
+ end
26
+
@@ -0,0 +1 @@
1
+ require 'active_record_atomic'
@@ -0,0 +1,48 @@
1
+ require 'active_record'
2
+
3
+ module ActiveRecord
4
+ module Locking
5
+ module Pessimistic
6
+ module ConvenienceMethods
7
+
8
+ # Finds a records with the given ID, applies the block, then calls save. Returns the record.
9
+ #
10
+ # All options are passed to #save, except for:
11
+ # <tt>lock:</tt> - pass an SQL locking clause to append the end of the SELECT statement, defaults to "FOR UPDATE"
12
+ #
13
+ # Example:
14
+ #
15
+ # Post.find_and_save_atomically(1, validate: false, lock: "FOR SHARE") {|post| post.title = "new title" s}
16
+ #
17
+ def find_and_save_atomically(id, opts = {}, &block)
18
+ _find_and_apply_atomically(:save, id, opts, &block)
19
+ end
20
+
21
+ # Same as find_and_save_atomically, just calls #save! on the model
22
+ def find_and_save_atomically!(id, opts = {}, &block)
23
+ _find_and_apply_atomically(:save!, id, opts, &block)
24
+ end
25
+
26
+ # Find and update attributes, but atomically
27
+ def find_and_update_atomically(id, attrs, opts = {})
28
+ find_and_save_atomically(id, opts) {|r| r.assign_attributes attrs }
29
+ end
30
+
31
+ private
32
+
33
+ def _find_and_apply_atomically(method, id, opts = {}, &block)
34
+ kind = opts.delete(:lock) || true
35
+ transaction do
36
+ record = lock(kind).find(id)
37
+ block.call(record)
38
+ record.send(method, opts)
39
+ record
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ ActiveRecord::Base.extend ActiveRecord::Locking::Pessimistic::ConvenienceMethods
@@ -0,0 +1 @@
1
+ require 'active_record/locking/pessimistic/convenience_methods'
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ActiveRecord::Locking::Pessimistic::ConvenienceMethods do
4
+
5
+ let!(:subject) { Post.create(title: "big news") }
6
+
7
+ it 'should save atomically' do
8
+ update = Proc.new do |id|
9
+ Post.connection.reconnect!
10
+ Post.find_and_save_atomically!(id) {|post| post.views += 1 }
11
+ end
12
+
13
+ (1..20).map do |_|
14
+ Thread.new(subject.id, &update)
15
+ end.each(&:join)
16
+
17
+ expect(subject.reload.views).to eq(20)
18
+ end
19
+
20
+ end
@@ -0,0 +1,25 @@
1
+ ENV['RACK_ENV'] ||= "test"
2
+
3
+ require 'active-record-atomic'
4
+ require 'rspec'
5
+ require 'database_cleaner'
6
+
7
+ DatabaseCleaner.strategy = :truncation
8
+
9
+ RSpec.configure do |c|
10
+ c.after :each do
11
+ DatabaseCleaner.clean
12
+ end
13
+ end
14
+
15
+ tempfile = Tempfile.new ["atomic", "test"]
16
+
17
+ ActiveRecord::Base.configurations["test"] = { 'adapter' => 'sqlite3', 'database' => tempfile.path, 'pool' => 20 }
18
+ ActiveRecord::Base.establish_connection(:test)
19
+ ActiveRecord::Base.connection.create_table :posts do |t|
20
+ t.string :title
21
+ t.integer :views, :null => false, :default => 0
22
+ end
23
+
24
+ class Post < ActiveRecord::Base
25
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active-record-atomic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Black Square Media Ltd
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-02 00:00:00.000000000 Z
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
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.2'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: database_cleaner
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: ''
104
+ email:
105
+ - info@blacksquaremedia.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".editorconfig"
111
+ - ".gitignore"
112
+ - ".travis.yml"
113
+ - Gemfile
114
+ - Gemfile.lock
115
+ - README.md
116
+ - Rakefile
117
+ - active-record-atomic.gemspec
118
+ - lib/active-record-atomic.rb
119
+ - lib/active_record/locking/pessimistic/convenience_methods.rb
120
+ - lib/active_record_atomic.rb
121
+ - spec/lib/active_record_atomic_spec.rb
122
+ - spec/spec_helper.rb
123
+ homepage: https://github.com/bsm/active-record-atomic
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: 1.9.3
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.4.7
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Atomic updates to ActiveRecord models
147
+ test_files:
148
+ - spec/lib/active_record_atomic_spec.rb
149
+ - spec/spec_helper.rb