retryable_record 0.2.0 → 0.3.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: 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