fbelanger
fbelanger

Reputation: 3568

Rails Nested Resources Creation

In RoR, whenever you create a nested resource, is there to set attributes during creation of a resource with a parent association, within the model?

I have this Role model that may belong_to and have_many other roles.

employee = Role.find_by_slug :employee
employee.role
=> nil
employee.roles
=> [...more roles...]
waitress = employee.roles.create(slug: :waitress)
=> #<Role id...
waitress.role
=> #<Role slug: 'employee'...
waitress.roles
=> []

The role model has a boolean attribute of subtype. Whenever I create a role from an existing role, I'd like for subtype to be set to true.

employee.subtype
=> false

And waitress would look like this:

waitress.subtype
=> true

Upvotes: 1

Views: 76

Answers (3)

Richard Peck
Richard Peck

Reputation: 76774

Whenever I create a role from an existing role, I'd like for subtype to be set to true.

#app/models/Role.rb
class Role < ActiveRecord::Base
   belongs_to :role
   has_many   :roles

   validate :role_exists, if: "role_id.present?"
   before_create :set_subtype, if: "role_id.present?"

   private

   def set_subtype
     self.subtype = true
   end

   def role_exists
      errors.add(:role_id, "Invalid") unless Role.exists? role_id
   end
end

The above will require another db request; it's only for create & it will happen when the model is invoked (IE you can call it whatever you like when you need it).

--

An alternative to this would be to use acts_as_tree or a similar hierarchy gem.

AAT adds a parent_id column in your db, to which it will then append a series of instance methods you can call (parent, child, etc).

This would permit you to get rid of the has_many :roles, and replace it with a children instance method:

#app/models/role.rb
class Role < ActiveRecord::Base
   acts_as_tree order: "slug"
   #no need to have "subtype" column or has_many :roles etc
end

root      = Role.create            slug: "employee"
child1    = root.children.create   slug: "waitress"
subchild1 = child1.children.create slug: "VIP_only"

root.parent   # => nil
child1.parent # => root
root.children # => [child1]
root.children.first.children.first # => subchild1

Upvotes: 1

fbelanger
fbelanger

Reputation: 3568

The following changes did the trick for me:

from:

has_many :roles

to:

has_many :roles do
  def create(*args, &block)
    args[0][:subtype] = true
    super(*args, &block)
  end
end

Upvotes: 0

Mate Solymosi
Mate Solymosi

Reputation: 5977

According to your description, a given Role is considered a subtype if it has no parent role. In this case, simply add the following method to Role:

def subtype?
  !self.role.nil?
end

Upvotes: 0

Related Questions