Skip to content
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

What is the best way to assign a resource to an attribute? #12

Open
stalcottsmith opened this issue Nov 6, 2023 · 5 comments
Open

What is the best way to assign a resource to an attribute? #12

stalcottsmith opened this issue Nov 6, 2023 · 5 comments

Comments

@stalcottsmith
Copy link

I am exploring whether to use this gem for a multi currency accounting system. It is old and presumably no longer developed but I liked the concept of being able to store the value and currency together and ensure that I would not be summing unlike quantities.

I tried using the ActiveRecord integration and setting the amount column to type string on my line_items model.

Then I tried creating with something like:

entry.line_items.create!(amount: '$40145.00')

In the LineItem class, I have:

serialize :amount, Latinum::Resource

This did not work. Amount is nil. I tried overriding amount=(value) and parsing:

  def amount=(value)
    if value.kind_of?(String)
      bank = Latinum::Bank.new(Latinum::Currencies::Global)
      attributes['amount'] = bank.parse(value)
      byebug
    end
  end

At this point the attribute is still nil. I tried setting it a variety of ways. None seemed to work.

What is the recommended way to do this?

@stalcottsmith
Copy link
Author

I found that I was able to do this with write_attribute(). But I would still like to know the canonical or expected usage.

@ioquatix
Copy link
Owner

ioquatix commented Nov 6, 2023

I'm still using this gem and I suppose you could call it maintained, it's just been stable enough that no changes were required.

The following code was implemented for support of serialize:

# Load a string representation of a resource.
# @parameter string [String | Nil] e.g. "5 NZD" or nil.
# @returns [Resource | Nil] The Resource that represents the parsed string.
def self.load(string)
if string
# Remove any whitespaces
string = string.strip
parse(string) unless string.empty?
end
end
# Dump a string representation of a resource.
# @parameter resource [Resource] The resource to dump.
# @returns [String | Nil] A string that represents the {Resource}.
def self.dump(resource)
resource.to_s if resource
end

A bank object is only needed for formatting and converting resources, it should not be needed for basic interactions with "I have X units of Y."

For actual values, you need to write:

entry.line_items.create!(amount: '40145.00 USD')

If you want rich input, you do need to use the bank configured with the currencies you want. Otherwise, how can you tell $5 is USD, NZD, AUD, etc.

In order to use the bank object, you should configure it in the controller to map the user input before creating the model with attribute values.

e.g.

params['amount'] = bank.parse(params['amount'])

I can write updated documentation with working examples if that helps.

@ioquatix
Copy link
Owner

ioquatix commented Nov 6, 2023

I wonder if we should make Resource.load also check for Resource instances and return it directly to support the above use case if it isn't already handled by AR.

@ioquatix
Copy link
Owner

ioquatix commented Nov 6, 2023

Okay, so with a few minor changes, we can make this work a bit more nicely:

Loading development environment (Rails 7.1.1)
irb(main):001> t = T.new
=> #<T:0x00007f2fb8b88ff0 id: nil, data: nil, amount: nil, created_at: nil, updated_at: nil>
irb(main):002> t.amount = "$5 USD"
=> "$5 USD"
irb(main):003> t.amount
=> #<Latinum::Resource "5.0 USD">

The code is:

require 'latinum/currencies/global'

class T < ApplicationRecord
  BANK = Latinum::Bank.new.tap do |bank|
    bank.import(Latinum::Currencies::Global)
  end
  
  serialize :amount, coder: BANK
end

This uses the bank as a coder, which accepts a wider range of inputs. However, if there is ambiguity, it is resolved according to the bank's internal priority, e.g. $ is used by several currencies.

@ioquatix
Copy link
Owner

ioquatix commented Nov 6, 2023

I've released v1.8.0 with the required changes and added documentation: https://ioquatix.github.io/latinum/guides/activerecord-integration/index.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants