Forum: Ruby on Rails Hardcode database record reference into model

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
92ce537e2c732b0300d9ea97f6bf3fe3?d=identicon&s=25 Andrew Edwards (Guest)
on 2009-05-07 01:55
(Received via mailing list)
Hi,

I have a table containing system account types ("Income", "Ordered",
"Available" etc) which are created as seed data during application
installation.

class InventoryAccountType < ActiveRecord::Base

  has_many :inventory_accounts
  enumerable_constant :normal_balance, :constants => [:debit, :credit]

  validates_uniqueness_of :name

  # system account types

  def self.income
    find_by_name("Income")
  end

  def self.ordered
    find_by_name("Ordered")
  end

  def self.available
    find_by_name("Available")
  end

end

These account types represent system concepts that are referenced in
code elsewhere in application, for example:

class InventoryTransaction < ActiveRecord::Base

  has_many :inventory_account_entries
  validate :account_entries_balance

  # locate new inventory
  def self.create_inventory(product_sku, storage_location, quantity,
inventory_batch = nil)

    # if no batch specified, create a new batch
    if inventory_batch.nil?
      inventory_batch = InventoryBatch.create(:product_sku =>
product_sku)
    end

    product_income_account = InventoryAccount.retrieve_by_type
(product_sku, InventoryAccountType.income)
    available_product_location_account =
InventoryAccount.retrieve_by_location(inventory_batch,
storage_location, InventoryAccountType.available)

    account_transfer(product_income_account,
available_product_location_account, quantity)
  end
end

Do you think this approach to hardcoding record lookups into the
InventoryAccountType model in such a way is good practice?

There may be other InventoryAccountTypes created by the user during
application usage but these would be only be managed and allocated to
by the user as general account transfers. Not by system core system
use cases such as creating new inventory as addressed by it own
methods in the model.

I have also added a "system" boolean column to the
InventoryAccountType table so as to know which types cannot be deleted
or edited.

I was also thinking of replacing the:

  def self.available
    find_by_name("Available")
  end

...style methods for each system type with some of dynamic ruby method
that automatically creates a method for any database record that has
system == 1. This would save having to clutter the
InventoryAccountType model with multiple method definitions doing the
same thing.

Thanks in advance, Andrew.
1e7782e67bb34c9c67ed19d5cde5f4eb?d=identicon&s=25 Tom Z Meinlschmidt (Guest)
on 2009-05-07 02:09
(Received via mailing list)
hi,

I think you should use named_scope instead of this kind of access...

class InventoryAccountType < ActiveRecord::Base
   named_scope :income, :conditions => {:name => 'Income'}
   named_scope :ordered, :conditions => {:name => 'Ordered'}
end

you can use it the same way but you can chain scopes.. eg

data = InventoryAccountType.income.ordered

in your case (with def.self methods) is that not possible

tom

Andrew Edwards wrote:
>
>   end
> class InventoryTransaction < ActiveRecord::Base
>       inventory_batch = InventoryBatch.create(:product_sku =>
> available_product_location_account, quantity)
> methods in the model.
>
> ...style methods for each system type with some of dynamic ruby method
> that automatically creates a method for any database record that has
> system == 1. This would save having to clutter the
> InventoryAccountType model with multiple method definitions doing the
> same thing.
>
> Thanks in advance, Andrew.
>

--
===============================================================================
Tomas Meinlschmidt, MS {MCT, MCP+I, MCSE, AER}, NetApp Filer/NetCache

www.meinlschmidt.com  www.maxwellrender.cz  www.lightgems.cz
===============================================================================
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2009-05-07 03:10
(Received via mailing list)
On Thursday 07 May 2009, Andrew Edwards wrote:

> I have a table containing system account types ("Income", "Ordered",
> "Available" etc) which are created as seed data during application
> installation.

The never-ending enumeration story. For my take on it see (without line
break, with hyphen):

http://www.schuerig.de/michael/blog/index.php/2009...
enums/

I notice that my caching mechanism would get in the way of your
requirement to have "non-managed" enumeration values.

> These account types represent system concepts that are referenced in
> code elsewhere in application, for example:
>
> class InventoryTransaction < ActiveRecord::Base
[snip]

With the enums implementation linked above, I'm handling a similar case
like this:

class InventoryAccountType < ActiveRecord::Base
  has_many :inventory_accounts

  enumerates do |e|
    e.value :name => 'income',    :title => 'Income'
    e.value :name => 'ordered',   :title => 'Ordered'
    e.value :name => 'available', :title => 'Available'
  end
end

class InventoryAccount < ActiveRecord::Base
  belongs_to :inventory_account_type

  InventoryAccountType.each_name do |name|
    named_scope "of_#{name}_type",
      :joins => :inventory_account_type,
      :conditions => { :inventory_account_types => { :name => name } }
  end
end

Then you can do thinks like

  InventoryAccount.of_income_type.find(...)

Without the trickery, you could define a parameterized scope like this

class InventoryAccount < ActiveRecord::Base
  belongs_to :inventory_account_type

  named_scope :of_type, lambda { |type|
    :joins => :inventory_account_type,
    :conditions => { :inventory_account_types => { :name => type } }
  }
end

And use it as

  InventoryAccount.of_type(:income).find(...)


Michael

--
Michael Schuerig
mailto:michael@schuerig.de
http://www.schuerig.de/michael/
92ce537e2c732b0300d9ea97f6bf3fe3?d=identicon&s=25 Andrew Edwards (Guest)
on 2009-05-07 03:11
(Received via mailing list)
Thanks Tom,

I've just tried using method_missing to generate the methods, but
thinking about it your named_scope suggestion might have future value,
chaining etc. Also, I think the named_scope might be a bit more self
documenting and helps enforce nothing gets missed or forgotten about.

# dynamically create lookup method for any system account type

def self.method_missing(method)
  if account_type = find(:first, :conditions => { :name =>
method.to_s, :system_type => true })
    account_type
  else
    super
  end
end

Thanks, Andrew.
This topic is locked and can not be replied to.