Domanda

I have a Mongoid model with a hash field. This Mongoid model has subclasses using single collection inheritance. Now I want to set different default hash keys for each subclass of the main model.

The main model

 class Sport
   include Mongoid::Document
   field :rules, type: Hash, default: {}
 end

Subclasses where I want to set different default hash keys for the :rule hash field. For instance, for football I want to have rules: {:offside => '', :penalty => ''} For Boxing we might have hash keys of rules: {:biting => 'not allowed'}. The open source app Errbit does something like it using constant in subclass to set default hash keys but I can see how they use the constant to populate the hash: https://github.com/errbit/errbit/blob/master/app/models/issue_trackers/github_issues_tracker.rb

 class Sport::Football < Sport
 end

 class Sport::Boxing < Sport
 end

I did overwrite the field definition in the subclass as shown below which works in the rails console. When I do a = Sport::Football.new and then call a.smtp returns the default settings. But the problem here is that when I go to the parent class and do b = Sport.new and b.smtp, I want it to be able to list all the default keys for the subclasses and it doesn't because I have overridden the hash field in the subclass.

 class Sport::Football < Sport
    field :rules, type: Hash, default: {:offside => '', :penalty => ''}
 end

Is there a way to set default hash keys for a subclass without overriding the field definition. It is okay to do this by overriding the setters and getters for the hash field in each subclass.

È stato utile?

Soluzione

The :default option can take a lambda as its value. Inside that lambda, self will be the new instance you're creating. That means that you can (indirectly) call methods to supply defaults:

class Sport
  include Mongoid::Document
  field :rules, type: Hash, default: lambda { default_rules }
end

class Sport::Football < Sport
private
  def default_rules
    { :offside => '', :penalty => '' }
  end
end

class Sport::Boxing < Sport
private
  def default_rules
    { :biting => 'not allowed except for ears' }
  end
end

You don't have to make default_rules private of course. You'll also want default_rules in Sport if you want to instantiate Sport directly.

You could also use one of the callbacks to set the rules by hand:

class Sport
  include Mongoid::Document
  field :rules, type: Hash
  after_initialize :set_default_rules, :if => :new_record? # Or one of the other callbacks.
end

and subclasses could say self.rules = ... in their set_default_rules implementations.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top