activerecord-transactionable 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 134ddc5a2d594c0ddf785ff45ff5ec2661f0660e
4
- data.tar.gz: 3fb2afbaa552fbec81c70bb7824874646efacfde
3
+ metadata.gz: 303aa3641ecea594ad05589a09a3e9d6a12c31ba
4
+ data.tar.gz: af04e048d3dbd066d5bd5fe917f49a6a421eba53
5
5
  SHA512:
6
- metadata.gz: 78d23d187b037feb977342e6120d001c92699b7db37dfd4cb953bd4add4dcc91762f822c359ce841dd3725d6c6232fcfe375d4b2f99f61415a9fc9aabff9e5a7
7
- data.tar.gz: 71d8ae5296a058dca0ff90c746b15320824556c6fde806abcdf3e01e3556e7b8a4b1e09e438bec82473222c3b60631525e9b67632f275317122055b872b49590
6
+ metadata.gz: d6314257b6007bd563dd9dbb682cd1ffa415fe42e7edb337303aa5ae04156fc4977e4f3bb368151fd29675e2a9af7364cf2ebff2d21542b80a9a89ab9795801a
7
+ data.tar.gz: fbb91d3c932c6739a0847018ce6a227beb61845109c8133542de71b3fe59e1dc4cae55cf0825632163a8d623e1150f8d3ea788ece283e8ba8fa15de460107c72
data/README.md CHANGED
@@ -25,17 +25,64 @@ Or install it yourself as:
25
25
 
26
26
  ## Usage
27
27
 
28
+ ```
29
+ class Car < ActiveRecord::Base
30
+ include Activerecord::Transactionable # Note lowercase "r" in Activerecord (different namespace than rails' module)
31
+
32
+ validates_presence_of :name
33
+ end
34
+ ```
35
+
28
36
  When creating, saving, deleting within the transaction make sure to use the bang methods (`!`) in order to ensure a rollback on failure.
29
37
 
38
+ When everything works:
30
39
  ```
31
40
  car = Car.new(name: "Fiesta")
32
41
  car.transaction_wrapper do
33
42
  car.save!
34
43
  end
44
+ car.persisted? # => true
45
+ ```
46
+
47
+ When something goes wrong:
48
+ ```
49
+ car = Car.new(name: nil)
50
+ car.transaction_wrapper do
51
+ car.save!
52
+ end
53
+ car.persisted? # => false
54
+ car.errors.full_messages # => ["Name can't be blank"]
35
55
  ```
36
56
 
57
+ These examples are too simple to be useful with transactions, but if you are working with multiple records then it will make sense.
58
+
37
59
  Also see the specs.
38
60
 
61
+ If you need to lock the car as well as have a transaction (note: will reload the `car`):
62
+ ```
63
+ car = Car.new(name: nil)
64
+ car.transaction_wrapper(lock: true) do # uses ActiveRecord's with_lock
65
+ car.save!
66
+ end
67
+ car.persisted? # => false
68
+ car.errors.full_messages # => ["Name can't be blank"]
69
+ ```
70
+
71
+ If you need to know if the transaction succeeded:
72
+ ```
73
+ car = Car.new(name: nil)
74
+ result = car.transaction_wrapper(lock: true) do # uses ActiveRecord's with_lock
75
+ car.save!
76
+ end
77
+ result # => true, false or nil
78
+ ```
79
+
80
+ Meanings of `transaction_wrapper` return values:
81
+
82
+ **nil** - ActiveRecord::Rollback was raised, and then caught by the transaction, and not re-raised; the transaction failed.
83
+ **false** - An error was raised which was handled by the transaction_wrapper; the transaction failed.
84
+ **true** - The transaction was a success.
85
+
39
86
  ## Development
40
87
 
41
88
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -2,7 +2,7 @@ require "activerecord/transactionable/version"
2
2
  require "active_model"
3
3
  require "active_record"
4
4
 
5
- module Activerecord
5
+ module Activerecord # Note lowercase "r" in Activerecord (different namespace than rails' module)
6
6
  # SRP: Provides an example of correct behavior for wrapping transactions.
7
7
  # NOTE: Rails' transactions are per-database connection, not per-model, nor per-instance,
8
8
  # see: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
@@ -20,6 +20,34 @@ module Activerecord
20
20
 
21
21
  module ClassMethods
22
22
  def transaction_wrapper(object: nil, **args)
23
+ if object
24
+ if args[:lock]
25
+ # Note with_lock will reload object!
26
+ object.with_lock do
27
+ error_handler(object: object, **args) do
28
+ yield
29
+ end
30
+ end
31
+ else
32
+ object.transaction do
33
+ error_handler(object: object, **args) do
34
+ yield
35
+ end
36
+ end
37
+ end
38
+ else
39
+ raise ArgumentError, "No object to lock!" if args[:lock]
40
+ ActiveRecord::Base.transaction do
41
+ error_handler(object: object, **args) do
42
+ yield
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def error_handler(object: nil, **args)
23
51
  rescued_errors = Array(args[:rescued_errors])
24
52
  prepared_errors = Array(args[:prepared_errors])
25
53
  retriable_errors = Array(args[:retriable_errors])
@@ -29,10 +57,12 @@ module Activerecord
29
57
  already_been_added_to_self, needing_added_to_self = rescued_errors.partition {|error_class| prepared_errors.include?(error_class)}
30
58
  re_try = false
31
59
  begin
32
- ActiveRecord::Base.transaction do
33
- yield
34
- end
35
- true # <= make the return value meaningful. Meaning is either: transaction succeeded, OR raised ActiveRecord::Rollback
60
+ # If the block we yield to here raises an error that is not caught below the `true` will not get hit.
61
+ # If the error is rescued higher up, like where the transaction in active record
62
+ # rescues ActiveRecord::Rollback without re-raising, then transaction_wrapper will return nil
63
+ # If the error is not rescued higher up the error will continue to bubble
64
+ yield
65
+ true # <= make the return value meaningful. Meaning: transaction succeeded, no errors raised
36
66
  rescue *reraisable_errors => error
37
67
  # This has highest precedence because raising is the most critical functionality of a raised error to keep
38
68
  # if that is in the intended behavior, and this way a specific child of StandardError can be reraised while
@@ -1,5 +1,5 @@
1
1
  module Activerecord
2
2
  module Transactionable
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-transactionable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Boling
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-28 00:00:00.000000000 Z
11
+ date: 2016-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
118
  version: '0'
119
119
  requirements: []
120
120
  rubyforge_project:
121
- rubygems_version: 2.4.8
121
+ rubygems_version: 2.4.6
122
122
  signing_key:
123
123
  specification_version: 4
124
124
  summary: Do ActiveRecord transactions the right way.