2009-01-12 8 views
10

J'utilise single table inheritance dans mon application rails, et je veux définir explicitement le type d'une instance.Rails Single Table Inheritance - Quelle est la meilleure façon de définir explicitement le type?

J'ai ce qui suit:

class Event < ActiveRecord::Base 
class SpecialEvent < Event 

qui est implémentée via l'héritage de table unique.

SpecialEvent.new fonctionne comme prévu, mais je veux être en mesure de faire des choses comme

Event.new(:type => 'SpecialEvent') 

Je peux créer différentes sub_types facilement dans l'application.

Toutefois, cela ne fonctionne pas et semble définir :type à nil, pas la valeur que je l'ai définie; Je suppose que c'est parce qu'en appelant Event.new, il écrase l'argument :type.

Est-ce que quelqu'un a un bon moyen de le faire?

+0

Voulez-vous dire que vous voulez créer des sous-types à la volée? – Bill

+0

Non, je veux créer des instances de sous-types, où je veux déterminer par programme quel sous_type ils sont – DanSingerman

Répondre

21

Si vous essayez d'instancier dynamiquement un sous-type, et vous avez le type comme une chaîne, vous pouvez le faire:

'SpecialEvent'.constantize.new() 
+3

Juste une note rapide ici - assurez-vous que vous nettoyez l'entrée si cette chaîne provient d'un formulaire ou similaire - vous ne veulent pas appeler #constantize sur des données arbitraires provenant d'une source potentiellement malveillante, par exemple 'klass.constantize.new() si valid_class_names.include? (klass)' –

5

Non, je veux créer des instances de sous-types , où je veux déterminer par programme qui sub_type ils sont - Hermand

Vous pouvez utiliser un factory pattern, bien que je l'ai entendu re cent que les gens froncer les sourcils sur l'utilisation excessive de ce modèle. Fondamentalement, utiliser l'usine pour créer les types réels que vous voulez obtenir

class EventFactory 
    def EventFactory.create_event(event_type) 
    event_type.constantize.new() 
    end 
end 
0

Apparemment, Rails ne vous permet pas de définir le type directement. Voici ce que je fais ...

klass_name = 'Foo' 
... 
klass = Class.const_get(klass_name) 
klass.new # Foo.new 

Je crois que .constantize est un raccourci de l'inflateur Rails. const_get est une méthode Ruby sur Class et Module.

+3

Cela ne fonctionnera pas si vous utilisez des espaces de noms pour vos cours , c'est à dire. Class.const_get ('Foo :: Bar') entraînera une erreur. 'Foo :: Bar'.constantize fonctionnera bien. –

14

de "Pragmatique - Développement Web Agile avec des rails 3ème édition", à la page 380

Il y a aussi une contrainte moins évidente (avec STI). Le type d'attribut est également le nom d'une méthode Ruby intégrée. L'accès direct à pour définir ou modifier le type d'une ligne peut entraîner des messages étranges Ruby .Au lieu de cela, l'accès implicitement en créant des objets de la classe appropriée, ou l'accès via l'indexation de l'objet modèle interface , en utilisant quelque chose comme ceci:

personne [: type] « Manager » =

l'homme, ce livre vraiment roches

2

Pour moi, il semble que vous aurez besoin d'un Mojo dans l'action event#create:

type = params[:event].delete(:type) 

# check that it is an expected value!!! 
die unless ['Event', 'SpecialEvent'].include(type) 

type.constantize.new(params[:event]) 
0

Je suis d'accord sur le fait que STI n'est souvent pas la meilleure façon de gérer les choses. Polymorphisme, oui, mais il est souvent préférable d'utiliser une association polymorphe que STI.

Cela dit, j'avais un système dans lequel STI était important. C'était un système judiciaire et les choses comme les affaires judiciaires étaient remarquablement similaires entre eux et partageaient généralement tous leurs attributs essentiels. Cependant, une affaire civile et une affaire pénale ont différé dans les éléments qu'ils ont gérés. Cela s'est passé à plusieurs niveaux dans le système si abstrait ma solution.

https://github.com/arvanasse/sti_factory

Longue histoire courte, il utilise une méthode de fabrication pour tirer parti de l'approche commune décrite ci-dessus. En conséquence, le contrôleur peut rester neutre/ignorant du type particulier de classe STI que vous créez.

0

Vous pouvez utiliser la méthode Rails safe_constantize, qui garantira la réalité de l'objet/classe.

Par exemple:

def typeify(string) 
    string.classify.safe_constantize 
end 

new_special_event = typeify('special_event').new 
Questions connexes