active-record-atomic 0.2.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.
@@ -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