2009-11-07 14 views
5

J'ai une application qui modélise une maison. La maison a de nombreuses chambres, chambres a beaucoup de lumières et de petits appareils, etc. J'ai aussi un contrôleur appelé Calculatrice qui est la façon dont l'application est accessible. Les données sont ajoutées à la maison (et à ses pièces) à l'aide du contrôleur de la calculatrice. Ensuite, un rapport est généré, qui se trouve à app/views/calculator/report.html.erb.Quelle devrait être la logique de calcul dans une application Rails?

Ma question est où aller tous les calculs et la logique pour le rapport? Actuellement, j'ai tout dans la vue, avec certaines choses dans calculator_helper. Normalement, cela irait dans le modèle, non? Mais Calculator n'a pas de modèle qui a été généré. Quelle est la norme pour cela?

Voici le contrôleur de la calculatrice.

class CalculatorController < ApplicationController 
    def index 
    end 

    def save_house 
    @house = House.new(params[:house]) 
    respond_to do |format| 
     if @house.save 
     format.html { render :action => 'add_rooms', :id => @house } 
     format.xml { render :xml => @house, :status => :created, :location => @house } 
     else 
     format.html { render :action => 'index' } 
     format.xml { render :xml => @house.errors, :status => :unprocessable_entity } 
     end 
    end 
    end 

    def add_rooms 
    @house = House.find(params[:id]) 
    @rooms = Room.find_by_house_id(@house.id) 

    rescue ActiveRecord::RecordNotFound 
    logger.error("Attempt to access invalid house #{params[:id]}") 
    flash[:notice] = "You must create a house before adding rooms" 
    redirect_to :action => 'index' 
    end 

    def add_room 
    @room = Room.new(params[:room]) 
    @house = @room.house 

    respond_to do |format| 
     if @room.save 
     flash[:notice] = "Room \"#{@room.name}\" was successfully added." 
     format.html { render :action => 'add_rooms' } 
     format.xml { render :xml => @room, :status => :created, :location => @room } 
     else 
     format.html { render :action => 'add_rooms' } 
     format.xml { render :xml => @room.errors, :status => :unprocessable_entity } 
     end 
    end 
    rescue ActiveRecord::RecordNotFound 
    logger.error("Attempt to access invalid house #{params[:id]}") 
    flash[:notice] = "You must create a house before adding a room" 
    redirect_to :action => 'index' 
    end 

    def report 
    flash[:notice] = nil 
    @house = House.find(params[:id]) 
    @rooms = Room.find_by_house_id(@house.id) 
    rescue ActiveRecord::RecordNotFound 
    logger.error("Attempt to access invalid house #{params[:id]}") 
    flash[:notice] = "You must create a house before generating a report" 
    redirect_to :action => 'index' 
    end 
end 
+0

S'il vous plaît nous montrer votre classe Calculatrice. –

+0

J'aime la réponse de James. Une autre question que vous devriez vous poser, c'est pourquoi vous êtes en train de rompre avec la convention - pourquoi le contrôleur de la Calculatrice gère-t-il des choses qui semblent appartenir au contrôleur de la Maison? Je ne dis pas que vous le faites mal, je dis juste que ça vaut la peine d'y réfléchir davantage. –

+0

Bon point, Andy. Ryan, obtenez les MODÈLES avant de commencer à vous inquiéter des contrôleurs et des vues. En utilisant cette approche, vous pouvez découvrir que le bon endroit pour tous les calculs est le modèle de la maison. –

Répondre

0

Tout dépend du type de données que vous créez. À quoi ressemble le contrôleur de la calculatrice?

Vous pouvez créer vos propres classes dans/lib et les utiliser dans vos modèles, ce qui peut être un bon moyen de séparer la logique du contrôleur/helpers. Y at-il une raison pour laquelle vous ne pourriez pas mettre une partie de la logique dans les modèles?

+0

calculator_controller enregistre les données et déplace l'utilisateur à la page suivante qui lui permet d'entrer plus d'informations. Les modèles sont en cours de validation, mais c'est à peu près tout. Les calculs effectués sont principalement des calculs de nombre basés sur les paramètres de la maison et de la pièce. J'ai essayé de créer un modèle pour la calculatrice, mais je ne peux pas accéder aux variables de la vue. Devrais-je faire alors toutes les variables globales? – Ryan

1

Je voudrais créer une classe dans RAILS_ROOT/lib/appelé, par exemple, Calculatrice et mettre le code là-dedans.

Les classes de/lib/doivent être chargées et disponibles n'importe où dans votre application.

Vous pouvez également créer un objet ruby ​​dans/app/models /. Il n'y a aucune raison pour qu'ils héritent tous d'ActiveRecord :: Base

+0

J'ai créé un fichier calculator.rb dans l'application/models qui n'hérite pas d'ActiveRecord. Cependant, lorsque je déplace mon code vers ce fichier, je ne peux plus y accéder à partir de la vue. Je suppose que c'est parce que j'ai tout fait avec des variables locales. Quelle est la meilleure façon de résoudre ce problème? J'ai essayé de les changer en variables globales, mais cela ne semble pas aider. – Ryan

+0

Placer les méthodes dans les modèles, au lieu d'une classe dans lib /, lie mieux la logique aux données sous-jacentes. De plus, il présente l'avantage de ne pas souffrir de ce problème lié aux variables d'instance puisque vous pouvez appeler les méthodes sur les instances des modèles que vous manipulez déjà dans le contrôleur et dans les vues. Le code de sortie dans lib/est le plus logique s'il est utilisé par plusieurs classes dans votre base de code, comme un module Mixin, ou est quelque chose que vous refactoring dans un projet/plugin sidecar réutilisable. Si la logique fait partie de la base de code, conservez-la avec votre code principal. –

5

Il y a plusieurs façons de l'approcher, mais la logique n'appartient certainement pas à la vue. Vous avez les différents modèles associés les uns aux autres dans une hiérarchie claire avec le haut de la hiérarchie étant le modèle de la maison, si je lis votre description correctement. Dans ce cas, j'ajouterais une méthode appropriée d'ensemble de méthodes au modèle House qui pourrait être composée d'appels à des méthodes de calcul dans les modèles Room associés à une instance House donnée et sur la ligne d'association. De cette façon, le calcul pertinent peut être effectué à chaque niveau et en composant une ou plusieurs méthodes au niveau du modèle maison, vous êtes en mesure d'avoir un moyen propre, expressif et maintenable pour faire face aux calculs. Une chose à faire, aussi, serait de s'assurer que tous les calculs qui peuvent être effectués par la base de données sont. Par exemple, s'il existe un calcul qu'un modèle de pièce peut faire en interrogeant simplement ses propres données, il faut pousser la charge de calcul vers la base de données en utilisant la capacité d'ActiveRecord à invoquer cette logique de calcul de niveau inférieur. Consultez le API docs pour les détails. Je regarderais très attentivement la logique que vous voulez et voir comment il peut être poussé dans le modèle puisque c'est probablement là où il appartient, près des données réelles des calculs, et dans les structures de classe qui représentent ces données Plus précisément; Je ne créerais pas de modèle uniquement pour gérer la logique de calcul, à moins que vous n'ayez vraiment besoin de stocker les calculs de manière persistante pour une raison quelconque.

1

Ok, maintenant je peux voir le code affiché. Je peux voir que le calculator_controller ne contient aucun calcul, sont-ils dans les vues? Essayez cette approche:

  1. Écrire un test qui configure un objet qui renverra les résultats que vous devez retourner à l'utilisateur de la page Web, donné une maison, des chambres ou tout ce dont il a besoin.
  2. Construire un modèle (dans les modèles) pour faire passer ce test.
  3. Modifiez le code de votre contrôleur ci-dessus pour utiliser votre nouveau modèle de calculatrice
  4. Modifiez les tests de votre contrôleur pour qu'ils passent également. Ces tests, bien sûr, n'ont pas besoin de tester une logique métier.

Mon respose avant:

Si la logique métier est assez simple et utilisé uniquement derrière cette application Web, vous pouvez le mettre dans votre app/dossier modèles.

class MyCoolClass 
    def initialize(clues) 
    @other_things = OtherThing.all 
    end 
    def do_cool_thing; end 
    def calculate_coolness 
    @other_things.length 
    end 
end 

Ensuite, dans votre contrôleur, créez une instance de votre modèle

def index 
    @mcc = MyCoolClass "A clue as to what I want" 
    render 
end 

Ensuite, dans vos modèles, vous pouvez y accéder

<%=h @mcc.calculate_coolness %> 

Notez que @other_things est un instance__variable de MyCoolClass et généralement non accessible aux modèles sans méthodes d'accès en cours de définition

+0

J'aime construire des modèles comme celui-ci lorsque la logique commence à encombrer le modèle AR. –