简体   繁体   中英

Rails Namespaced Model Not Triggering Callbacks

Basic Group Model:

class Group < ActiveRecord::Base
  @@ItemType = 'GroupItem' # Base ItemType
  after_initialize :default_item_type

  has_many :group_assignments
  has_many :items, through: :group_assignments, source: :groupable, source_type: @@ItemType, dependent: :destroy

  accepts_nested_attributes_for :items, allow_destroy: true

  private
  def default_item_type
    self.item_type = @@ItemType if self.new_record?
  end
end

Namespaced Model:

class Gradation::Group < Group
  @@ItemType = 'Gradation' # Base ItemType
  after_initialize :default_item_type

  has_many :group_assignments
  has_many :items, through: :group_assignments, source: :groupable, source_type: @@ItemType, dependent: :destroy

  accepts_nested_attributes_for :items, allow_destroy: true
  private
  def default_item_type
    self.item_type = @@ItemType if self.new_record?
  end
end

I am attempting to have a field *item_type* be set on the *after_initialize* callback, but not matter what I do, the callback is only triggered on the regular group model and not the namespaced one.

I am struggling to understand why this is the expected behavior, and also how to get callbacks to work for the namespaced Group .

Using the class variable here @@ItemType = 'Gradation' is not likely to help you out. In an initial modeling of this problem, the @@ItemType were stepping on each other, always giving you 'Gradation'

The following will work. You'll notice the differences:

  1. move to constant for the class instead of class variable
  2. changed the namespacing around

app/models/group.rb

class Group < ActiveRecord::Base
  ITEM_TYPE = 'GroupItem' # Base ItemType
  after_initialize :default_item_type

  private
  def default_item_type
    self.item_type = ITEM_TYPE if self.new_record?
  end
end

app/models/group/gradation.rb

class Group::Gradation < Group
  ITEM_TYPE = 'Gradation' # Base ItemType

  private
  def default_item_type
    self.item_type = ITEM_TYPE if self.new_record?
  end
end

Notes:

  1. You cannot use the SingleTableInheritance here -- Rails expects there to be a gradations table
  2. you must define default_item_type on each subclass so that it can pull the correct ITEM_TYPE

To get rid of #2 above, you could do this:

class Group < ActiveRecord::Base
  ITEM_TYPE = 'GroupItem' # Base ItemType
  after_initialize :default_item_type

  private
  def default_item_type
    self.item_type = "#{self.class}::ITEM_TYPE".constantize if self.new_record?
  end
end

Then, each subclass could be:

class Group::Graduation < Group
  ITEM_TYPE = 'Graduation' # Base ItemType  
end

The overall solution to my problem looks like this...

Base Group Model:

class Group < ActiveRecord::Base
  @@ItemType = 'GroupItem' # Base ItemType
  after_initialize :default_item_type

  has_many :group_assignments
  has_many :items, through: :group_assignments, source: :groupable, source_type: @@ItemType, dependent: :destroy

  accepts_nested_attributes_for :items, allow_destroy: true

  private
  def default_item_type
    self.item_type = @@ItemType if self.new_record?
  end
end

Subclassed Group model with ItemType set to another arbitrary model (in app/models/groups/gradation_group.rb):

class Groups::GradationGroup < Group
  @@ItemType = 'Gradation'
  has_many :items, through: :group_assignments, source: :groupable, source_type: @@ItemType, dependent: :destroy
end

My understanding is I had to change the namespace to something that is not a class name. I also had to change the group name to something other than what the items association will be composed of (in this case Gradation ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM