Rails - Find max instance method - ruby-on-rails

I have a instance method called entry_sum which sums a few other instance methods.
...
def game4sum
if games[3] and games[3].winning_team_id == game4_selection
then 1
else 0
end
end
def entry_sum
game1sum + game2sum + game3sum + game4sum
end
In my view I'm running
<% #entries.each do |entry| %>
...
<%= entry.entry_sum %>
My question is, is it possible to then find the max value for entry_sum if there are X amount of entries in #entries? I'm looking to identify the entry with the highest value for entry_sum. This is my first app and I have a feeling that I need to restructure/visit class vs instance methods.

You can use detect and check the max value. I've put a full example below. You should only need to adapt the last line to your program.
require 'ostruct'
#entries = [OpenStruct.new(:entry_sum => 1), OpenStruct.new(:entry_sum => 4), OpenStruct.new(:entry_sum => 5), OpenStruct.new(:entry_sum => 2)]
#entries.detect { |entry| entry.entry_sum == #entries.map(&:entry_sum).max }

Related

Rails views output is different than Puma web server logs

I have a ruby script called abc.rb in rails config/initializers
require 'http'
class Abc
def initialize(url)
#url = url
#doc = web_lookup(#url)
end
def web_lookup(url_to_open)
begin
return Nokogiri::HTML(HTTP.get(url_to_open).to_s)
rescue
"Please check your URL!"
end
end
def frequency_count
#word_array = #doc.css("p").text.split(" ")
#occurance = Hash.new(0)
#word_array.each {|x| #occurance[x.downcase] += 1 }
#occurance.each {|x,y|
if y > 5
puts "#{x} : #{y} times"
end
}
end
end
And I'm trying to access that script's class in a rails controller.
class UrlsController < ApplicationController
def index
#url_to_check = Abc.new("http://ecodehut.com/linux")
end
end
Everything is fine so far but when I call this variable in index.html.erb <%= #url_to_check.frequency_count %> output is this:
{"it’s"=>1, "like"=>1, "asking"=>1, "“should"=>1, "i"=>5, "go"=>3, "to"=>14, "school?”"=>1, "and"=>8, "“would"=>1, "get"=>1, "a"=>3, "job"=>1, "if"=>2, "school?”.list"=>1, "of"=>1, "advantages"=>1, "is"=>5, "never"=>1, "ending"=>1, "elaborating"=>1, "each"=>1, "throw"=>1, "your"=>2, "mouse"=>1, "away"=>1, "this"=>3, "time."=>1, "keyboard"=>1, "all"=>1, "need"=>1, "mozart."=>1, "navigate..."=>1, "before"=>2, "we"=>1, "begin"=>1, "with"=>3, "configuration"=>1, "here"=>1, "download"=>1, "install"=>2, "system."=>1, "after"=>1, "come"=>1, "back"=>1, "continue."=>1}
But my expected output is this:
to : 14 times
and : 8 times
you : 9 times
the : 6 times
I'm not getting why rails printing everything inside the hash instead of the key values with values bigger than 5 like mentioned in the frequency_count method.
P.S: Abc.rb script results fine and dandy when ran in terminal using ruby Abc.rb
Plese change this and try again
def frequency_count
#word_array = #doc.css("p").text.split(" ")
#occurance = Hash.new(0)
#word_array.each {|x| #occurance[x.downcase] += 1 }
#occurance.select {|x, y| y> 5}
end
The reason is the last line of your method is
#occurance.each {|x,y|
if y > 5
puts "#{x} : #{y} times"
end
}
Just iterate all key and values inside #occurance variable but not sort based on the count. So you are getting all key values in the views instead which has count more than 5.
When you do #occurance.select {|x, y| y> 5}, which filter your hash and gives you the desired values which count is more than 5. Now you can just loop it and print inside views.
Hope you understand.
Update
In your index.html.erb put this code, where you would like to print hash details
<% #occurance.each do |key, value| %>
<span><%= "#{key}: #{value}" %></span><br />
<% end %>

Rails: How to initialize an object with the attributes in strings?

Probably been working on this too long, sloppy design, or both. My issue is I have a model I wish to initialize. The object has like 52 attributes, but I'm only setting a certain ~25 depending on which object I've just scanned. When I scan an object I get the columns and match them up with a hash_map I've created.
Example Hash Map
This just matches the scanned text to their respective attribute name.
hash_map = {"Pizza."=>"pizza_pie","PastaBowl"=>"pasta_bowl","tacos"=>"hard_shell_taco","IceCream"=>"ice_cream","PopTarts"=>"pop_tart"}
What I want to do
menu = RestaurantMenu.new(pizza_pie => var1, pasta_bowl => var2, ...)
My only problem is in my code at the moment I have this...
t.rows.each do |r|
for i in 0..r.length-1
#hash_map[t.combined_columns[i]] => r.[i]
puts "#{hash_map["#{t.combined_columns[i]}"]} => #{r[i]}"
end
end
the puts line displays what I want, but unsure how to get that in my app properly.
Here is several ways to fix this:
hash_map = {"Pizza."=>"pizza_pie","PastaBowl"=>"pasta_bowl","tacos"=>"hard_shell_taco","IceCream"=>"ice_cream","PopTarts"=>"pop_tart"}
attributes.each do |attribute, element|
message.send((attribute + '=').to_sym, hash_map[element])
end
or like this:
class Example
attr_reader :Pizza, :PastaBowl #...
def initialize args
args.each do |k, v|
instance_variable_set("##{k}", v) unless v.nil?
end
end
end
for more details click here
I ended up doing the following method:
attributes = Hash[]
attributes["restaurant"] = tmp_basic_info.name
attributes["menu_item"] = tmp_basic_info.item_name
t.rows.each do |r|
for i in 0..r.length-1
attributes["other"] = t.other_information
attributes[hash_map[t.combined_columns[i]] = r[i]
end
row = ImportMenuItem.new(attributes)
row.save
end

Building an array based on stock levels

I made a simple inventory system, and want to display a selector to choose the size before buying the product. I want the selector to only show the items that are in stock, and not allow the user to choose the ones that aren't. I made it so that when the product is created, it creates a set of variants, with their name and quantity. So when I create a product, it creates 3 variants named Small, Medium, and Large, each with a qty of 0. I used the ruby console to update the qty of each variant so the below would work:
views/product/show.html.erb
<div class="size"><br/>Size: <%= f.select(:size, options_for_select(#sizes_availiable), :prompt => "Select a size") %></div>
product_controller.rb
def show
#product = Product.find(params[:id])
#sizes_availiable = Product.build_size_enum( #product.id )
end
Product.rd
def self.build_size_enum(product_id)
p = Product.find(product_id)
a = []
p.variants.each do |var|
unless var.qty = 0
a << var.name
end
end
a
end
However, the options are empty (with only the option "Select a size"). If I remove the unless statement (which defeats the purpose) from Product.rb so the method looks like below, the list populated with small medium and large:
Product.rd
def self.build_size_enum(product_id)
p = Product.find(product_id)
a = []
p.variants.each do |var|
a << var.name
end
a
end
Any ideas on why the unless statement is causing a problem?
You are using = instead of ==. var.qty = 0 will always test as "true" in Ruby, with the unfortunate side effect of wiping out the data you are trying to test. Change that to var.qty == 0 and you should be fine.

How to make this code more Ruby-way?

I am new to Ruby and Rails (switched from Python and Python frameworks). I'm writing a simple dashboard website which displays information about the S.M.A.R.T. state of hard disks. Here I wrote a helper to display a badge in a table cell near the relevant S.M.A.R.T attribute if it's value meets a condition. At first, the helper code was as simple as in Listing 1, but then I decided to draw a summary of all badges for the specific drive, in addition to the badges near individual S.M.A.R.T. attributes in the table. So at first I added a simple method like:
def smart_chk_device(attrs)
attrs.each { |item| smart_chk_attr(item) }
end
But this approach didn't work and it caused the entire array of attributes to be output to the resulting page. It only started to work when I made it as in Listing 2, but I believe there's something wrong there, and the same thing can be done in a more simple way. Please show me the right, Ruby-way of doing it.
Listing 1:
module HomeHelper
def smart_chk_attr(attr)
case attr[:id].to_i
when 1,197
content_tag(:span, "Warning", :class => "label label-warning") if attr[:raw].to_i > 0
when 5,7,10,196,198
content_tag(:span, "Critical", :class => "label label-important") if attr[:raw].to_i > 0
end
end
end
Listing 2 (works, but I don't like it):
module HomeHelper
def smart_chk_attr(attr)
case attr[:id].to_i
when 1,197
return content_tag(:span, "Warning", :class => "label label-warning") if attr[:raw].to_i > 0
when 5,7,10,196,198
return content_tag(:span, "Critical", :class => "label label-important") if attr[:raw].to_i > 0
else
return String.new
end
return String.new
end
def smart_chk_device(attrs)
output = ""
attrs.each { |item| output << smart_chk_attr(item) }
return output.html_safe
end
end
attrs is an Array of Hashes, where each Hash contains keys :id and :raw with the numeric code of a S.M.A.R.T attribute and its RAW value, both in Strings.
Also, RoR complaints if to remove the last "return String.new" in Listing 2. Why is it so? Doesn't the "case" block all the possible cases, so that control should never reach the end of the function?
I believe this would behave in the same way, and is much shorter:
module HomeHelper
def smart_chk_attr(attr)
return '' unless attr[:raw].to_i > 0
case attr[:id].to_i
when 1,197
content_tag(:span, "Warning", :class => "label label-warning")
when 5,7,10,196,198
content_tag(:span, "Critical", :class => "label label-important")
else ''
end
end
def smart_chk_device(attrs)
attrs.map { |item| smart_chk_attr(item) }.join.html_safe
end
end
Ruby methods return the value of the last expression, so get rid of the explicit returns all over that method. Also, DRY: Don't Repeat Yourself (the attr[:raw] check). In this case, I replaced those with a guard clause at the start of the method. Short-circuiting guard clauses are a matter of taste, but I like them and you'll see them in a lot of Ruby code.
Your method smart_chk_attr(attr) has an extra return at the end, it will never get run.
The each is an enumerator, when it finishes going through each item that you supplied it returns the the original object passed into it, not the modified stuff inside.
If you use collect you will get an array with your modified objects.
If you are wanting a string output you can use join to put them into a string. Join will also take an option for how to join your items.
def smart_chk_device(attrs)
attrs.collect{ |item| smart_chk_attr(item) }.join.html_safe
end

How to format values before saving to database in rails 3

I have a User model with Profit field. Profit field is a DECIMAL (11,0) type. I have a masked input on the form which allows user to input something like $1,000. I want to format that value and remove everything except numbers from it so i will have 1000 saved. Here is what i have so far:
class User < ActiveRecord::Base
before_save :format_values
private
def format_values
self.profit.to_s.delete!('^0-9') unless self.profit.nil?
end
end
But it keeps saving 0 in database. Looks like it is converting it to decimal before my formatting function.
Try this:
def profit=(new_profit)
self[:profit] = new_profit.gsub(/[^0-9]/, '')
end
First of all, this:
def format_values
self.profit.to_s.delete!('^0-9') unless self.profit.nil?
end
is pretty much the same as this:
def format_values
return if(self.profit.nil?)
p = self.profit
s = p.to_s
s.delete!('^0-9')
end
So there's no reason to expect your format_values method to have any effect whatsoever on self.profit.
You could of course change format_values to assign the processed string to self.profit but that won't help because your cleansing logic is in the wrong place and it will be executed after '$1,000' has been turned into a zero.
When you assign a value to a property, ActiveRecord will apply some type conversions along the way. What happens when you try to convert '$1,000' to a number? You get zero of course. You can watch this happening if you play around in the console:
> a = M.find(id)
> puts a.some_number
11
> a.some_number = 'pancakes'
=> "pancakes"
> puts a.some_number
0
> a.some_number = '$1,000'
=> "1,000"
> puts a.some_number
0
> a.some_number = '1000'
=> "1000"
> puts a.some_number
1000
So, your data cleanup has to take place before the data goes into the model instance because as soon as AR gets its hands on the value, your '$1,000' will become 0 and all is lost. I'd put the logic in the controller, the controller's job is to mediate between the outside world and the models and data formatting and mangling certainly counts as mediation. So you could have something like this in your controller:
def some_controller
fix_numbers_in(:profit)
# assign from params as usual...
end
private
def fix_numbers_in(*which)
which.select { |p| params.has_key?(p) }.each do |p|
params[p] = params[p].gsub(/\D/, '') # Or whatever works for you
end
end
Then everything would be clean before ActiveRecord gets its grubby little hands on your data and makes a mess of things.
You could do similar things by overriding the profit= method in your model but that's really not the model's job.
class User < ActiveRecord::Base
before_save :format_values
private
def format_values
self.profit = profit.to_s.gsub(/\D/,'') if profit
end
end
def format_values
self.profit.to_d!
end
I recommend you to write custom setter for this particular instance variable #profit:
class User
attr_accessor :profit
def profit= value
#profit = value.gsub(/\D/,'')
end
end
u = User.new
u.profit = "$1,000"
p u.profit # => "1000"
I would suggest using the rails helper of number with precision. Below is some code.
Generic Example:
number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
Rails code Example:
def profit=(new_profit)
number_with_precision(self[:profit], :precision => 1, :significant => true)
end

Resources