Class: RuboCop::Cop::Rails::SaveBang
- Inherits:
-
RuboCop::Cop
- Object
- RuboCop::Cop
- RuboCop::Cop::Rails::SaveBang
- Includes:
- NegativeConditional
- Defined in:
- lib/rubocop/cop/rails/save_bang.rb
Overview
This cop identifies possible cases where Active Record save! or related should be used instead of save because the model might have failed to save and an exception is better than unhandled failure.
This will allow:
-
update or save calls, assigned to a variable, or used as a condition in an if/unless/case statement.
-
create calls, assigned to a variable that then has a call to ‘persisted?`, or whose return value is checked by `persisted?` immediately
-
calls if the result is explicitly returned from methods and blocks, or provided as arguments.
-
calls whose signature doesn’t look like an ActiveRecord persistence method.
By default it will also allow implicit returns from methods and blocks. that behavior can be turned off with ‘AllowImplicitReturn: false`.
You can permit receivers that are giving false positives with ‘AllowedReceivers: []`
Constant Summary collapse
- MSG =
'Use `%<prefer>s` instead of `%<current>s` if the return ' \ 'value is not checked.'
- CREATE_MSG =
(MSG + ' Or check `persisted?` on model returned from ' \ '`%<current>s`.').freeze
- CREATE_CONDITIONAL_MSG =
'`%<current>s` returns a model which is ' \ 'always truthy.'
- CREATE_PERSIST_METHODS =
%i[create create_or_find_by first_or_create find_or_create_by].freeze
- MODIFY_PERSIST_METHODS =
%i[save update update_attributes destroy].freeze
- PERSIST_METHODS =
(CREATE_PERSIST_METHODS + MODIFY_PERSIST_METHODS).freeze
Instance Method Summary collapse
- #after_leaving_scope(scope, _variable_table) ⇒ Object
- #autocorrect(node) ⇒ Object
- #check_assignment(assignment) ⇒ Object
- #join_force?(force_class) ⇒ Boolean
-
#on_send(node) ⇒ Object
(also: #on_csend)
rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
Instance Method Details
#after_leaving_scope(scope, _variable_table) ⇒ Object
123 124 125 126 127 128 129 |
# File 'lib/rubocop/cop/rails/save_bang.rb', line 123 def after_leaving_scope(scope, _variable_table) scope.variables.each_value do |variable| variable.assignments.each do |assignment| check_assignment(assignment) end end end |
#autocorrect(node) ⇒ Object
156 157 158 159 160 161 |
# File 'lib/rubocop/cop/rails/save_bang.rb', line 156 def autocorrect(node) save_loc = node.loc.selector new_method = "#{node.method_name}!" ->(corrector) { corrector.replace(save_loc, new_method) } end |
#check_assignment(assignment) ⇒ Object
131 132 133 134 135 136 137 138 139 |
# File 'lib/rubocop/cop/rails/save_bang.rb', line 131 def check_assignment(assignment) node = right_assignment_node(assignment) return unless node&.send_type? return unless persist_method?(node, CREATE_PERSIST_METHODS) return if persisted_referenced?(assignment) add_offense_for_node(node, CREATE_MSG) end |
#join_force?(force_class) ⇒ Boolean
119 120 121 |
# File 'lib/rubocop/cop/rails/save_bang.rb', line 119 def join_force?(force_class) force_class == VariableForce end |
#on_send(node) ⇒ Object Also known as: on_csend
rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/rubocop/cop/rails/save_bang.rb', line 142 def on_send(node) return unless persist_method?(node) return if return_value_assigned?(node) return if implicit_return?(node) return if check_used_in_condition_or_compound_boolean(node) return if argument?(node) return if explicit_return?(node) return if checked_immediately?(node) add_offense_for_node(node) end |