lusketeer
lusketeer

Reputation: 1930

Rails namespace class same as existing active_record model

I'm having a bit trouble with the namespaces in Rails 4.

I have ActiveRecord models Shop, Order, and OrderItem

# model/shop.rb
class Shop < ActiveRecord::Base

# model/order.rb
class Order < ActiveRecord::Base
  has_many :order_items

# model/order_item.rb
class OrderItem < ActiveRecord::Base
  belongs_to :orderable, polymorphic: true
  belongs_to :order

I'm replicating the relationship between Order and OrderItem in a namespace like this

# model/shop/order.rb
class Shop::Order
  attr_accessor :order_items
  def initialize
    self.order_items = []
    self.order_items << Shop::OrderItem.new
  end

# model/shop/order_item.rb
class Shop::OrderItem
  attr_accessor :orderable_type, :orderable_id

  def initialize(params = {})
    if params
      self.orderable_type = params['orderable_type'] if params['orderable_type']
      self.orderable_id = params['orderable_id'] if params['orderable_id']
    end
  end

  def price
    orderable.price
  end

  def orderable
    orderable_type.constantize.find_by(id: orderable_id)
  end

  def to_h
    Hash[
      orderable_type: self.orderable_type,
      orderable_id: self.orderable_id,
      price: self.price
    ]
  end

end

So my problem is that when I initialize Shop::Order.new, sometimes its order_items is an array of OrderItems instead of Shop::OrderItems, and when I test it in the controller, if I type Shop::OrderItem, it will return OrderItem.

I'm wondering if Shop::OrderItem wasn't initialized before OrderItem and cause the issue?

Upvotes: 0

Views: 745

Answers (1)

Ryenski
Ryenski

Reputation: 9692

You are running into a namespace collision. Depending on where the code is executing, Shop could be the ActiveRecord model that you've defined in models/shop.rb, or it could be the module namespace that you've defined under models/shops/*.rb. Not only will this cause unpredictable execution, it's also confusing to read.

I recommend using a module namespace other than "Shop". Even calling it "MyShop" would be an improvement. However you'll probably still run into naming collisions between Shop and MyShop::Shop. You should probably rename the Shop class under the MyShop module to avoid this:

For example:

# model/my_shop/my_order.rb
class MyShop::MyOrder
  # ...
end
# model/my_shop/my_order_item.rb
class MyShop::MyOrderItem
  # ...
end

Having said all that, I feel like you're setting yourself up for a world of hurt. This problem might be better solved using service objects. Google up "Rails Service Objects" for some really good examples.

Upvotes: 1

Related Questions