Skip to content

Commit

Permalink
Merge pull request #6040 from tvdeyen/autoload-spree-money
Browse files Browse the repository at this point in the history
Make Spree::Money autoloadable
  • Loading branch information
tvdeyen authored Jan 9, 2025
2 parents bb4e024 + 95c39b2 commit b841e8a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 121 deletions.
3 changes: 1 addition & 2 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ Layout/SpaceAroundOperators:
- "backend/spec/features/admin/orders/order_details_spec.rb"
- "bin/__rspec"
- "bin/rspec"
- "core/lib/spree/money.rb"
- "core/spec/models/spree/order/number_generator_spec.rb"

# Offense count: 8
Expand Down Expand Up @@ -413,7 +412,7 @@ Rails/OutputSafety:
- "core/app/helpers/spree/base_helper.rb"
- "core/app/helpers/spree/checkout_helper.rb"
- "core/app/helpers/spree/products_helper.rb"
- "core/lib/spree/money.rb"
- "core/app/models/spree/money.rb"

# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
Expand Down
120 changes: 120 additions & 0 deletions core/app/models/spree/money.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# frozen_string_literal: true

module Spree
# Spree::Money is a relatively thin wrapper around Monetize which handles
# formatting via Spree::Config.
class Money
include Comparable
DifferentCurrencyError = Class.new(StandardError)

class << self
attr_accessor :default_formatting_rules

def parse(amount, currency = Spree::Config[:currency])
new(parse_to_money(amount, currency))
end

# @api private
def parse_to_money(amount, currency)
::Monetize.parse(amount, currency)
end
end
self.default_formatting_rules = {
# Ruby money currently has this as false, which is wrong for the vast
# majority of locales.
sign_before_symbol: true
}

attr_reader :money

delegate :cents, :currency, :to_d, :zero?, to: :money

# @param amount [Money, #to_s] the value of the money object
# @param options [Hash] the default options for formatting the money object See #format
def initialize(amount, options = {})
if amount.is_a?(::Money)
@money = amount
else
currency = options[:currency] || Spree::Config[:currency]

@money = Monetize.from_string(amount, currency)
end
@options = Spree::Money.default_formatting_rules.merge(options)
end

# @return [String] the value of this money object formatted according to
# its options
def to_s
format
end

# @param options [Hash, String] the options for formatting the money object
# @option options [Boolean] with_currency when true, show the currency
# @option options [Boolean] no_cents when true, round to the closest dollar
# @option options [String] decimal_mark the mark for delimiting the
# decimals
# @option options [String, false, nil] thousands_separator the character to
# delimit powers of 1000, if one is desired, otherwise false or nil
# @option options [Boolean] sign_before_symbol when true the sign of the
# value comes before the currency symbol
# @option options [:before, :after] symbol_position the position of the
# currency symbol
# @return [String] the value of this money object formatted according to
# its options
def format(options = {})
@money.format(@options.merge(options))
end

# @note If you pass in options, ensure you pass in the { html_wrap: true } as well.
# @param options [Hash] additional formatting options
# @return [String] the value of this money object formatted according to
# its options and any additional options, by default with html_wrap.
def to_html(options = { html_wrap: true })
output = format(options)
# Maintain compatibility by checking html option renamed to html_wrap.
if options[:html_wrap]
output = output.html_safe
end
output
end

# (see #to_s)
def as_json(*)
to_s
end

def <=>(other)
if !other.respond_to?(:money)
raise TypeError, "Can't compare #{other.class} to Spree::Money"
end
if currency != other.currency
# By default, ::Money will try to run a conversion on `other.money` and
# try a comparison on that. We do not want any currency conversion to
# take place so we'll catch this here and raise an error.
raise(
DifferentCurrencyError,
"Can't compare #{currency} with #{other.currency}"
)
end
@money <=> other.money
end

# Delegates comparison to the internal ruby money instance.
#
# @see http://www.rubydoc.info/gems/money/Money/Arithmetic#%3D%3D-instance_method
def ==(other)
raise TypeError, "Can't compare #{other.class} to Spree::Money" if !other.respond_to?(:money)
@money == other.money
end

def -(other)
raise TypeError, "Can't subtract #{other.class} to Spree::Money" if !other.respond_to?(:money)
self.class.new(@money - other.money)
end

def +(other)
raise TypeError, "Can't add #{other.class} to Spree::Money" if !other.respond_to?(:money)
self.class.new(@money + other.money)
end
end
end
1 change: 0 additions & 1 deletion core/lib/spree/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ class GatewayError < RuntimeError; end

require 'spree/i18n'
require 'spree/localized_number'
require 'spree/money'
require 'spree/permitted_attributes'

require 'spree/core/importer'
Expand Down
123 changes: 5 additions & 118 deletions core/lib/spree/money.rb
Original file line number Diff line number Diff line change
@@ -1,120 +1,7 @@
# frozen_string_literal: true

module Spree
# Spree::Money is a relatively thin wrapper around Monetize which handles
# formatting via Spree::Config.
class Money
include Comparable
DifferentCurrencyError = Class.new(StandardError)

class <<self
attr_accessor :default_formatting_rules

def parse(amount, currency = Spree::Config[:currency])
new(parse_to_money(amount, currency))
end

# @api private
def parse_to_money(amount, currency)
::Monetize.parse(amount, currency)
end
end
self.default_formatting_rules = {
# Ruby money currently has this as false, which is wrong for the vast
# majority of locales.
sign_before_symbol: true
}

attr_reader :money

delegate :cents, :currency, :to_d, :zero?, to: :money

# @param amount [Money, #to_s] the value of the money object
# @param options [Hash] the default options for formatting the money object See #format
def initialize(amount, options = {})
if amount.is_a?(::Money)
@money = amount
else
currency = options[:currency] || Spree::Config[:currency]

@money = Monetize.from_string(amount, currency)
end
@options = Spree::Money.default_formatting_rules.merge(options)
end

# @return [String] the value of this money object formatted according to
# its options
def to_s
format
end

# @param options [Hash, String] the options for formatting the money object
# @option options [Boolean] with_currency when true, show the currency
# @option options [Boolean] no_cents when true, round to the closest dollar
# @option options [String] decimal_mark the mark for delimiting the
# decimals
# @option options [String, false, nil] thousands_separator the character to
# delimit powers of 1000, if one is desired, otherwise false or nil
# @option options [Boolean] sign_before_symbol when true the sign of the
# value comes before the currency symbol
# @option options [:before, :after] symbol_position the position of the
# currency symbol
# @return [String] the value of this money object formatted according to
# its options
def format(options = {})
@money.format(@options.merge(options))
end

# @note If you pass in options, ensure you pass in the { html_wrap: true } as well.
# @param options [Hash] additional formatting options
# @return [String] the value of this money object formatted according to
# its options and any additional options, by default with html_wrap.
def to_html(options = { html_wrap: true })
output = format(options)
# Maintain compatibility by checking html option renamed to html_wrap.
if options[:html_wrap]
output = output.html_safe
end
output
end

# (see #to_s)
def as_json(*)
to_s
end

def <=>(other)
if !other.respond_to?(:money)
raise TypeError, "Can't compare #{other.class} to Spree::Money"
end
if currency != other.currency
# By default, ::Money will try to run a conversion on `other.money` and
# try a comparison on that. We do not want any currency conversion to
# take place so we'll catch this here and raise an error.
raise(
DifferentCurrencyError,
"Can't compare #{currency} with #{other.currency}"
)
end
@money <=> other.money
end

# Delegates comparison to the internal ruby money instance.
#
# @see http://www.rubydoc.info/gems/money/Money/Arithmetic#%3D%3D-instance_method
def ==(other)
raise TypeError, "Can't compare #{other.class} to Spree::Money" if !other.respond_to?(:money)
@money == other.money
end

def -(other)
raise TypeError, "Can't subtract #{other.class} to Spree::Money" if !other.respond_to?(:money)
self.class.new(@money - other.money)
end

def +(other)
raise TypeError, "Can't add #{other.class} to Spree::Money" if !other.respond_to?(:money)
self.class.new(@money + other.money)
end
end
end
Spree.deprecator.warn(
<<~MSG
The file "#{__FILE__}" does not need to be `require`d any longer, it is now autoloaded.
MSG
)
File renamed without changes.

0 comments on commit b841e8a

Please sign in to comment.