retryable_record 0.2.0 → 0.3.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: fd3a3c47c9c5b116e53ab13546b73a141c5c6248
4
+ data.tar.gz: 1e4f1d3b5dd0ff89a5cda4f4389ee46fb05f2dff
5
+ SHA512:
6
+ metadata.gz: a5d6b2ab96bb4f6eda6f901aec0f294d29a69d366fe960b356478ea2d937b4d331ff7f86a13b62d6f46b2cebf61d1b019d48211d8ca263f56539cb87b5cdea17
7
+ data.tar.gz: 6c74189f910b32da855c101a9405cd5fd27ebda3f2005cf788b54a5c36690ce908ee23968b4d9eac5a5fcd8b699fbd7dfde01c119cdd3d570439439b0dfc32d3
data/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ The MIT License (MIT)
2
+
1
3
  Copyright (c) 2010 Peter Suschlik
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
@@ -46,6 +46,29 @@ You can use +retryable_record+ in 3 different ways:
46
46
  user.save!
47
47
  end
48
48
 
49
+ == Option +attempts+
50
+
51
+ There is also an option +attempts+ to limit the number of retries.
52
+ If no attempts option is specified, it's assumed to be possibly infinte attempts until
53
+ an ActiveRecord::StaleObjectError is not raised. The +attempts+ option works in all three forms.
54
+
55
+ Here is the Module inclusion example with an attempts option used.
56
+
57
+ require 'retryable_record'
58
+
59
+ class User < ActiveRecord::Base
60
+ include RetryableRecord
61
+ end
62
+
63
+ user = User.first
64
+
65
+ user.retryable(:attempts => 5) do
66
+ user.username = "foo"
67
+ user.save!
68
+ end
69
+
70
+ After 5 attempts, this will just re-raise the ActiveRecord::StaleObjectError anyway.
71
+
49
72
  == Optimistic locking (lock_version column)
50
73
 
51
74
  ActiveRecord migration needs to support optimistic locking. See http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
@@ -69,9 +92,14 @@ Inspired by
69
92
  * http://github.com/nfedyashev/retryable
70
93
  * http://vision-media.ca/resources/ruby/better-ruby-retryable-method (broken)
71
94
 
95
+ === Contributors
96
+
97
+ Thanks to all contributions from awesome people[https://github.com/neopoly/retryable_record/contributors]!
98
+
72
99
  == TODO
73
100
 
74
- * Better example in README
101
+ * Improve README example
102
+ * Add Changelog!
75
103
  * Intergration test with ActiveRecord
76
104
 
77
105
  == Note on Patches/Pull Requests
@@ -16,6 +16,19 @@ require 'active_record/base'
16
16
  # user.save!
17
17
  # end
18
18
  #
19
+ # == Example using attempts
20
+ #
21
+ # class User < ActiveRecord::Base
22
+ # include RetryableRecord
23
+ # end
24
+ #
25
+ # user = User.first
26
+ #
27
+ # user.retryable(:attempts => 2) do
28
+ # user.username = "foo"
29
+ # user.save!
30
+ # end
31
+ #
19
32
  module RetryableRecord
20
33
  # Retryable operations on an ActiveRecord +record+.
21
34
  #
@@ -26,16 +39,23 @@ module RetryableRecord
26
39
  # user.save!
27
40
  # end
28
41
  #
29
- def retry(record)
30
- yield
31
- rescue ActiveRecord::StaleObjectError
32
- record.reload
33
- retry
42
+ def retry(record, opts = {})
43
+ attempts = opts[:attempts]
44
+ begin
45
+ yield
46
+ rescue ActiveRecord::StaleObjectError
47
+ unless attempts.nil?
48
+ raise unless attempts > 0
49
+ attempts -= 1
50
+ end
51
+ record.reload
52
+ retry
53
+ end
34
54
  end
35
55
  module_function :retry
36
56
 
37
57
  # Retries operations on an ActiveRecord.
38
- def retryable(&block)
39
- RetryableRecord.retry(self, &block)
58
+ def retryable(opts = {}, &block)
59
+ RetryableRecord.retry(self, opts, &block)
40
60
  end
41
61
  end
@@ -12,8 +12,17 @@ module Kernel
12
12
  # user.save!
13
13
  # end
14
14
  #
15
+ # == Example using attempts
16
+ #
17
+ # require 'retryable_record/import'
18
+ #
19
+ # RetryableRecord(user, :attempts => 3) do
20
+ # user.username = "foo"
21
+ # user.save!
22
+ # end
23
+ #
15
24
  # See RetryableRecord#retry
16
- def RetryableRecord(record, &block)
17
- RetryableRecord.retry(record, &block)
25
+ def RetryableRecord(record, opts = {}, &block)
26
+ RetryableRecord.retry(record, opts, &block)
18
27
  end
19
28
  end
@@ -1,3 +1,3 @@
1
1
  module RetryableRecord
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -14,10 +14,11 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "retryable_record"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = RetryableRecord::VERSION
17
+ gem.license = 'MIT'
17
18
 
18
19
  gem.add_runtime_dependency 'activerecord', '>= 3'
19
20
 
20
21
  gem.add_development_dependency 'rake'
21
22
  gem.add_development_dependency 'rdoc'
22
- gem.add_development_dependency 'minitest', '~> 3.2.0'
23
+ gem.add_development_dependency 'minitest', '~> 5.0.4'
23
24
  end
@@ -1,40 +1,27 @@
1
1
  require 'rubygems'
2
2
 
3
- require 'minitest/unit'
4
3
  require 'minitest/autorun'
5
4
 
6
5
  require 'retryable_record'
7
6
 
8
- class Spec < MiniTest::Spec
9
- end
10
-
11
- class FakeRecord
12
- include RetryableRecord
13
-
14
- attr_accessor :counter
15
-
16
- def initialize(retries_left = 0)
17
- @counter = Hash.new(0)
18
- @counter[:retries_left] = retries_left
19
- end
20
-
21
- def reload
22
- @counter[:reload] += 1
23
- self
24
- end
25
-
26
- def save!
27
- @counter[:save] += 1
28
- end
29
-
30
- def retries_left?
31
- @counter[:retries_left] > 0
32
- end
33
-
34
- def concurrent_modification!
35
- if retries_left?
36
- @counter[:retries_left] -= 1
37
- raise ActiveRecord::StaleObjectError.new self, :save
38
- end
7
+ require 'support/fake_record'
8
+
9
+ class Spec < Minitest::Spec
10
+ # Assert internal counter of the fake +record+.
11
+ #
12
+ # The following counters must be provided:
13
+ # * +:reloads+
14
+ # * +:saves+
15
+ #
16
+ # == Example
17
+ #
18
+ # assert_record :reloads => 2, :saves => 1
19
+ #
20
+ def assert_record(counter={})
21
+ raise ":reloads missing" unless counter.key?(:reloads)
22
+ raise ":saves missing" unless counter.key?(:saves)
23
+
24
+ assert_equal counter[:reloads], record.count_reloads, "unexpected record reloads"
25
+ assert_equal counter[:saves], record.count_saves, "unexpected record saves"
39
26
  end
40
27
  end
@@ -15,9 +15,23 @@ class RetryableRecordImportTest < Spec
15
15
 
16
16
  let(:retries) { 0 }
17
17
 
18
- it "saves and does not retry" do
19
- assert_equal 0, record.counter[:reload]
20
- assert_equal 1, record.counter[:save]
18
+ it "saves and does not retry/reload" do
19
+ assert_record :reloads => 0, :saves => 1
20
+ end
21
+ end
22
+
23
+ describe :RetryableRecord_with_attempts do
24
+ let(:retries) { 2 }
25
+
26
+ it "retries `attempts` times, before re-raising" do
27
+ assert_raises ActiveRecord::StaleObjectError do
28
+ RetryableRecord(record, :attempts => 1) do
29
+ record.concurrent_modification!
30
+ record.save!
31
+ end
32
+ end
33
+
34
+ assert_record :reloads => 1, :saves => 0
21
35
  end
22
36
  end
23
37
  end
@@ -16,8 +16,7 @@ class RetryableRecordTest < Spec
16
16
  let(:retries) { 0 }
17
17
 
18
18
  it "saves and does not retry" do
19
- assert_equal 0, record.counter[:reload]
20
- assert_equal 1, record.counter[:save]
19
+ assert_record :reloads => 0, :saves => 1
21
20
  end
22
21
  end
23
22
 
@@ -25,8 +24,7 @@ class RetryableRecordTest < Spec
25
24
  let(:retries) { 5 }
26
25
 
27
26
  it "saves and reloads 5 times" do
28
- assert_equal 5, record.counter[:reload]
29
- assert_equal 1, record.counter[:save]
27
+ assert_record :reloads => 5, :saves => 1
30
28
  end
31
29
  end
32
30
 
@@ -37,8 +35,7 @@ class RetryableRecordTest < Spec
37
35
  end
38
36
  end
39
37
 
40
- assert_equal 0, record.counter[:reload]
41
- assert_equal 1, record.counter[:save]
38
+ assert_record :reloads => 0, :saves => 1
42
39
  end
43
40
  end
44
41
 
@@ -53,8 +50,22 @@ class RetryableRecordTest < Spec
53
50
  let(:retries) { 0 }
54
51
 
55
52
  it "saves and does not retry" do
56
- assert_equal 0, record.counter[:reload]
57
- assert_equal 1, record.counter[:save]
53
+ assert_record :reloads => 0, :saves => 1
54
+ end
55
+ end
56
+
57
+ describe :retryable_with_attempts do
58
+ let(:retries) { 2 }
59
+
60
+ it "retries `attempts` times, before re-raising" do
61
+ assert_raises ActiveRecord::StaleObjectError do
62
+ record.retryable(:attempts => 1) do
63
+ record.concurrent_modification!
64
+ record.save!
65
+ end
66
+ end
67
+
68
+ assert_record :reloads => 1, :saves => 0
58
69
  end
59
70
  end
60
71
  end
@@ -0,0 +1,40 @@
1
+ class FakeRecord
2
+ include RetryableRecord
3
+
4
+ def initialize(retries_left = 0)
5
+ @counter = Hash.new(0)
6
+ @counter[:retries_left] = retries_left
7
+ end
8
+
9
+ def reload
10
+ counter[:reloads] += 1
11
+ self
12
+ end
13
+
14
+ def save!
15
+ counter[:saves] += 1
16
+ end
17
+
18
+ def count_saves
19
+ counter[:saves]
20
+ end
21
+
22
+ def count_reloads
23
+ counter[:reloads]
24
+ end
25
+
26
+ def concurrent_modification!
27
+ if retries_left?
28
+ counter[:retries_left] -= 1
29
+ raise ActiveRecord::StaleObjectError.new self, :save
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :counter
36
+
37
+ def retries_left?
38
+ counter[:retries_left] > 0
39
+ end
40
+ end
metadata CHANGED
@@ -1,80 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retryable_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Peter Suschlik
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-08 00:00:00.000000000 Z
11
+ date: 2013-06-13 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activerecord
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '3'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '3'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rdoc
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: minitest
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
69
- version: 3.2.0
61
+ version: 5.0.4
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
77
- version: 3.2.0
68
+ version: 5.0.4
78
69
  description: Retries an operation on an ActiveRecord until no StaleObjectError is
79
70
  being raised.
80
71
  email:
@@ -99,38 +90,34 @@ files:
99
90
  - test/helper.rb
100
91
  - test/retryable_record_import_test.rb
101
92
  - test/retryable_record_test.rb
93
+ - test/support/fake_record.rb
102
94
  homepage: https://github.com/neopoly/retryable_record
103
- licenses: []
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
104
98
  post_install_message:
105
99
  rdoc_options: []
106
100
  require_paths:
107
101
  - lib
108
102
  required_ruby_version: !ruby/object:Gem::Requirement
109
- none: false
110
103
  requirements:
111
- - - ! '>='
104
+ - - '>='
112
105
  - !ruby/object:Gem::Version
113
106
  version: '0'
114
- segments:
115
- - 0
116
- hash: -864916261665633026
117
107
  required_rubygems_version: !ruby/object:Gem::Requirement
118
- none: false
119
108
  requirements:
120
- - - ! '>='
109
+ - - '>='
121
110
  - !ruby/object:Gem::Version
122
111
  version: '0'
123
- segments:
124
- - 0
125
- hash: -864916261665633026
126
112
  requirements: []
127
113
  rubyforge_project:
128
- rubygems_version: 1.8.25
114
+ rubygems_version: 2.0.3
129
115
  signing_key:
130
- specification_version: 3
116
+ specification_version: 4
131
117
  summary: Retries an operation on an ActiveRecord until no StaleObjectError is being
132
118
  raised.
133
119
  test_files:
134
120
  - test/helper.rb
135
121
  - test/retryable_record_import_test.rb
136
122
  - test/retryable_record_test.rb
123
+ - test/support/fake_record.rb