-
-
Notifications
You must be signed in to change notification settings - Fork 279
Fix #400 Add Rails/TransactionRequiresNew cop #414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
# Common configuration. | ||
|
||
inherit_mode: | ||
merge: | ||
- Exclude | ||
|
@@ -695,6 +694,11 @@ Rails/TimeZone: | |
- strict | ||
- flexible | ||
|
||
Rails/TransactionRequiresNew: | ||
Description: 'When using a transaction requires that the argument `requires_new: true` is passed.' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the reference will help users. Reference: https://makandracards.com/makandra/42885-nested-activerecord-transaction-pitfalls |
||
Enabled: true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I know, it's not a common rule. I think it should be |
||
VersionAdded: '2.10' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this auto-correction will be false because the behavior will change. SafeAutoCorrect: false |
||
|
||
Rails/UniqBeforePluck: | ||
Description: 'Prefer the use of uniq or distinct before pluck.' | ||
Enabled: true | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4202,6 +4202,37 @@ Time.at(timestamp).in_time_zone | |
* https://rails.rubystyle.guide#time | ||
* http://danilenko.org/2012/7/6/rails_timezones | ||
|
||
== Rails/TransactionRequiresNew | ||
|
||
|=== | ||
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged | ||
|
||
| Enabled | ||
| Yes | ||
| Yes | ||
| 2.10 | ||
| - | ||
|=== | ||
|
||
|
||
|
||
=== Examples | ||
|
||
==== Enabled (default) | ||
|
||
[source,ruby] | ||
---- | ||
# bad | ||
ActiveRecord::Base.transaction do | ||
some_database_stuff | ||
end | ||
|
||
# good | ||
ActiveRecord::Base.transaction(requires_new: true) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it okay not to specify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that they work independently. Anyways, I will do some research and testing on this. I have read many blogs/articles about it so far but none of them seem super clear on this topic. I will try to figure it out and come up with some good documentation and examples. I might even write a medium article 😂 |
||
some_database_stuff | ||
end | ||
---- | ||
|
||
== Rails/UniqBeforePluck | ||
|
||
|=== | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,54 @@ | ||||||
# frozen_string_literal: true | ||||||
|
||||||
module RuboCop | ||||||
module Cop | ||||||
module Rails | ||||||
# @example Enabled (default) | ||||||
# # bad | ||||||
# ActiveRecord::Base.transaction do | ||||||
# some_database_stuff | ||||||
# end | ||||||
# | ||||||
# # good | ||||||
# ActiveRecord::Base.transaction(requires_new: true) do | ||||||
# some_database_stuff | ||||||
# end | ||||||
# | ||||||
class TransactionRequiresNew < Base | ||||||
extend AutoCorrector | ||||||
MSG = 'Always pass "requires_new: true" to transactions.' | ||||||
|
||||||
def_node_matcher :transaction?, <<~PATTERN | ||||||
(block | ||||||
(send _ :transaction ...) | ||||||
(args) {_}) | ||||||
PATTERN | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you replace |
||||||
|
||||||
def_node_matcher :requires_new_argument_passed?, <<~PATTERN | ||||||
(block | ||||||
(send _ :transaction (hash ... (pair (sym :requires_new) (true) ) )) | ||||||
(args) {_}) | ||||||
PATTERN | ||||||
|
||||||
def_node_matcher :arguments_present?, <<~PATTERN | ||||||
(block | ||||||
(send _ :transaction _+ ) | ||||||
(args) {_} | ||||||
) | ||||||
PATTERN | ||||||
|
||||||
def on_block(node) | ||||||
return unless transaction?(node) && !requires_new_argument_passed?(node) | ||||||
|
||||||
if arguments_present?(node) | ||||||
add_offense(node) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps it is possible to |
||||||
else | ||||||
add_offense(node) do |corrector| | ||||||
corrector.replace(node, node.source.gsub('.transaction', '.transaction(requires_new: true)')) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Anyway this myvar.transactional_user_actions(foo: :bar) # before
myvar.transaction(requires_new: true)al_user_actions(foo: :bar) # after etc |
||||||
end | ||||||
end | ||||||
end | ||||||
end | ||||||
end | ||||||
end | ||||||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RuboCop::Cop::Rails::TransactionRequiresNew do | ||
subject(:cop) { described_class.new(config) } | ||
|
||
let(:config) { RuboCop::Config.new } | ||
|
||
context 'when no arguments are passed' do | ||
it 'registers an offense when using `ActiveRecord::Base#transaction`' do | ||
expect_offense(<<~RUBY) | ||
ActiveRecord::Base.transaction do | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Always pass "requires_new: true" to transactions. | ||
end | ||
RUBY | ||
|
||
expect_correction(<<~RUBY) | ||
ActiveRecord::Base.transaction(requires_new: true) do | ||
end | ||
RUBY | ||
end | ||
|
||
it 'registers an offense when using a Model transaction' do | ||
expect_offense(<<~RUBY) | ||
User.transaction do | ||
^^^^^^^^^^^^^^^^^^^ Always pass "requires_new: true" to transactions. | ||
end | ||
RUBY | ||
|
||
expect_correction(<<~RUBY) | ||
User.transaction(requires_new: true) do | ||
end | ||
RUBY | ||
end | ||
end | ||
|
||
context 'when arguments other than `requires_new: true` are passsed' do | ||
it 'registers an offense when using `ActiveRecord::Base#transaction`' do | ||
expect_offense(<<~RUBY) | ||
ActiveRecord::Base.transaction(some_option: false) do | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Always pass "requires_new: true" to transactions. | ||
end | ||
RUBY | ||
|
||
# Does NOT auto-correct when arugments are present | ||
expect_correction(<<~RUBY) | ||
ActiveRecord::Base.transaction(some_option: false) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it can be auto-corrected for this offense. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I suppose it can be. I imagine that it will be safe enough to simply add |
||
end | ||
RUBY | ||
end | ||
|
||
it 'registers an offense when using a Model transaction' do | ||
expect_offense(<<~RUBY) | ||
User.transaction(some_option: true) do | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Always pass "requires_new: true" to transactions. | ||
end | ||
RUBY | ||
|
||
# does NOT auto-correct when arguments are present | ||
expect_correction(<<~RUBY) | ||
User.transaction(some_option: true) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. |
||
end | ||
RUBY | ||
end | ||
end | ||
|
||
context 'when `requires_new: true` is passed' do | ||
it 'does not register an offense when using `ActiveRecord::Base#transaction`' do | ||
expect_no_offenses(<<~RUBY) | ||
ActiveRecord::Base.transaction(requires_new: true) do | ||
end | ||
RUBY | ||
end | ||
|
||
it 'does not register an offense when using a Model transaction' do | ||
expect_no_offenses(<<~RUBY) | ||
User.transaction(requires_new: true) do | ||
end | ||
RUBY | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entry belongs to the
### New features
category. Can you create category and move to it?