ruby (for rails) sort_by instantiated variable - ruby

In a rails 3.2.17 application, with ruby 1.9.3, a method filters a previously-found set of products based on a condition. If it passes the condition, it creates an instance variable for the record (rate), which itself is the product of another method (shown_price).
#products = #inventories.map(&:product).uniq
def validarray
validarray = Array.new
#products.each do |product|
#inventories_for_product = #inventories.select{ |i| i.product_id == product.id }
if #inventories_for_product.count == #threshhold
product.rate = #inventories_for_product.map(&#shown_price).inject(0, :+)
validarray << product
end
end
validarray.sort_by{|p| p[:rate]}
end
#validarray = validarray
All elements in this method generate proper data which can be viewed via the browser. In fact (even re-calling sort_by)
<% #validarray.sort_by{|p| p[:rate]}.each do |vp| %>
<%= vp.id %> <%= vp.rate %><br />
<% end %>
will show the proper data.
Issue when I ask for descending order
#validarray.sort_by{|p| -p[:rate]}
undefined method `-#' for nil:NilClass
whereas for ascending order
no complaints, but no sorting either.
I assume that the symbol
:rate is wrong
and ruby lets the stuff unsorted as ASC is invoked by default and has nothing to sort with, but complains otherwise because it has no tools in its hands.

Most likely yourate attribute is not a column in products table, but it is defined as a method within Product model. This means that [] method on the model will always return nil, as it only reads given database column and perform a typecast. In short, do:
#validarray.sort_by &:rate
or (in reverse order)
#validarray.sort_by{|p| -p.rate }

Possibly not all products have a :rate value. You could either:
#validarray.sort_by{|p| p[:rate]}.reverse
or:
#validarray.sort_by{|p| -(p[:rate] || 0) }

Related

Iterate over a singleton or multiple of a returned DataMapper class

I have the following code
if admin_authorized?
#allschools = School.all
elsif faculty_authorized?
#allschools = School.get(Faculty.get(session[:faculty_id]))
end
I am getting this error
undefined method `each' for #<School:0x007f88a1360318>
#allschools will either be an array of schools or a singleton school. I need to iterate over this list in the view, like so:
<% #allschools.each do |s| %>
<%= f_optionselected(s.id.to_s, params[:school], s.name) %>
<% end %>
I've tried to do if statements with #allschools.count, #allschools.typeof(Array), ....
Should I use two different variables and a PHP type isset() statment to determine which block to display? Or is there a way to iterate over the statement in some Ruby way?
School.all returns an ActiveRecord::Relation and you can iterate through it, as you do with each. I think School.get returns a single School instance, right? Try to wrap it into an Array:
#allschools = [School.get(Faculty.get(session[:faculty_id]))]

setting and array of params in rails

A series of objects need to be purchased in a single transaction, however each item must be a different transaction object due to relationship issues.
A confirmation action aims to list each of these items independently. The controller attempts to prepare parameters for viewing each transaction
<% #transactions.each do |transaction| %>
<%= params[:product_id][:product_id] %>
[...]
as follows:
#transactions = params[:product_ids].map do |product_id|
#transaction = Transaction.new
#inventories = Inventory.where(['product_id = ?', product_id]).all
#my_price = my_price
params[:product_id][:product_id] = product_id # sanity check
params[:product_id][:additional_info] = session[:additional_info]
params[:product_id][:rate] = #inventories.map(&#my_price).inject(0, :+)
end
#sub_total = #transactions.params[:product_id][:rate].sum
However undefined method []= for nil:NilClass is generated when building the params for each product_id. This command is obviously mistaken. [The approach could also be inefficient/ineffective...]
I believe you don't really mean params[:product_id][:product_id], but rather params[product_id][:product_id], since :product_id is a symbol, not a variable.
When setting a new hash in the hash, you need to first define it as such:
params[product_id] = {}
params[product_id][:product_id] = product_id
# ...
This should resolve the undefined method []= for nil:NilClass. I guess you will encounter some more problems (like #inventories.map(&#my_price)) after you fix this one though...
With some guesswork, I guess a code which does what you want will look something like this:
#transactions = params[:product_ids].map do |product_id|
transaction = Transaction.new
transaction.product_id = product_id # sanity check
transaction.additional_info = session[:additional_info]
transaction.rate = Inventory.where(['product_id = ?', product_id]).sum(:my_price)
transaction
end
#sub_total = #transactions.map(&:rate).sum
And the view would be something like this:
<% #transactions.each do |transaction| %>
<%= transaction.product_id %>
[...]

Is sprintf incompatible with sinatra?

Say I have this:
class Account
...
property :charge, Decimal, :precision => 7, :scale => 2
...
classy stuff
...
def self.balance(prefix)
x = Account.get(prefix.to_sym).order(:fields => [:charge]).sum(:charge)
sprintf("%5.2f", x)
end
end
(Edit: The value of all :charge fields is 0.13E2 (0.1E2 + 0.3E1). This is correctly returned. Only in a View does it seem to get borked from sprintf)
In IRB Account.balance(:AAA) returns => "13.00"
if I call Account.balance(:AAA) from a view I get TypeError at /accounts
can't convert nil into Float
Account.balance(:AAA) works anywhere I call it except in a view. If I remove sprintf("%5.2f", x) I get 0.13E2 in my view. (using Account.balance(:AAA).to_f in a view gives me 13.0)
Is sinatra incompatible with sprintf? or am I not understanding how to use sprintf?
(Edit: This is the offending view:)
<section>
<% #accounts.each do |account| %>
<article>
<h2><%= account.prefix %></h2>
<span><p>This account belongs to <%= account.name %> & has a balance of $<%= Account.balance(account.prefix) %>.</p></span>
</article>
<% end %>
</section>
Wouldn't it make more sense to define balance as an instance method rather than a class method? It looks from your example like you're calling balance in an account-specific way anyway, so why not make it:
# the model
class Account
#...
def balance
amount = self.order(:fields => [:charge]).sum(:charge)
sprintf "%5.2f", amount
# or the infix version:
"%5.2f" % amount
end
end
,
# the view
...balance of $<%= account.balance %>...
I know that this doesn't address sprintf per se, but the problem is more likely to be coming from the slightly convoluted lookup than from a built-in method. Even if my specific code doesn't suit your application, it might be worth simplifying the lookup step, even if that involves a few more lines of code.
The advantage of this approach is that there is no doubt that you'll be getting the right Account record.
tested it with a little sinatra app and it worked for me
app.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
get '/' do
#x = 10.23
erb :index
end
views/index.erb
<%= sprintf("%5.2f", #x) %>
output:
10.23
ruby 1.9.2 / sinatra 1.3.1
I think there is another error before the sprintf because of your error message:
can't convert nil into Float
seems like your x is nil. try to be sure that x is not nil there, then sprintf should work as expected.

Array of Ruby objects returning strings on each method. Why?

Useful additional info: I am using the decent_exposure gem so this might be the issue - correcting the code below:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tags = Array.new
filter_tag_names.each do |f|
t = Tag.find_by_name(f)
filter_tags << t
end
end
end
So, something funny happens when I call this in the view:
query string ?utf8=✓&filter_tag_names=test
<% get_filter_tags.each do |ft| %>
<%= ft.name %>
<% end %>
Error message: undefined method `name' for "test":String
Why is this trying to call name on a string not a Tag object? If I put the following in the view, and have jut one filter_tag_names item
def getfiltertag
Tag.find_by_name(params[:filter_tag_names])
end
#view
<%= getfiltertag.name %>
query string: ?utf8=✓&filter=test
like above then I can call name just fine, so obviously I am doing something wrong to get an array of strings instead of objects. I just don't know what. Any suggestions?
Your problem is that each returns self — so if you write filter_tag_names.each, it returns filter_tag_names. You could fix this by explicitly returning filter_tags, but more idiomatically, you could just rewrite it as:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tag_names.map {|f| Tag.find_by_name(f) }
end
end
Just as an aside, this method will return nil if there aren't any filter tag names. You may want to do that, or you might want to return an empty collection to avoid exceptions in the calling code.

Adding values in a group_by in Rails

I'm working on generating quarterly royalty reports for authors. I have a Report model (not backed by a database table) that I'm using to display the report for specific authors based on the year and quarter. I'm routing that to author/:author_id/:reports/:year/:quarter
I've got some pretty ugly code in my controller right now, for the sake of getting things working. I can refactor later:
def show
#author = Author.find(params[:author_id])
#find the orders that took place in the current quarter and year for the report
#orders = Order.get_current_orders(quarter_range, params[:year])
#find only the products that apply for the current author
#author_products = #author.products
#find only the line_items that match the current quarter's orders and the author's products
#line_items = LineItem.find_all_by_order_id_and_product_id(#orders, #author_products)
#group the line items by product
#line_items_by_product = #line_items.group_by { |l| l.product_id }
end
That let's me do this in my view:
<%= #line_items_by_product.each do |product, line_item | %>
<h3><%= product %></h3>
<% line_item.each do |line_item| %>
<%= line_item.quantity %> <br />
<% end %>
<% end %>
Two things I need to fix. Right now, product just returns the product id, not the title (it's stored in the products table, not the line_items table). I can't access product.title here obviously, but I need to get the title in the grouping.
My second issue is that instead of just looping over the quantity of every single line item, I want to total the quantity of each line item and just display that. So in stead of getting , 1, 10, 55 ... I just want 66. I tried array#inject, but I'm not grasping the syntax.
And, that all be said ... I'm sure I'm doing more work than I need to. I started with a lot of the controller code in the model but was having a lot of undefined method errors. Any advice or help would be appreciated.
I agree that you need to refactor your code, and I'm only offering these answers to your specific questions - these are not my overall recommendations.
First Problem
Remember this is Ruby, a Product instance is just as valid to group by as the Integer that you're currently using. So, you can change your group_by call to:
#line_items_by_product = #line_items.group_by { |l| l.product }
...and then change your view to...
<h3><%= product.title %></h3>
Second Problem
<%= line_item.sum {|li| li.quantity } %>

Resources