2010-11-21 5 views
3

J'ai une situation où je veux faire des modèles «paramétriques» dans les rails; par exemple, je voudrais définir PrototypeRecipe, puis être en mesure de faire plusieurs DerivedRecipe; peut-être une recette dérivée utilise plus de sucre et une autre utilise moins d'œufs ou quelque chose. Le point critique est que je veux que toutes les instances 'dérivées' héritent des propriétés d'un seul PrototypeRecipe partagé, mais soient capables de faire des modifications locales. Idéalement, j'aimerais pouvoir définir des méthodes sur le prototype (par exemple, créer une liste de courses), et avoir ces méthodes pour répondre aux changements locaux dans les instances dérivées (donc si j'ai spécifié 3 oeufs au lieu de 2 , je pourrais appeler la fonction make_shopping_list du prototype et cela refléterait cela).rails modèles modèles (ou héritage d'instance) options?

Existe-t-il une méthode existante pour accomplir quelque chose comme ça? Voici le meilleur que je peux trouver à ce jour:

class Ingredient << ActiveRecord::Base 
    belongs_to :recipe, :polymorphic => true 

    # uuid => UUID String (for grouping ingredients which change between prototype and derived instances) 
end 

class PrototypeRecipe << ActiveRecord::Base 
    has_many :ingredients 

    def make_ingredient_list(derived_recipe = nil) 
     self.ingredients.map {|i| derived_recipe.nil? ? i : derived_recipe.ingredients.where(:ingredient_uuid => i.uuid).first } 
    end 
end 

class DerivedRecipe << ActiveRecord::Base 
    belongs_to :prototype_recipe 

    has_many :ingredients 

    def method_missing(sym, *args) 
     self.prototype_recipe.send(sym, *args, self) 
    end 
end 

Je sais que ce code peut être un beaucoup plus propre, je suis plus se demander si l'approche générale peut être améliorée. L'idée de base est que les ingrédients auraient chacun une identité unique. Pour modifier une recette de prototype, il vous suffit de créer une instance de DerivedRecipe, de la lier au prototype, puis d'ajouter un ingrédient avec le même UUID comme ingrédient du prototype.

+0

vous vouliez dire '<' ... not '<<' – Tilo

Répondre

0

Je ne suis pas à 100% sur le comportement que vous cherchez à avoir, alors voici ma tentative de solution.

Inheritance de table unique (STI). Votre classe de base sera PrototypeRecipe et votre classe enfant sera DerivedRecipe. Dans votre table prototype_recipes, spécifiez une colonne type (texte). Cela signale à Rails que vous voulez utiliser STI. Si vous placez votre méthode make_ingredients_list dans la classe de base, elle sera accessible depuis vos classes enfants.

# app/models/ingredient.rb 
class Ingredient < ActiveRecord::Base 
    belongs_to :recipe, :class_name => "PrototypeRecipe" 
    ... 
end 

# app/models/prototype_recipe.rb 
class PrototypeRecipe < ActiveRecord::Base 
    has_many :ingredients 
    has_many :derived_recipes 

    def make_ingredient_list 
    ... 
    end 
end 

# app/models/derived_recipe.rb 
class DerivedRecipe < PrototypeRecipe 
    belongs_to :prototype_recipe 
end 

Maintenant, vous pouvez faire quelque chose comme:

@cupcakes = PrototypeRecipe.create 
@cupcakes_with_extra_eggs = @cupcakes.derived_recipes.create 
print @cupcakes_with_extra_eggs.make_ingredient_list 

Est-ce que vous cherchez?