undefined method sitenum - ruby-on-rails

I get a sitenum undefined method error. Problem is I am trying to target site_id and student_number and then increment value of student_number by 1 based on site_id. So there will be at least two records with a 2001 value for student_number if there are two site_ids (e.g. site_id 1, site_id 2). And if a value of 2001 exists for that site, then I would like to increment by 1 so next student_number for that site will be 2002, 2003, etc.
Student Model:
:student_number =>
sitenum = self.site_id
count = Student.count_by_sql("SELECT MAX(student_number) FROM students WHERE site_id = #{sitenum}")
if count >= 2001
Student.sitenum(:order => "student_number DESC").student_number + 1
else
2001
end
Any response would be greatly appreciated.

I understood nothing from a description, but suppose, you want this that way:
:student_number =>
sitenum = self.site_id
count = Student.count_by_sql("SELECT MAX(student_number) FROM students WHERE site_id = #{sitenum}")
if count >= 2001
Student(:condition => { :site_id => sitenum },
:order => "student_number DESC").student_number + 1
else
2001
end

Related

How to get start and end date from quarters?

I am building a rails 5 app.
I need to be able to get the dates that is from a current quarter. With that I mean the user will provide me with a selected quarter (1 to 4) and I will convert that number to a start and end date for that selected quarter. How can I do that?
This is how I tried it but it is good?
def quarter_date(quarter, year)
if quarter == 1
where(date_at: Time.parse("01-01-#{year}")..Time.parse("01-03-#{year}"))
elsif quarter == 2
where(date_at: Time.parse("01-04-#{year}")..Time.parse("01-06-#{year}"))
elsif quarter == 3
where(date_at: Time.parse("01-07-#{year}")..Time.parse("01-09-#{year}"))
elsif quarter == 4
where(date_at: Time.parse("01-10-#{year}")..Time.parse("01-12-#{year}"))
end
end
You mean something like this?
require 'date'
today = Date.today
=> #<Date: 2018-07-02 ((2458302j,0s,0n),+0s,2299161j)>
year = today.year
=> 2018
input = 3
start_date = Date.new(2018, input * 3 - 2, 1)
=> #<Date: 2018-07-01 ((2458301j,0s,0n),+0s,2299161j)>
end_date = Date.new(2018, input * 3 + 1, 1) - 1
=> #<Date: 2018-09-30 ((2458392j,0s,0n),+0s,2299161j)>
It returns the start and end dates for the given quarter of current year.
Update
Updated with method from your attempt:
def quarter_date_range(quarter, year)
start_date = Time.parse("#{year}-#{quarter * 3 - 2}-1")
end_date = (start_date + 2.months).end_of_month
where(date_at: start_date..end_date)
end

How to get next records `bending` by order?

Let say I have a model Product with columns id and sort_number.
id | sort_number (random)
1 | 325
2 | 161
3 | 58
...
147 | 500 # the biggest sort_number number is equal to the Product.count result
...
500 | 5
I want a next(n = 20) and previous(n = 20) methods for the Product instances. IE if I:
product = Product.find(43)
product.sort_number # => 490
product.next(20) # should return products with sort_number equal to (491..500) + (1..10)
How can I implement this functionality? How can I get the next record with sort_number started at 1, if there are no more records next?
OK, I will put this as an answer.
def next id, count
product = Product.find(id)
products = Product.where(
'sort_number > ? AND sort_number <= ?',
product.sort_number, product.sort_number + count
) # this will return 20 requested, unless sort_number is near 500
products |= Product.where(
'sort_number > 1 AND sort_number <= ?',
count - product.count
) if products.count < count
products
end

How to determine the nth weekday for a given month?

This snippet of code determines the date for the nth weekday of a given month.
Example: for the 2nd Tuesday of December 2013:
>> nth_weekday(2013,11,2,2)
=> Tue Nov 12 00:00:00 UTC 2013
Last Sunday of December 2013:
>> nth_weekday(2013,12,'last',0)
=> Sun Dec 29 00:00:00 UTC 2013
I was not able to find working code for this question so I'm sharing my own.
If you are using Rails, you can do this.
def nth_weekday(year, month, n, wday)
first_day = DateTime.new(year, month, 1)
arr = (first_day..(first_day.end_of_month)).to_a.select {|d| d.wday == wday }
n == 'last' ? arr.last : arr[n - 1]
end
> n = nth_weekday(2013,11,2,2)
# => Tue, 12 Nov 2013 00:00:00 +0000
You can use:
require 'date'
class Date
#~ class DateError < ArgumentError; end
#Get the third monday in march 2008: new_by_mday( 2008, 3, 1, 3)
#
#Based on http://forum.ruby-portal.de/viewtopic.php?f=1&t=6157
def self.new_by_mday(year, month, weekday, nr)
raise( ArgumentError, "No number for weekday/nr") unless weekday.respond_to?(:between?) and nr.respond_to?(:between?)
raise( ArgumentError, "Number not in Range 1..5: #{nr}") unless nr.between?(1,5)
raise( ArgumentError, "Weekday not between 0 (Sunday)and 6 (Saturday): #{nr}") unless weekday.between?(0,6)
day = (weekday-Date.new(year, month, 1).wday)%7 + (nr-1)*7 + 1
if nr == 5
lastday = (Date.new(year, (month)%12+1, 1)-1).day # each december has the same no. of days
raise "There are not 5 weekdays with number #{weekday} in month #{month}" if day > lastday
end
Date.new(year, month, day)
end
end
p Date.new_by_mday(2013,11,2,2)
This is also available in a gem:
gem "date_tools", "~> 0.1.0"
require 'date_tools/date_creator'
p Date.new_by_mday(2013,11,2,2)
# nth can be 1..4 or 'last'
def nth_weekday(year,month,nth,week_day)
first_date = Time.gm(year,month,1)
last_date = month < 12 ? Time.gm(year,month+1)-1.day : Time.gm(year+1,1)-1.day
date = nil
if nth.class == Fixnum and nth > 0 and nth < 5
date = first_date
nth_counter = 0
while date <= last_date
nth_counter += 1 if date.wday == week_day
nth_counter == nth ? break : date += 1.day
end
elsif nth == 'last'
date = last_date
while date >= first_date
date.wday == week_day ? break : date -= 1.day
end
else
raise 'Error: nth_weekday called with out of range parameters'
end
return date
end

RSPEC - Test says object is nil, but works fine in practice

I can't figure out why this object keeps coming up as nil.
Here is the error:
1) Item Calculate with just Total
Failure/Error: subject.calculate_tax(tax, sub_category)
TypeError:
nil can't be coerced into Fixnum
# ./app/models/item.rb:111:in `+'
# ./app/models/item.rb:111:in `calculate_tax'
# ./spec/models/item_spec.rb:26:in `block (2 levels) in <top (required)>'
Here is the line it applies to - it thinks "self.tax_rate" is nill (second last argument)
self.tax_amount = ((self.total - self.deduction) - ((self.total - self.deduction) / (1 + self.tax_rate))) * self.tax_adjustment
Here is my Test
describe Item do
subject {Item.new(:report_id => 26 , :name => 'Gas' ,:tax_rate => 0.13, :tax_id => 1 , :category_id => 15 , :sub_category_id => 31 , :job_id => 1 , :total => 20 )}
let(:tax) {Tax.where(id: subject.tax_id).first}
let(:sub_category) {SubCategory.where(id: subject.sub_category_id).first}
it 'Calculate with just Total' do
subject.name.should be == 'Gas'
tax = Tax.find_by_id(subject.tax_id)
subject.sub_category_id.should be == 31
subject.set_nil_values
sub_category.should_receive(:taxable).exactly(3).times.and_return(sub_category.taxable)
tax.should_receive(:rate).exactly(4).times.and_return(tax.rate)
sub_category.should_receive(:tax_adjustment).and_return(sub_category.tax_adjustment)
subject.calculate_tax(tax, sub_category)
subject.should_receive(:tax_rate).exactly(2).times.and_return(tax.rate)
subject.calculate_cost
subject.cost.should be_within(0.01).of(17.70)
subject.tax_amount.should be_within(0.01).of(2.30)
subject.save
end
Your taxes table in your test database appears not to have an entry with id equal to 1 whose tax.rate is not nil

Controller private method not working

I am building an app where users can post. Each post an be upvoted and downvoted. Where the posts are displayed, I have a little form that allows the user to filter the posts. It simply passes a paramter to url called params[:post_filter] (localhost:3000/somepage?post_filter=value). Now, this works just fine and dandy, except the private method I wrote to modify the query does not work.
Here is my query:
def room
#posts = Post.where('lesson_id = ?', params[:id]).order(post_filter_params).page(params[:page]).per(30)
end
and here is my private method:
private
def post_filter_params
chosen_option = params[:post_filter].to_i == 1 or 2 or 3 ? params[:post_filter] : '1'
case chosen_option
when 1
'created_at DESC'
when 2
'upvotes DESC'
when 3
'downvotes DESC'
end
end
Now, whenever I replace the .order() value with one of the strings in my private method, everything works as planned. However, placing private method name inside the .order() value does not work. Any ideas for what is going on?
EDIT
To make sure all values are the same data type I did this but it still does not work:
def post_filter_params
param_option = params[:post_filter].to_i
chosen_option = param_option == 1 or 2 or 3 ? param_option : 1
case chosen_option
when 1
'created_at DESC'
when 2
'(upvotes - downvotes) DESC'
when 3
'downvotes DESC'
end
end
I don't think that private method does what you think it does.
How about this?
def post_filter_params
case params[:post_filter].to_i
when 1
'created_at DESC'
when 2
'upvotes DESC'
when 3
'downvotes DESC'
else
'created_at DESC'
end
end
Try to compare numbers with numbers or strings with strings.
Quote 1, 2, and 3
when '1'
...
Or convert chosen_options to number
I want to explain the point of #shioyama. First, let's focus on this code:
param_option = params[:post_filter].to_i
chosen_option = param_option == 1 or 2 or 3 ? param_option : 1
In an IRB session..
irb(main):005:0> param_option = 1
=> 1
irb(main):006:0> chosen_option = param_option == 1 or 2 or 3 ? param_option : 1
=> true
irb(main):007:0> chosen_option
=> true
irb(main):008:0> param_option = 2
=> 2
irb(main):009:0> chosen_option = param_option == 1 or 2 or 3 ? param_option : 1
=> 2
irb(main):010:0> chosen_option
=> false
So, the line chosen_option = param_option == 1 or 2 or 3 ? param_option : 1 is the same as chosen_option = (param_option == 1) or (2) or (3 ? param_option : 1), that will be either true or false.
What you wanted is, probably: chosen_option = param_option == 1 || param_option == 2 || param_option == 3 ? param_option : 1.
irb(main):036:0> chosen_option = param_option == 1 || param_option == 2 || param_option == 3 ? param_option : 1
=> 3
irb(main):037:0> chosen_option
=> 3
A common shortcut for that is chosen_option = [1, 2, 3].include?(param_option) ? param_option : 1. But in this case, the best would be what #shioyama suggested:
def post_filter_params
case params[:post_filter].to_i
when 2
'upvotes DESC'
when 3
'downvotes DESC'
# you can just fallback to else
# when 1
# 'created_at DESC'
else
'created_at DESC'
end
end
And a last tip: consider using CONSTANTS instead of magic numbers, it will help others reading your code and yourself in a (non long) future :)

Resources