Ruby Rails - how to update database with a calculation? - ruby-on-rails

I'm working on a simple rails app. The user enters two separate integers and when they press 'create', the 'show' page displays the two values and a third value which is calculated from the user input. I have put the arithmetic to calculate the third value in the 'show' html.erb view, but I can't work out how to add the third value into the database. Heres my code for the 'show' view:`
Name:
<%= #startup.name %>
<p>
<strong>Revenue:</strong>
<%= #startup.revenue %>
</p>
<p>
<strong>Costs:</strong>
<%= #startup.costs %>
</p>
<h2>
<strong>Profit:</strong>
<%= #startup.profit = #startup.revenue - #startup.costs %>

You could add a column to the database for this calculated value, and update it upon completing the calculation.
Create a migration to add the column profit to the startup model:
rails g migration AddProfitToStartups profit:decimal
with a note that you may choose a different datatype for profit depending on what datatype your revenue and costs columns are. There are other helpers for currency, see here.
This will generate:
class AddProfitToStartups < ActiveRecord::Migration
def change
add_column :startups, :profit, :decimal
end
end
now run rake db:migrate to update the database.
After this, the code you have in your view should work as is.

Create a variable in the Startup model call profit.
In the create action have something like this:
def create
#startup = Startup.new(startup_params)
#startup.profit = #startup.revenue - #startup.costs
#startup.save
#redirect
end
Apparently the above answer is bad practice. So, here is what I hope is a better more general solution.
At the top of your Startup model have a call to before_save that will look like this:
before_save :calculate_profit
and then write a function in the Model called calculate profit. It will look something like this
def calculate_profit
self.profit ||= self.revenue - self.costs
end

Related

Change Class based on active record db query with link_to in loop in rails?

Requesting expert help.
I have an applications table which has_many metrics.
At some point of time, metrics will have following kind of records.
{capture_time:"08:00:10.1234",metric_name:"log",metric_value:"OK",application_id:1}
{capture_time:"09:00:10.1234",metric_name:"process",metric_value:"KO",application_id:1}
{capture_time:"10:00:10.1234",metric_name:"process",metric_value:"OK",application_id:1}
{capture_time:"08:00:10.1234",metric_name:"log",metric_value:"OK",application_id:2}
{capture_time:"09:00:10.1234",metric_name:"process",metric_value:"OK",application_id:2}
{capture_time:"10:00:10.1234",metric_name:"process",metric_value:"KO",application_id:2}
I have a bigger loop for applications and for each application , I create buttons for each metric for that application
<% applic.metric.uniq{|p|p.metric_name}.each do |m| %>
<%= link_to m.metric_name, metrics_path(m.application_id,metric_name: m.metric_name) , :class=>"btn btn-success",:role=>"button" %>
<% end %>
On clicking any button it shows me records only for that metrics. For e.g. if I click on process, i see all records of that metric, which is 2 records in my case.
So till here its fine. What I am looking help for is two folds:
How to determine latest metrics(based in capture time) for that application that is KO and then use that to change the class in link_to inside the loop. Something like this:
<% applic.metric.uniq{|p|p.metric_name}.each do |m| %>
<%= link_to m.metric_name, metrics_path(m.application_id,metric_name: m.metric_name),:class=>"btn btn-success",:role=>"button" %>
<% end %>
Class => btn-danger if latest record for this metric was KO else btn-success
Then I would want to use the combined statuses of the Metrices and change the Class for the whole Application1 box.
For e.g if any one of Process, Log, Errorcounts is failed , which means any of the latest matrices of any of 3 category is KO, then the whole Application1 box should have a class as "btn-danger"
like this:
UPDATE 1 : Many Thanks to #sammms
I tried the suggestion and created following, but it still does not solve my problem.
class Metric < ApplicationRecord
belongs_to :application
def isFailed(metric_value=nil)
metric_value == 'KO'
end
end
<% applic.metric.uniq{|p|p.metric_name}.each do |metric| %>
<%= link_to metric.metric_name, application_dashboard_metrics_path(appid:metric.application_id,metric_name: metric.metric_name),
{:class=>"btn #{metric.isFailed(metric.metric_value)? 'btn-danger':'btn-success' }",:role=>"button"} %>
<% end %>
the string interpolation works, as in it changes the class based on metric value. But the problem is in uniq bit here.
applic.metric.uniq{|p|p.metric_name}.each
since I am looping through only the unique metric_name, it could be the one with metric_value as OK. And hence when it loops, it actually does not find any KO for that metric.
If I don't use uniq in the loop, then I see one button for each record. something like below,
This is not what I wanted.
I wanted to have a button only once, per metric_name, then change the class based on the collective status of all the records for that metric.
So, when I have 100 record for process metric, I don't want 100 buttons, I want only one process button, but the class should be based on if the latest metric_value is KO.
UPDATE 2:
I solved the problem with this:
def isFailed(metric_name=nil)
p metric_name
#metric_value == 'KO'
Metric.where(metric_name:metric_name).order("capture_time DESC").first.metric_value == "KO"
end
Now I need to figure out the part 2.
Since you are inside of an ERB block <%=... %> you can write vanilla ruby and it will be evaluated. That means you can used interpolation inside the string you pass to class, e.g.
class="#{'btn-danger' if metric.ko?}"
where ko? is a method defined in Metric that will return the boolean used to evaluate your condition. eg...
class Metric
def ko?
metric_value == 'KO'
end
end
For the second part, you could use the same logic. Create a CSS class that makes your box look the way you want it to look, then conditionally add it using string interpolation in the ERB class definition. Then just define a method on the Class (I think it sounds like you would want the application class in this case so you can evaluate it's associated metrics) and use that to return a boolean.

Migrated a new column to a table and updated its form in Rails but fails to update

I am using a blog engine in my Rails project called Lines, and blog posts are contained in a table called LinesArticle.
I want to add a custom attribute to hide certain posts behind a paywall, so I ran the following migration and raked it successfully:
class AddPaywallToLinesArticles < ActiveRecord::Migration
def change
add_column :lines_articles, :paywall, :string
end
end
Each blog post added to this table is posted through a form in the UI contained in my views/Lines/articles folder. I added a new text_field (where the user would write yes or no, I know I could do this through a Boolean but wanted to use a string just to test it).
<div class="input-field checkbox">
<%= f.label :paywall %><br>
<%= f.text_area :paywall%>
The form now loads and shows a text_area; however, that text is not saved when I submit the form. Example output if I check the last record in the table:
<LinesArticle id: 5, title: "Test 2 with False", teaser: nil, insider: nil, paywall: nil>
So the paywall column is not updating with the string I am typing in. I don't think it's an issue with the form -- I am wondering if the real issue is that I need to set some sort of permits: -- this blog engine does not have a Controller associated with it though.
Any ideas?
Your problem is most likely due to a missing strong parameter in your controller:
def create
...
#lineArticle = LinesArticle.create(line_article_params)
...
end
private
def line_article_params
params.require(:line_article).permit(:title, :teaser, :insider, :paywall)
end

Rails scaffolding belongs_to - showing #<MyClass:xxxx>

Experienced Java developer, new to Rails - wondering about belongs_to relationship in scaffolding.
Saw another answer like this
Does rails scaffold command support generate belongs_to or many to many model middle table migration info?
and followed the rails generate scaffold_controller obj:references pattern.
The index/show page is showing #<MyClass:xxxx> instead of the string I want - is there a method in the target class (parent side of the belongs_to) I need to override to specify the identifier?
Also in the edit view, it looks like it's trying to modify the reference as a string rather than as drop-down - is there something I need to specify to make that happen?
Thanks!
BTW - I was able to get similar scaffolding to work in Django and Grails, where the foreign key turned into a drop-down; I'm hoping Rails is equally easy and I'm just missing it.
You can override the #to_s method on the instances, as it is the one being called.
class FooDoodle < ActiveRecord::Base
def to_s
name
end
end
That's when showing a record.
However, when you're actually using the form to set the associations, scaffold will only generate an input field in the view so you can enter the id. You could have a dropdown menu for example, but the options for that dropdown would somehow have to be selected in a manner.
For example, if there are 2000 possible associated records, which ones do you show? Do you show the 2000? Only the first 10? That logic would go into your controller.
So, for example:
class FooDoodlesController < ApplicationController
def edit
#foodoodle = FooDoodle.find(params[:id])
#friends = #foodoodle.possible_friends # or else
end
end
and using select and options_for_select as choices
# _form.html.erb
<%= form_for #foodoodle do |f| %>
<%= f.label :friend %>
<%= f.select :friend, #friends.map{ |p| [p.to_s, p.id] } %>

Rails - default value in text_field but only for new_record?

On a Content model have an attribute named slug. When creating a new record, I want to use a helper to populate this field, but on an existing record I want to use the value from the database.
Currently I have:
<% if #content.new_record? %>
<%= f.text_field :slug, :value => "#{generate_slug(6)}" %>
<% else %>
<%= f.text_field :slug %>
<% end %>
But that seems a bit verbose. Is this the best way, or is there no other way? (Rails newb just trying to find the "Rails way" on issues I'm unsure of)
Edit
I should note that the helper is currently in /app/helpers/application_helper.rb Moved to be a private action in the Contents controller. David's answer worked great.
In your controller
#content.slug ||= generate_slug(6)
This will assign a value to the slug attribute if none is present
Then, in your view you can simply use
<%= f.text_field :slug %>
Options
Try after_initialize callback in your model.
Try creating a method in your model where you set defaults and call it in your new action in the controller. Also call this method if your create fails and you render new. Remember to set default only when no value exists by using the ||= operator.
Example to follow. I'm typing on phone!
I happen to use jQuery in my projects, so when I want some functionality like this, I usually use something like labelify. Then, I'd use something like <%= f.text_field :slug, :title => generate_slug(6) %>. (Hot tip, you don't need to put the #generate_slug call inside of a string if it returns something that will resolve to a string by itself, in fact it's more performant if you don't.)
If you don't want to go with jQuery approach, you might want to wrap this piece of logic in your model.
def Content < ActiveRecord::Base
def slug
self.new_record? ? self.slug_for_new_record : attributes[:slug]
end
private
def slug_for_new_record
# I don't know what you're doing in generate_slug, but it sounds model-
# related, so if so, put it here and not in a helper
end
end
If it really belongs in the view, still another option is to just make your Ruby a little bit more concise (you'll have to judge if this is more readable):
<%= f.text_field :slug, :value => (generate_slug(6) if #content.new_record?) %>
Don't forget the parens surrounding (generate_slug(6) if #content.new_record?). If you do, the if will be applied to the text_field, which is not what you want.
But there are still more ways to do it. The above line of code isn't great if your logic might change and you're pasting this code all over your rails project. When I wanted to add a 'required' class to my text fields but only if they were a new record (we had some legacy data that we didn't want to make people clean up), I created my own form builder with a required_field method that just called text_field and added a 'required' class if the item was a new record. This might seem like a work, but we have around 20 different forms, each with potentially multiple required fields, and it's a lot easier to change the business logic in one place. So if you really think this logic belongs in the view but you've got a ton of these lines of code and you don't want to have to change it in a million places, then FormBuilder is the way to go. I think this is in most cases prettier and more appropriate than a helper, but again, beauty is in the eye of the beholder. Here's my code somewhat adapted for your case:
# config/environment.rb
ActionView::Base.default_form_builder = NamespacesAreFun::FormBuilder
# lib/namespaces_are_fun/form_builder.rb
module NamespacesAreFun
class FormBuilder < ActionView::Helpers::FormBuilder
def slug_field(method, options = {})
opts = options.to_options
opts.merge!(:value => generate_slug) if self.object.new_record?
text_field(method, opts)
end
end
end
# views/.../your_view.html.erb
<%= f.slug_field :slug %>
Hopefully in all of these different approaches is one that fits your project.

ruby on rails form_for

I have a calendar_date_select in a view that shows a table listing all the information on a certain phone. I want to add a To: and From: date range that a user can select and update the table in the view. The structure is like this:
Usage Controller
Detail action in the usage controller that shows the call history from a certain phone.
Inside detail I want the To and from fields with a refresh button.
What is exactly happening in this code:
<% form_for :date_range do |f| %>
<%= f.calendar_date_select :start, :time => true %>
<%= f.calendar_date_select :end, :time => true %>
<%= f.submit "Submit" %>
<% end %>
Does this pass a hash to the usage controller and look for a date_range method? My current route looks like this
usage/detail/id-of-phone
I would like it to look something like this:
usage/detail/id-of-phone#start-end
So I could then (I think) extract the start and end dates from the params with just params[:start] and params[:end]. Am I doing this right, or is there a better way to get the desired result that I want.
I haven't used the calendar_date_select plugin, but you should be getting the parameters back already.
params[:date_range][:start]
params[:date_range][:end]
What you want is the url or the smart solution to get the params?
Please set the routes.rb for the url. Or you can make many method in the 'DataRange' model.
As many programmers using, save many dates in the model. But making us terrible is using the params smartly.
Such as
class Model
def start
......
end
def end
......
end
end
You can't get the params by params[:start] if you pass the params by the form. You can see the form's html for detail.
Please use the
params[:...][:start]

Resources