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.
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.