How can I move calculations in View to Model? - ruby-on-rails

I´m very new to ROR and I have stumbled up on a problem. I believe it´s a newbie problem, but I´m not sure if it´s. I´m building this app were users should be able to enter the amount of paper use in their business and from there the app calculates their eco footprint so to say.
at login the user enters their staff number. The staff number is used to divide with paper_weight etc. ( see in view example).
I know it basically works the way it is now, but I´m aware of this is not the best practice.
I also wanted to move the div containing the running totals to application.html.erb but I can´t do that since my calculations are in the View but not in the Model.
here is a link to the github repo https://github.com/DadiHall/SprettaEMS1.5
Are there any other ways to do this? Can someone please advise me?
my index.html.erb view
<div class="row">
<div class="col-md-10 ">
<h1>Pappírsnotkun</h1>
<table class=" well table table-hover">
<thead>
<tr>
<th>Dags</th>
<th>Tegund</th>
<th>Þyngd</th>
<th>Kostnaður</th>
<th>Þar af umhvm. pappír</th>
<th>Pappírs magn m.v. Stöðugildi</th>
<th>Hl.f. Umhvm. Pappírs</th>
<th>Fjöldi trjáa m.v. magn pappírs</th>
</tr>
</thead>
<tbody>
<% paper_total_cost = 0 %>
<% paper_total_trees = 0 %>
<% paper_total_staff_ratio = 0 %>
<% #papers.each do |paper| %>
<tr>
<td><%= paper.date.strftime("%Y/%m/%d") %></td>
<td><%= paper.paper_type %></td>
<td><%= paper.paper_weight %> -kg.</td>
<td><%= paper.paper_cost %> -kr.</td>
<td><%= paper.env_paper_weight %> -kg.</td>
<td><%= (paper.paper_weight.to_i / current_user.staff) %> kg/stöðugildi </td>
<td><%= (( paper.env_paper_weight / paper.paper_weight)* 100) %>%</td>
<td><%= (( paper.paper_weight.to_f/1000)*15) %></td>
<td><%= link_to 'Sýna', paper, class: 'btn btn-xs btn-info' %></td>
<td><%= link_to 'Uppfæra', edit_paper_path(paper), class: 'btn btn-xs btn-warning' %></td>
<td><%= link_to 'Eyða', paper, method: :delete, class: 'btn btn-xs btn-danger', data: { confirm: 'Are you sure?' } %></td>
<% paper_total_cost = paper_total_cost + (paper.paper_cost.to_i) %>
<% paper_total_trees = paper_total_trees + (( paper.paper_weight.to_f/1000)*15) %>
<% paper_total_staff_ratio = paper_total_staff_ratio + (paper.paper_weight.to_i / current_user.staff) %>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="row">
<div class=" col-md-2 panel panel-default pull-right" >
<div class="panel panel-heading ">
<h5 class="user-name">User: <% if current_user && current_user %>
<%= current_user.name %>,
Staff: <%= current_user.staff %>
<% end %>
</h5>
</div>
<div class="panel-body">
<h6 class="pull-right">Pappír Kostnaður Samtals:<%= number_with_precision(paper_total_cost.to_f, precision: 2) %> kr </h6><br>
<h6 class="pull-right">Fjöldi Trjáa vegna Pappírs notkunar:<%= number_with_precision(paper_total_trees.to_f, precision: 2) %> Tré </h6><br>
<h6 class="pull-right">Magn Pappírs f. hvert stöðugildi:<%= number_with_precision(paper_total_staff_ratio.to_f, precision: 2) %> Kg </h6>
</div>
</div>
</div>
<%= link_to 'New Paper', new_paper_path %>
My papers_controller.rb
class PapersController < ApplicationController
before_action :set_paper, only: [:edit, :update, :show, :destroy]
def index
#papers = Paper.all
end
def create
#paper = Paper.new(paper_params)
if #paper.save
flash[:success] = "Messages sent."
redirect_to paper_path(#paper)
else
flash[:danger] = "Error occured, message has not been sent."
redirect_to new_paper_path
end
end
def new
#paper = Paper.new
end
def edit
end
def show
end
def update
if #paper.update(paper_params)
flash[:success] = "Line was successfully updated"
redirect_to papers_path(#paper)
else
render 'edit'
end
end
def destroy
#paper.destroy
flash[:danger] = "Line was successfully destroyed"
redirect_to papers_path
end
private
def set_paper
#paper = Paper.find(params[:id])
end
def paper_params
params.require(:paper).permit(:paper_type, :date, :paper_weight, :paper_cost, :env_paper_weight)
end
end
my paper.rb Model
class Paper < ActiveRecord::Base
has_many :users
end

You can move the calculations into the model by defining functions in the model.
The first calculation uses current_user, which will not be in the model by default, so that'll have to be a parameter. The other two are just using paper's attributes; pardon my poor naming, I'm not clear what the second two metrics are.
class Paper
has_many :users
def paper_weight_per_capita(current_user)
paper_weight.to_i / current_user.staff
end
def paper_weight_metric1
( env_paper_weight / paper_weight)* 100)
end
def paper_weight_metric2
(( paper_weight.to_f/1000)*15)
end
end
And in your view
<%= paper.paper_weight_per_capita(current_user) %>
<%= paper.paper_weight_metric1 %>
If you want the running total in your layout then you'll need to load the paper objects on every page. You can do this with an application_controller before_action, though some folks don't like this approach and here, but I think it is a reasonable way for you now.
class ApplicationController < ActionController::Base
before_action :load_running_totals
def load_running_totals
# method 1. Similar to how the calculation is done in the view.
# #paper_weight_per_capita = Papers.all.map(&:paper_weight_per_capita, current_user).reduce(&:+)
# method 2. does the summation in the database without having to load all the records into ruby. (weight needs to be a number in the db.)
#paper_weight_per_capita = Papers.sum(:weight) / current_user.staff
end
end
Now in any view (including your layout):
<%= #paper_weight_per_capita %>

Related

How to use Rails 5.2 form_with to trigger a specific action?

My application needs to duplicate a Skill (from skills index) as many times the user needs it in his cart. So I decided to trigger the add-to-cart method of the skills_controller when the related form, including the number of duplicates and the Skill's id, is submitted. For this purpose, I added counter to the strong parameters of skills_controller.
Unfortunately, I am missing something to correctly setup the form: when submitted, it triggers the create method. Here is the code:
routes.rb extract
resources :skills, :path => "variables" do
resources :values_lists
member do
post :add_to_cart
get :create_values_list
get :upload_values_list
get :remove_values_list
end
collection do
get :index_all
end
end
skills_controller.rb method
def add_to_cart
#template_skill = Skill.find(params[:id])
iterations = params[:skill][:counter].to_i
until iterations == 0
#skill = #template_skill.deep_clone include: [:translations, :values_lists]
#skill.business_object_id = session[:cart_id]
#skill.template_skill_id = #template_skill.id
#skill.code = "#{#template_skill.code}-#{Time.now.strftime("%Y%m%d:%H%M%S")}-#{iterations}"
#skill.is_template = false
#skill.save
iterations -= 1
end
#business_object = BusinessObject.find(session[:cart_id])
redirect_to #business_object, notice: t('SkillAdded2BO') # 'Skill successfully added to business object'
end
index.html.erb table content
<tbody>
<% #skills.each do |skill| %>
<tr data-href="<%= url_for skill %>">
<% if not session[:cart_id].nil? %>
<td>
<%= form_with model: #skill, :action => "add_to_cart", :method => :post, remote: false do |f| %>
<%= f.text_field :counter, value: "1", class: "mat-input-element", autofocus: true %>
<button type="submit" class="mat-icon-button mat-button-base mat-primary add-button" title="<%= t('AddToUsed') %>">
<span class="fa fa-plus"></span>
</button>
<% end %>
</td>
<% end %>
<td class="no-wrap"><%= skill.code %></td>
<td><%= link_to skill.translated_name, skill %></td>
<td><%= link_to translation_for(skill.parent.name_translations), skill.parent %></td>
<td><%= skill.responsible.name %></td>
<td><%= skill.updated_by %></td>
<td class="text-right"><%= format_date(skill.updated_at) %></td>
</tr>
<% end %>
</tbody>
Thanks a lot for your help!
According to this form helpers guide, the syntax you used doesn't exist
form_with model: #model, action: :custom_action
So in this case, you have to specify the url parameter for form_with to make it works.
<%= form_with model: #skill, url: :add_to_cart_skill_path(#skill), method: :post, remote: false do |f| %>

how can i get project_id by remarks in ruby on rails

I have manager remark model that takes input as a remark and decision value and saves it with the project site ID. I have a project site model that takes input as name, date, and file and stores it. Many remarks have a many to one relation with project site ID, and the project site belongs to the manager remark. I want to access the decision attribute boolean value in project site index form, but I am unable to access that boolean value in the index page of the project site. Here is my code of project site and manager remarks model, view and controller-
project site index.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Attendance</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #project_sites.each do |project_site| %>
<tr>
<td><%= project_site.name.titleize %></td>
<td><%= project_site.date %></td>
<td><%= link_to ' View attendance', project_site.file, :class => "fi-page-export-csv" %></td>
<td><%= "here i want to access manager remark decision value" %></td>
<td><%= link_to 'Remark ', project_site %><span>(<%= project_site.manager_remarks.size %>)</span></td>
<td><%= link_to 'Edit', edit_project_site_path(project_site) %></td>
<td><%= link_to 'Destroy', project_site, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
project site controller
def index
#project_sites = ProjectSite.all.order("created_at DESC")
#manager_remark = ManagerRemark.joins(:project_site).where(:project_sites => { :user_id => #user.id })
end
# GET /project_sites/1
# GET /project_sites/1.json
def show
#manager_remark = ManagerRemark.new
#manager_remark.project_site_id = #project_site.id
end
# GET /project_sites/new
def new
#project_site = ProjectSite.new
end
def project_site_params
params.require(:project_site).permit(:name, :date, :file)
end
manager_remark controller
class ManagerRemarksController < ApplicationController
def create
#manager_remark = ManagerRemark.new(remark_params)
#manager_remark.project_site_id = params[:project_site_id]
#manager_remark.save
redirect_to project_site_path(#manager_remark.project_site)
end
def remark_params
params.require(:manager_remark).permit(:remark, :decision)
end
end
manager_remark view form
<%= form_for [ #project_site, #manager_remark ] do |f| %>
<div class="row">
<div class="medium-6 columns">
<%= f.radio_button :decision, true %>
<%= f.label :approve %>
<%= f.radio_button :decision, false %>
<%= f.label :reject %>
</div>
<br>
<br>
<div class="medium-6 cloumns">
<%= f.label :remark %><br/>
<%= f.text_area :remark %>
</div>
</div>
<div>
<%= f.submit 'Submit', :class => 'button primary' %>
</div>
<% end %>
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :project_sites do
resources :manager_remarks
end
get '/project_manager_level_two' => 'project_manager_level_two#index'
get '/project_managers' => 'project_managers#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
If I understand correctly, you have a ProjectSite that contains a ManagerRemark with a decision, right? If that's the case, the simple answer is:
<%= project_site.ManagerRemark.decision %>
If you are saying that each ProjectSite has many ManagerRemarks, you'll want to place the above inside a loop, like so:
<% project_site.manager_remarks.each do |manager_remark| %>
<%= manager_remark.decision %><br/>
<% end %>
This assumes that your models are correctly configured to recognize these relationships. The above may also be optimized by adding an include clause to your fetch inside the controller and there's no need to fetch the ManagerRemark objects separately. Therefore, you'd probably want something like:
def index
#project_sites = ProjectSite.all.includes( :manager_remark ).order("created_at DESC")
end

Rails: No route matches... missing required keys: [:element_id]

So, basically, I have Features nested within Elements which is nested within Apps. The Apps show page contains a Table of Elements, and each Element has a table of Features. I know that this will look a bit messy, but I'm more worried about functionality right now, and I'll clean up the HTML later.
Full error:
Showing ~/app/views/apps/show.html.erb where line #41 raised:
No route matches {:action=>"new", :controller=>"apps/elements/features",
:element_id=>nil} missing required keys: [:element_id]
My FeaturesController (only a few methods shown for the sake of space)
class Elements::FeaturesController < ApplicationController
before_action :set_feature, only: [:show, :edit, :update, :destroy]
# GET /features
# GET /features.json
def index
#features = Feature.all
respond_with(#features)
end
# GET /features/1
# GET /features/1.json
def show
respond_with(#element.features)
end
# GET /features/new
def new
#element = Element.find(params[:element_id])
#feature.element = #element
#feature = Feature.new
end
# GET /features/1/edit
def edit
end
# POST /features
# POST /features.json
def create
#element = Element.find(params[:element_id])
#feature = Feature.new(feature_params)
#feature.element = #element
#feature.save
if #feature.save
flash[:notice] = "#{#feature.name} purchase was added to the #{#feature.element.name}."
redirect_to(#feature)
else
flash[:error] = "There was a problem adding the purchase."
render :show
end
end
My show.html.erb for Apps:
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #app.name %>
</p>
<p>
<strong>Infivewords:</strong>
<%= #app.infivewords %>
</p>
<p>
<strong>Description:</strong>
<%= #app.description %>
</p>
<div class="media">
<div class="media-body">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Delete</th>
<tr>
</thead>
<tbody>
<% #app.elements.each do |element| %>
<tr>
<td><h4><%= element.name %></h4><br><h4><%= element.description %></h4></td>
<td>
<% element.features.each do |feature| %>
<td><h4><%= feature.name %></h4><h4><%= feature.description %></h4>
</td>
<% end %>
<td><h4><%= link_to "Delete", [#app, element], method: :delete, confirm: "Are you sure?" %></h4></td>
<td><%= link_to 'Add a Feature', new_element_feature_path(#element), class: 'pull-right' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<br>
<br>
<%= link_to 'Add an Element', new_app_element_path(#app) %>
<br>
<%= link_to 'Edit', edit_app_path(#app) %> |
<%= link_to 'Back', apps_path %>
Routes:
Rails.application.routes.draw do
resources :apps do
resources :elements, controller: 'apps/elements'
end
resources :elements do
resources :features, except: [:index], controller: 'apps/elements/features'
end
root to: 'apps#index'
Please excuse the mess. I'm new to rails and I've been tearing my code apart trying to figure this out. Please let me know if I should post any more code. Thank you in advance!
try changing the path inside
<td><%= link_to 'Add a Feature', new_element_feature_path(#element), class: 'pull-right' %></td>
to
new_element_feature_path(element)
since you are still looping #app.elements, right?
Solution:
In view, changed params to #element.feature:
<%= link_to 'Add a Feature', new_app_element_feature_path(#element.feature), class: 'pull-right' %>

How do I add a delete function in my cart in rails?

the cart controller - im having trouble declaring the delete function
class CartController < ApplicationController
def add
id = params[:id]
cart = session[:cart] ||= {}
cart[id] = (cart[id] || 0) + 1
redirect_to :action => :index
end
def index
#cart = session[:cart] || {}
end
end
the main item page - the link to delete the item is already defined, I'm confused in the controller part
<h1 id="prodhead">Products</h1>
<table class="catalog">
<% for item in #items %>
<tr>
<td>
<div class="image">
<%= link_to (image_tag item.image_url), item %>
</div>
</td>
<td>
<div class="title">
<%= link_to item.title, item %>
</div>
<div class="description">
<%=h item.description %>
</div>
<div class="links">
<% if session[:login] == 1 %>
<%= link_to 'Edit Item', edit_item_path(item) %> |
***<%= link_to 'Delete Item', item, :confirm => 'Are you sure?', :method => :delete %>***
<% else %>
<%= link_to "Add to Cart", :controller => :cart, :action => :add, :id => item %><br />
<% end %>
</div>
<div class="price">
<%= number_to_currency(item.price, :unit => "&dollar;") %>
</div>
</td>
</tr>
<% end %>
</table>
<% if session[:login] == 1 %>
<p><%= link_to 'New item', new_item_path %></p>
<% end %>
The Routes.rb - the route for deleting is also defined already
OnlineShop::Application.routes.draw do
get "cart/index"
get "cart/add"
get "cart/checkout"
get "cart/del"
get "site/about"
get "site/contact"
get "user/admin_login"
get "user/logout"
resources :items
Well first of all I've notice you reposted your issue however this time somewhat more descriptive, please for future issues, think before you create a ticket and take the time to edit previous tickets which need refining to keep the community clean.
OT:
I see you've created item and cart resources with basic CRUD functionality however the way you did it is not quite "The rails way" I suggest you start a project bij using the scaffold command to learn how this CRUD and routes should be implemented.
If you are willing to learn more about the topic I advice reading: http://guides.rubyonrails.org/getting_started.html
Also I've noticed you use the "h" syntax to escape your output this is only done before Rails 2.3.8 may I suggest using a newer rails version when starting out?

Building has many through relationship rails from existing records

I'm a bit stuck on creating a form for a has many through relationship. At the moment my models are that Songs can have many Setlists and vice versa, through Allocations.
I'm currently working on an edit page, in which a user can add songs to a setlist. The view currently looks like this:
<h1>Edit a Setlist</h1>
<div class="row">
<div class="span8">
<%=form_for(#setlist) do|f|%>
<%=f.label :date, "Set a date" %>
<span><%=f.date_select :date%><span>
<div>
<div id="library">
<%= render 'library' %>
</div>
<%= render 'songs_in_set' %>
</div>
<%=f.submit "Save", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
The library partial referred to above:
<table class= "table table-striped table-bordered">
<thead>
<th>Title</th>
<th>Artist</th>
<th>Add to Set</th>
</thead>
<tbody>
<% #songs.each do |song| %>
<tr>
<td><%= song.title %></td>
<td><%= song.artist %></td>
<td><%= link_to "ADD", '#' %></td>
</tr>
<% end %>
</tbody>
</table>
I want to convert that link in the library partial to a form for creating a new allocation, thus adding a song to the set list.
The relevant bits from my controllers:
The setlist controller:
def edit
#songs = Song.all(order: 'title')
#setlist = Setlist.find(params[:id])
#allocations = #setlist.allocations
end
def update
#setlist = Setlist.find(params[:id])
if #setlist.update_attributes(params[:setlist])
flash[:success] = "SAVED!"
redirect_to setlist_path(#setlist)
else
render 'edit'
end
end
and the allocations controller:
def new
#allocation = Allocation.new
end
def create
#allocation = Allocation.new(params[:allocation])
if #allocation.save
flash[:success] = "Songs added"
redirect_to edit_setlist_path(#allocation.setlist_id)
else
flash[:fail] = "Error"
redirect_to edit_setlist_path(#allocation.setlist_id)
end
end
I know that I've got to do something along the lines of setlist.allocations.build but I'm having trouble getting the right parameters (getting each individual song id and the setlist id). I've tried putting a form for helper within the songs.each do loop but that didn't seem to work. I'm a bit lost so any pointers in the right direction would be appreciated. Thanks in advance
Try adding this to Setlist:
accepts_nested_attributes_for :allocations, :dependent => :destroy
Then you can nest association fieldsets within your setlist form. In the library partial:
<td>
<%= f.fields_for :allocations do |ff| %>
<%= f.hidden_field :song_id, song.id %>
...
<% end %>
</td>
This should work if you want to create allocations for songs which exist already, but you might need something different for a scenario when you are also creating songs in the same form. I had a problem like that not long ago, and wasn't able to find a clean solution, but let me know and I can share that as well...

Resources