samedi 14 février 2015

Should I save a Model calculation as an attribute?

I have an app that enables a user to create a new room using a form to enter name, description, length, and width. Each room created becomes a new record and the app calculates the 'size' of the room as 'length' * 'width'. This is a simple app that I'm playing with to learn Rails but I may take it further to have a collection of rooms form a house, with some total 'size' of each house.

My question relates to the 'size' value and how that should be integrated into the app. I initially thought that the user should see the value of 'size' right away on the form, but shelved that once it appeared that Ajax may be required. I moved the 'size' method calculation from the view to the model to conform to "fat model, skinny controller" concept and I now show the 'size' in the 'index' view, leaving the 'new' view purely for entering data.


I initially set up the model to include length, width and size. See migration for the Room model:


20150118183743_create_rooms.rb



class CreateRooms < ActiveRecord::Migration
def change
create_table :rooms do |t|
t.string :name
t.text :description
t.integer :length
t.integer :width
t.integer :size

t.timestamps null: false
end
end
end


Should I save 'size' for each record to the database? I've read that it shouldn't be necessary to save calculations as an attribute to the model. Presumably, the app should handle that? What's the correct way to think about this?

My 'index' view calculates & returns max 'length' and 'width', but I run into an error when I try to calculate the max 'size'. I have a calculation (i.e., method) for this in model but it appears to be wrong. Any suggestions? Below are relevant code:


room.rb



class Room < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 30 },
uniqueness: { case_sensitive: false }
validates :length, :width, presence: true,
numericality: { only_integer: true,
less_than_or_equal_to: 1000,
greater_than_or_equal_to: 1 }

def size
size = length * width
end

def max_room
size.max
end

end


rooms_controller.rb



class RoomsController < ApplicationController

def show
@room = Room.find(params[:id])
end

def new
@room = Room.new
end

def index
@rooms = Room.all
end

def create
@room = Room.new(user_params)
if @room.save #a boolean, if able to save the instance
flash[:success] = "You created a new Room!!"
redirect_to @room #we send the user to the room
else
render 'new' #so we want to render the new template
end
end

private
def user_params
params.require(:room).permit(:name, :description, :length,
:width, :size)
end


end


index.html.erb



<% provide(:title, 'All Rooms') %>

<h1>All rooms</h1>


<div class="container">
<div class="row column-md-7">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th class="text-right">Length (ft.) </th>
<th class="text-right">Width (ft.) </th>
<th class="text-right">Size (sq.ft.) </th>
<th class="text-center">Delete? </th>
</tr>
</thead>

<tbody>

<% @rooms.each do |room| %>
<tr>
<td> <%= link_to room.name, room %> </td>
<td> <%= room.description %> </td>
<td class="text-right"> <%= room.length %> </td>
<td class="text-right"> <%= room.width %> </td>
<td class="text-right"> <%= room.size %> </td>
<td class="text-center"> <%= link_to "delete", room, method: :delete,
data: { confirm: "You sure?" } %> </td>
</tr>
<% end %>
</tbody>
</table>

<div class="alert alert-info">
The model contains <%= pluralize(Room.count, "room") %> in total.
The max length is <%= Room.maximum('length') %>.
The max width is <%= Room.maximum('width') %>.

</div>

</div>
</div>


I tried showing 'size' by adding



The max size is <%= Room.max_room %>


but that returned an error.


new.html.erb



<% provide(:title, "New Room") %>
<h1>The Rooms page </h1>

<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@room) do |f| %>
<%= render 'shared/error_messages' %>

<%= f.label :name %>
<%= f.text_field :name %>

<%= f.label :description %>
<%= f.text_area :description %>

<%= f.label :length, "Length (ft.)" %>
<%= f.number_field :length %>

<%= f.label :width, "Width (ft.)" %>
<%= f.number_field :width %>

<%= f.submit "Create my room", class: "btn btn-primary" %>

<% end %>

</div>
</div>


show.html.erb



<% provide(:title, @room.name) %>
<h1>The "<%= @room.name %>" page </h1>
<h2>This page contains the show action associated with the
Rooms page </h2>
<br>
<br>

<div class="container">
<div class="col-md-6 col-md-offset-3">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th class="text-right">Length (ft.) </th>
<th class="text-right">Width (ft.) </th>
<th class="text-right">Size (sq.ft.) </th>
</tr>
</thead>

<tbody>
<tr>
<td> <%= @room.name %> </td>
<td> <%= @room.description %> </td>
<td class="text-right"> <%= @room.length %> </td>
<td class="text-right"> <%= @room.width %> </td>
<td class="text-right"> <%= @room.size %> </td>
</tr>
</tbody>

</table>
</div>
</div>

<hr>

<%= link_to "Create a new room", new_room_path, class: "btn btn btn-primary" %>


routes.rb



Rails.application.routes.draw do

root 'static_pages#home'
get 'home' => 'static_pages#home'
get 'calculations' => 'static_pages#calculations'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'new_room' => 'rooms#new'
get 'rooms' => 'rooms#index'
resources :rooms
end


I plan to use apps that will be heavy with numerical calculations so I want to get these fundamentals right. I don't want the app's database to blow up if I'm saving too many calculations down when they should be done (perhaps) in a virtual environment.


So, to recap ....



  1. Should a app calculation be saved to the database as an attribute to a new record?

  2. What might be the correct calculation/method for 'size'?

  3. If I want to perform a calculation on a calculated value, does that value first have to be saved as an attribute?


Aucun commentaire:

Enregistrer un commentaire