2009-05-14 6 views
6

J'ai des méthodes dans tous mes modèles qui ressemblent à ceci:méthodes globales en rubis sur les modèles de rails

def formatted_start_date 
    start_date ? start_date.to_s(:date) : nil 
end 

je voudrais avoir quelque chose qui écrit automatiquement une méthode comme celui-ci pour chaque champ datetime dans chaque modèle , quelle est la meilleure façon de faire cela?

-C

Répondre

16

Je viens de devoir répondre à cette question, car c'est un amusant exercice Ruby.

L'ajout de méthodes à une classe peut se faire de plusieurs façons, mais l'une des façons les plus simples consiste à utiliser certaines des fonctions de réflexion et d'évaluation de Ruby.

Créer ce fichier dans votre dossier lib comme lib/date_methods.rb

module DateMethods 

    def self.included(klass) 

    # get all dates 
    # Loop through the class's column names 
    # then determine if each column is of the :date type. 
    fields = klass.column_names.select do |k| 
        klass.columns_hash[k].type == :date 
       end 


    # for each of the fields we'll use class_eval to 
    # define the methods. 
    fields.each do |field| 
     klass.class_eval <<-EOF 
     def formatted_#{field} 
      #{field} ? #{field}.to_s(:date) : nil 
     end 

     EOF 
    end 
    end 
end 

Maintenant, il suffit d'inclure dans tous les modèles qui en ont besoin

class CourseSection < ActiveRecord::Base 
    include DateMethods 
end 

Lorsque inclus, le module cherchera à tout colonnes de date et générez les méthodes formatted_ pour vous.

Apprenez comment fonctionne cette substance Ruby. C'est très amusant.

Cela dit, vous devez vous demander si cela est nécessaire. Je ne pense pas que ce soit personnellement, mais encore une fois, c'était amusant d'écrire.

-b-

+2

pourquoi utiliser collecter et compacter sur la matrice plutôt que de sélectionner? –

+1

Vous avez raison. .select serait plus approprié ici. J'ai trouvé des moments où .collect.compact est plus rapide, même quand il semble que ce ne devrait pas l'être, mais dans ce cas, .select est un bien meilleur choix. Merci d'avoir attrapé ça. Je me concentrais sur faire les choses difficiles et raté les choses faciles. –

+0

+1 Des trucs intéressants et amusants. Ce serait mieux si vous corrigiez la réponse (ici sur SO) pour inclure les commentaires de Chris. –

11

Cela ressemble plus à quelque chose pour un assistant pour moi. Essayez ceci dans l'aide de l'application de votre:

def formatted_date(date) 
    date ? date.to_s(:date) : nil 
end 

Le formatage est pas quelque chose qui appartient vraiment dans le modèle (précisément la raison pour laquelle vous avez découvert ... mettre un tel code commun dans tous les modèles est ennuyeux)

Si vous voulez vraiment faire comme vous le dites, alors ce que vous pouvez faire est monkeypatch la superclasse ActiveRecord et ajouter la fonction que vous voulez là-bas. Il serait alors disponible pour tous vos modèles. Gardez à l'esprit que le monkeypatching peut mener à un comportement imprévisible et indéfini et à l'utilisation à vos risques et périls! Il est également assez hacky :)

class ActiveRecord::Base 
    def formatted_start_date 
     start_date ? start_date.to_s(:date) : nil 
    end 
end 

bâton juste que quelque part qui sera exécuté avant toute autre chose dans votre application sera, et il ajoutera dynamiquement la méthode à la classe de base de vos modèles, le rendant disponible pour .

Ou vous pourriez créer un mixin pour tous vos modèles, mais cela semble un peu exagéré pour une seule méthode.

+1

+1 de moi. Était en train d'écrire la même réponse ... – tomafro

+0

ok, supposons qu'il était approprié de mettre la mise en forme dans le modèle, ou que je veux faire quelque chose d'autre que le formatage avec chaque champ de date dans mon application, comment le ferais-je? –

+0

ouais, mais la partie 'start_date' est codée en dur ici, je dois le faire pour tous les champs de date de l'application, ils peuvent être appelés start_date, end_date, term_date, finish_date, date, etc. Comment puis-je vérifier sous-classe pour toutes les colonnes de date, puis écrire une méthode dynamique pour chacun d'eux? –

0

Une réponse à votre commentaire: Vous pouvez extraire le code/fonctionnalités communes en modules que vous incluez dans une classe (I belive qui est appelé mixin?) Ou vous pouvez aller pour les sous-classes. Je ne pense pas que la sous-classe soit la voie à suivre quand il s'agit simplement d'une fonctionnalité commune que vous voulez intégrer à vos objets, et non d'une véritable situation d'héritage.

Jetez un coup d'œil à this for more info on modules and Ruby.