Ask A Question

Notifications

You’re not receiving notifications from this thread.

Custom Attribute Serializers with ActiveRecord in Rails Discussion

This feels much simpler for custom value objects and surprisingly more straightforward than the official way, i.e. registering a new Active Model type.

Great video, thank you.

Reply

you're everywhere my friend!!!
Needed just that recently as well.

Reply

What is the datatype of the price column in event table?

Reply

Chris, could you make a video comparing serialize (that you demonstrated here) vs attribute with a custom type (ActiveRecord::Type) vs using composed_of?

Reply

That is a great idea. I will definitely do that. 👍

Reply

Hello Chris, In which ruby and rails version, this custom attribute sanitizer is supported?

Reply

While this may be obvious, but the serialize method can take 2 types of argument - a class (like Array) or a coder (as coder: CoderClass).

For a custom class coder option is necessary. Additionally, the coder Class needs to be added to Rails.application.config.active_record.yaml_column_permitted_classes during the initialization, unless Rails.application.config.active_record.use_yaml_unsafe_load is used (which is not ideal.)

Reply

When I deal with currency amounts, I always store the amount in the DB as cents and then use some custom methods to generate "_in_dollars" methods that I use on my forms, because most people prefer to write "$1,234.56" dollars instead of "123456" cents.

For my simple currency uses, I think I prefer formatting methods, but if I needed to extract an actual object that I could run methods on from the value, then serialize would make more sense to me.

I've used this model concern for years to handle currency that I store as cents in the DB but my users want to interact with it as dollars:

# app/models/concerns/formatted_currency_in_dollars.rb
module FormattedCurrencyInDollars
  extend ActiveSupport::Concern

  included do
    def self.formatted_currency_in_dollars(model_attribute)
      model_attribute = model_attribute.to_s

      if model_attribute.ends_with?("_in_cents")
        model_attribute = model_attribute.delete_suffix("_in_cents")
      end

      puts model_attribute

      # Getter (cents / 100.0)
      define_method("#{model_attribute}_in_dollars") do
        cents = send("#{model_attribute}_in_cents")
        return nil if cents.blank?
        value = (cents.to_f / 100.0)
        ("%.2f" % value) # Force 2 spaces to right of decimal
      end

      # Setter (dollars * 100.0)
      define_method("#{model_attribute}_in_dollars=") do |value|
        cents = (value.blank? ? nil : (value.to_s.gsub(/[^0-9.\-]/, "").to_f * 100)).to_i
        send("#{model_attribute}_in_cents=", cents)
      end
    end
  end
end

It assumes you have a DB column named "_in_cents", and it will create the getter/setter for the "_in_dollars". I prefer to be very explicit and store the "_in_cents" on my DB column, so that there's no confusion about what value is in the DB. If you were just looking at the DB values, and you saw "price" and the value was "200", is that "2.00 dollars" or "200 dollars"? Being explicit in situations like this avoids any confusion.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 84,256+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.