RubyMoney/money

Marshal dump and ActiveRecord store

yanina-mosilevich opened this issue · 2 comments

we use ActiveRecord store similar to that one from docs

class ExchangeRate < ApplicationRecord
  def self.get_rate(from_iso_code, to_iso_code)
    rate = find_by(from: from_iso_code, to: to_iso_code)
    rate&.rate
  end

  def self.add_rate(from_iso_code, to_iso_code, rate)
    exrate = find_or_initialize_by(from: from_iso_code, to: to_iso_code)
    exrate.rate = rate
    exrate.save!
  end

  def self.each_rate
    return find_each unless block_given?

    find_each do |rate|
      yield rate.from, rate.to, rate.rate
    end
  end
end

# in initializer
  Money.default_bank = Money::Bank::VariableExchange.new('ExchangeRate')

# when you try to dump money object it raises an error:
Marshal.dump(Money.zero('USD')) # => undefined method `marshal_dump' for ExchangeRate:Class (NoMethodError)

As I see here, custom store needs to implement method marshal_dump.
but even if we try to do it in this case - marshal_load (here) tries to instantiate store object.

So it looks like with current implementation you can't use any class as store, just instance of class. Need to update the docs or re-work current approach of dumping store?

Running into the same issue. The default initializer for the Exchange takes MemoryStore.new (an instance of memory store) but the docs show passing the ExchangeRate class (not an instance). The result is that marshal_dump is undefined because you cant marshal the class object.

I was able to get it working with

def self.marshal_dump
   [ExchangeRate]
end

Added to the implementation

@JoshReedSchramm in this case it breaks loading:

Marshal.load Marshal.dump(Money.zero('USD')) # => instance of Class needs to have method `marshal_load' (TypeError)

By default you can marshal the class object, but not in case when you override marshal_dump method.
Compare:

class A; end
Marshal.load(Marshal.dump(A)) # => A

vs

class A
  def self.marshal_dump
     [A]
  end
end
Marshal.load(Marshal.dump(A)) # => instance of Class needs to have method `marshal_load' (TypeError)

In my case I added such temporary fix:

class ExchangeStore
  delegate :get_rate, :add_rate, :each_rate, :transaction, to: :ExchangeRate

  def marshal_dump
    [self.class]
  end
end

Money.default_bank = Money::Bank::VariableExchange.new(ExchangeStore.new)

but ideally it should be fixed in Money gem.