Search code examples
ruby-on-railsrubyactiverecord

Custom method as ID


I have two models:

class Product
  belongs_to :storage_box
end

class StorageBox
  validates :key
            presence: true,
            uniqueness: { scope: %i[kind] }

  validates :kind
            presence: true
  def code
    "#{key}#{kind.to_s.rjust(4, '0')}"
  end
end

I would like to be able to set the storage_box relation based on the code method:

Product.new(storage_box: '150001')

How should I proceed to achieve this? I'm not sure if the Product should handle this (seems not "rails" to do it there) or to instruct the StorageBox how to find base on the code.


Solution

  • There are many ways you could code Product.new(storage_box: '150001'). For example you could probably overwrite the storage_box= method to detect a string input and generate a new StorageBox instance to replace the string. But I would strongly recommend you do not!

    The problem with that approach is that you are making the standard ActiveRecord methods work in ways that they weren't designed to. In my experience that always causes unexpected consequencies later on. Also it will confuse the next developer who touches your code.

    Instead I recommend you write a dedicated class method:

    def self.new_in_storage_box(code)
      key = code[0..-5]
      kind = code[-4..-1]
      storage_box = StorageBox.find_or_create_by(key:, kind:)
      new(storage_box:)
    end
    

    Your call then becomes Product.new_in_storage_box('15001')

    Even better, I'd suggest you move the breakdown of the storage box code to the storage box class. So:

    class StorageBox
      def self.find_or_create_by_code(code)
        key = code[0..-5]
        kind = code[-4..-1]
        find_or_create_by(key:, kind:)
      end
      ...
    end
    
    class Product
      def self.new_in_storage_box(code)
        storage_box = StorageBox.find_or_create_by_code(code)
        new(storage_box:)
      end
      ...
    end
    

    That way your intension is clear and the new code will not interfere with the normal operation of the Product.new method.