Skip to content

Commit

Permalink
Merge pull request #264 from Shopify/seb-unsafe_to_money-cop
Browse files Browse the repository at this point in the history
Add a Money/UnsafeToMoney cop
  • Loading branch information
lavoiesl authored Jun 20, 2023
2 parents eb214be + dac274d commit 9b1c256
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 4 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,17 @@ require:

Money/MissingCurrency:
Enabled: true
```
# If your application is currently handling only one currency,
# it can autocorrect this by specifying a default currency.
ReplacementCurrency: CAD

If your application is currently handling only one currency, it can autocorrect this by specifying a currency under the `Enabled` line:
Money/ZeroMoney:
Enabled: true
# Same here:
# ReplacementCurrency: CAD

```yaml
ReplacementCurrency: 'CAD'
Money/UnsafeToMoney:
Enabled: true
```
## Contributing to money
Expand Down
35 changes: 35 additions & 0 deletions lib/rubocop/cop/money/unsafe_to_money.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

module RuboCop
module Cop
module Money
# Prevents the use of `to_money` because it has inconsistent behaviour.
# Use `Money.new` instead.
#
# @example
# # bad
# "2.000".to_money("USD") #<Money value:2000.00 currency:USD>
#
# # good
# Money.new("2.000", "USD") #<Money value:2.00 currency:USD>
class UnsafeToMoney < Cop
MSG = '`to_money` has inconsistent behaviour. Use `Money.new` instead.'.freeze

def on_send(node)
return unless node.method?(:to_money)
return if node.receiver.is_a?(AST::NumericNode)

add_offense(node, location: :selector)
end

def autocorrect(node)
lambda do |corrector|
receiver = node.receiver.source
args = node.arguments.map(&:source)
args.prepend(receiver)
corrector.replace(node.loc.expression, "Money.new(#{args.join(', ')})")
end
end
end
end
end
end
57 changes: 57 additions & 0 deletions spec/rubocop/cop/money/unsafe_to_money_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

require_relative '../../../rubocop_helper'
require 'rubocop/cop/money/unsafe_to_money'

RSpec.describe RuboCop::Cop::Money::UnsafeToMoney do
subject(:cop) { described_class.new(config) }

let(:config) { RuboCop::Config.new }

context 'with default configuration' do
it 'does not register an offense for literal integer' do
expect_no_offenses(<<~RUBY)
1.to_money
RUBY
end

it 'does not register an offense for literal float' do
expect_no_offenses(<<~RUBY)
1.000.to_money
RUBY
end

it 'registers an offense and corrects for Money.new without a currency argument' do
expect_offense(<<~RUBY)
'2.000'.to_money
^^^^^^^^ #{described_class::MSG}
RUBY

expect_correction(<<~RUBY)
Money.new('2.000')
RUBY
end

it 'registers an offense and corrects for Money.new with a currency argument' do
expect_offense(<<~RUBY)
'2.000'.to_money('USD')
^^^^^^^^ #{described_class::MSG}
RUBY

expect_correction(<<~RUBY)
Money.new('2.000', 'USD')
RUBY
end

it 'registers an offense and corrects for Money.new with a complex receiver' do
expect_offense(<<~RUBY)
obj.money.to_money('USD')
^^^^^^^^ #{described_class::MSG}
RUBY

expect_correction(<<~RUBY)
Money.new(obj.money, 'USD')
RUBY
end
end
end

0 comments on commit 9b1c256

Please sign in to comment.