ActionView::Template::Error (PG::UndefinedColumn: ERROR: column does not exist) - ruby-on-rails

model
Attempt 1
def challenge_date
deadline || date_started
end
Attempt 2
def challenge_date
if deadline != date_started
[deadline, date_started].reject(&:blank?).join()
else
date_started
end
end
Both attempts gave the error. I need the method to iterate through next_user_challenge
def next_user_challenge
user.challenges.where('challenge_date > ?', challenge_date).order('challenge_date ASC').first
end
rails c
# some challenges will just have a deadline
id: 1,
name: "Publish a Novel",
deadline: Sat, 26 Nov 2016,
date_started: nil,
user_id: 117,
# some challenges will just have a date_started
id: 2,
name: "Write a Chp",
deadline: nil,
date_started: Thu, 20 Oct 2016,
user_id: 117,
# and some challenges will have both
id: 3,
name: "Run a Mile",
deadline: Thu, 26 Sep 2016,
date_started: Thu, 26 Sep 2016, # If challenge has both deadline and date_started then date_started will be the same date as the deadline
user_id: 117,

Not a postgres query but you can achieve it rails way
def next_user_challenge
user.challenges.select{ |ch| ch.challenge_date > challenge_date }.sort_by(&:challenge_date).first
end
NOTE: This will fetch all challenges from db and then select the valid one.
I will suggest creating a new column in challenges table and using ActiveModal callbacks to update it.

You're using not database model attribute to query the data and of course it will not work.
To solve this problem, there are 2 ways:
Use Deepak way
Use raw SQL query, in this way, you should prepare challenge_date and join with self table to process other query.

Related

Rspec let variable producing weird result

I am having a weird issue with RSpec that I don't quite understand.
This is my port_stock_spec.rb file:
# == Schema Information
#
# Table name: port_stocks
#
# id :bigint(8) not null, primary key
# portfolio_id :integer
# stock_id :integer
# volume :integer
# transaction_price :float
# current_price :float
# percent_change :float
# created_at :datetime not null
# updated_at :datetime not null
# current_value :float
# dollar_change :float
# total_spend :float
# transaction_date :datetime
# action :integer
# position :integer default("open")
# ticker :string
# slug :string
#
require 'rails_helper'
RSpec.describe PortStock, type: :model do
let(:stock) { create(:stock, price: 10.00) }
let(:portfolio) { create(:portfolio) }
let(:port_stock_1) { create(:port_stock, stock: stock, portfolio: portfolio, transaction_price: stock.price, action: :buy, volume: 100) }
context "associations" do
it { should belong_to(:portfolio) }
it { should belong_to (:stock) }
end
context "methods" do
it "should accurately calculate the positive percent_change of the current PortStock" do
port_stock_1.current_price = 20.00
expect(port_stock_1.calculate_percent_change).to eql 100.00
end
it "should accurately calculate the negative percent_change of the current PortStock" do
port_stock_1.current_price = 5.00
expect(port_stock_1.calculate_percent_change).to eql(-50.00)
end
it "should accurately calculate the negative dollar_change of the current PortStock" do
port_stock_1.current_price = 5.00
port_stock_1.volume = 1000
expect(port_stock_1.calculate_dollar_change).to eql (-5000.00)
end
# more specs that may or may no interact with the let variables.
it "should accurately calculate the portfolio's initial_dollar_value" do
expect(portfolio.initial_dollar_value).to eql 1000.00
end
end
Then I have the following method on my portfolio.rb model:
def calculate_portfolio_initial_dollar_value
if self.portfolio.initial_dollar_value.nil?
self.portfolio.initial_dollar_value = 0.0
end
self.portfolio.initial_dollar_value += (self.transaction_price * self.volume)
self.portfolio.save!
end
When I run my test suite, that last test keeps failing, when it shouldn't:
Failures:
1) PortStock methods should accurately calculate the portfolio's initial_dollar_value
Failure/Error: expect(portfolio.initial_dollar_value).to eql 1000.00
expected: 1000.0
got: 798229.0
(compared using eql?)
# ./spec/models/port_stock_spec.rb:77:in `block (3 levels) in <top (required)>'
Finished in 5.05 seconds (files took 3.68 seconds to load)
29 examples, 1 failure, 19 pending
So I put a binding.pry within the it blocks of the last few tests and when I check the portfolio.initial_dollar_value it repeatedly changes the value.
[1] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
id: 14,
user_id: 7,
current_dollar_value: 2864770.0,
percent_change: 75.02,
created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
updated_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
num_winners: 2,
num_losers: 7,
initial_dollar_value: 860679.0,
dollar_change: 92865.0>
[2] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> port_stock_1.portfolio
=> #<Portfolio:0x00007fcdc5c5db28
id: 14,
user_id: 7,
current_dollar_value: 150.0,
percent_change: -85.0,
created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
num_winners: 0,
num_losers: 1,
initial_dollar_value: 1000.0,
dollar_change: -850.0>
[3] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
id: 14,
user_id: 7,
current_dollar_value: 150.0,
percent_change: -85.0,
created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
num_winners: 0,
num_losers: 1,
initial_dollar_value: 1000.0,
dollar_change: -850.0>
[4] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
id: 14,
user_id: 7,
current_dollar_value: 150.0,
percent_change: -85.0,
created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
num_winners: 0,
num_losers: 1,
initial_dollar_value: 1000.0,
dollar_change: -850.0>
[5] pry(#<RSpec::ExampleGroups::PortStock::Methods>)>
I don't understand why.
Thoughts?
Edit 1
This is portfolio.rb Factory:
FactoryBot.define do
factory :portfolio do
user
current_dollar_value { Faker::Number.number(7) }
percent_change { Faker::Number.decimal(2) }
num_winners { Faker::Number.number(1) }
num_losers { Faker::Number.number(1) }
initial_dollar_value { Faker::Number.number(6) }
dollar_change { Faker::Number.number(5) }
end
end
Edit 2
There is a callback on my port_stock.rb model that triggers methods related to portfolio_initial_dollar_value:
after_save :calculate_portfolio_initial_dollar_value
Also other callbacks that impact other aspects of the portfolio:
after_save :update_portfolio_current_dollar_value
after_save :update_portfolio_initial_dollar_value, if: (:total_spend_previously_changed? || :volume_previously_changed?)
def update_portfolio_current_dollar_value
self.portfolio.current_dollar_value = self.portfolio.port_stocks.open.map(&:current_value).sum
self.portfolio.save!
end
def update_portfolio_initial_dollar_value
self.portfolio.initial_dollar_value = self.portfolio.port_stocks.open.map { |ps| ps.volume * ps.transaction_price }.sum
self.portfolio.save!
end
Edit 3
For the full version both the model (port_stock.rb) & spec (port_stock_spec.rb) files, check out this gist. I didn't want to pollute SO with that full dump.
As #grzekos point out you never call stock or port_stock_1 during the execution of it "should accurately calculate the portfolio's initial_dollar_value" test.
Why? Because you used let to setup/create the test objects.
If you want to always setup/create stock, portfolio and port_stock_1 you can either use let! (RSpec documentation) or use a before block like this:
let(:stock) { create(:stock, price: 10.00) }
let(:portfolio) { create(:portfolio) }
let(:port_stock_1) { create(:port_stock, stock: stock, portfolio: portfolio, transaction_price: stock.price, action: :buy, volume: 100) }
before do
stock
portfolio
port_stock_1
end
Why do you see different numbers during debuging with pry?
In your first test you called the portfolio object, which was created with FactoryBot. The Factory asinged a random 6 digit number to the initial_dollar_value atttribute via Faker::Number.number(6).
In your second test you called port_stock_1.portfolio. Now the block of let(:port_stock_1) gets evaluated. This creates a PortStock object, which in its after_save method updates the initial_dollar_value of portfolio.
All subsequet calls of portfolio or port_stock_1.portfolio do not change the value of initial_dollar_value anymore.
Ok, so the failing test is:
it "should accurately calculate the portfolio's initial_dollar_value" do
expect(portfolio.initial_dollar_value).to eql 1000.00
end
Here I can see that you create the portfolio object and the initial_dollar_value is set (in the factory) to Faker::Number.number(6). Why do you expect it to be equal to 1000.00?
The stock or the port_stock_1 objects are never created when you run this particular test. Quoting the Rspec let documentation
Use let to define a memoized helper method. The value will be cached across
multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked.

Rails/Rspec - objects not existing until interacted with

I have the following code where I create some instances of records using FactoryBot:
describe "#force_recalculation_of_lab_container_labs" do
(1..5).each do |n|
let("lab_#{n}".to_sym) { create(:lab) }
let("lab_container_#{n}".to_sym) { create(:skill_path) }
let("lab_collection_#{n}".to_sym) do
create(:lab_collection, lab_container: eval("lab_container_#{n}"))
end
end
context 'when adding labs' do
it "starts with 0 labs" do
expect(lab_collection_1.labs.count).to eq(0)
end
(1..3).each do |n|
let("lab_collection_inclusion_#{n}") do
create(:lab_collection_inclusion,
included_item_id: eval("lab_#{n}").id,
included_item_type: 'Lab',
lab_collection_id: eval("lab_collection_1").id,
subscribed: 0)
end
end
it "updates the lab total correctly after adding labs" do
binding.pry
end
end
end
From my pry point, I receive the following:
LabCollectionInclusion.count
=> 0
lab_collection_1.lab_collection_inclusions.count
=> 0
When I then call a record individually, it appears to then exist:
lab_collection_inclusion_1
<LabCollectionInclusion:0x000055a45c985b10
id: 1,
included_item_id: 1,
included_item_type: "Lab",
lab_collection_id: 4,
subscribed: false,
created_at: Thu, 01 Nov 2018 10:48:00 UTC +00:00,
updated_at: Thu, 01 Nov 2018 10:48:00 UTC +00:00>
After which point it exists when searching:
LabCollectionInclusion.count
=> 1
lab_collection_1.lab_collection_inclusions.count
=> 1
Obviously I don't want to have to do this for every record so my 2 questions are first of all why is this happening, and second of all how to correct it?
Thanks in advance
This is expected behavior because let is designed to lazy-evaluated. Quote from the docs of let:
Note that let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked. You can use let! to force the method's invocation before each example.
As described in the documentation: Just let! (note the !) instead of let when you need the records to be created without calling them by their name first.

is it possible to override built-in Ruby methods?

I am working on a problem where I have to pass an rpsec test. The problem is that the method is using the same name as a built in ruby method .count
given that I cannot change the rspec test, is it possible to override .count to behave differently? if not, is there a better way to get around this?
here is the rspec test I am trying to pass
subject = FinancialSummary.one_day(user: user, currency: :usd)
expect(subject.count(:deposit)).to eq(2)
my code:
class FinancialSummary
def self.one_day(user: user, currency: currency)
one_day_range = Date.today.beginning_of_day..Date.today.end_of_day
find_transaction(user.id, currency).where(created_at: one_day_range)
end
def self.find_transaction(user_id, currency)
Transaction.where(user_id: user_id,
amount_currency: currency.to_s.upcase
)
end
end
output:
[#<Transaction:0x00007f9b39c2e9b8
id: 1,
user_id: 1,
amount_cents: 1,
amount_currency: "USD",
category: "deposit",
created_at: Sat, 10 Mar 2018 18:46:53 UTC +00:00,
updated_at: Sat, 10 Mar 2018 18:46:53 UTC +00:00>,
#<Transaction:0x00007f9b3d0dbc38
id: 2,
user_id: 1,
amount_cents: 2000,
amount_currency: "USD",
category: "deposit",
created_at: Sat, 10 Mar 2018 18:47:43 UTC +00:00,
updated_at: Sat, 10 Mar 2018 18:47:43 UTC +00:00>,
#<Transaction:0x00007f9b3d0b3fa8
id: 7,
user_id: 1,
amount_cents: 1200,
amount_currency: "USD",
category: "withdraw",
created_at: Mon, 05 Mar 2018 02:22:42 UTC +00:00,
updated_at: Tue, 06 Mar 2018 18:48:20 UTC +00:00>]
it is printing out, what I believe to be the correct information, up until the test attempts to count the transactions by their category: 'deposit'. Then I get this error message:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: deposit: SELECT COUNT(deposit) FROM "transactions" WHERE "transactions"."user_id" = ? AND "transactions"."amount_currency" = ?
EDITED FOR MORE INFO
Some Assumptions Were Made in the Writing of this answer and modifications may be made based on updated specifications
Overriding count is a bad idea because others who view or use your code will have no idea that this is not the count they know and understand.
Instead consider creating a scope for this like
class FinancialSummary < ApplicationRecord
scope :one_day, ->(user:,currency:) { where(user: user, currency: currency) } #clearly already a scope
scope :transaction_type, ->(transaction_type:) { where(category: transaction_type) }
end
then the test becomes
subject = FinancialSummary.one_day(user: user, currency: :usd)
expect(subject.transaction_type(:deposit).count).to eq(2)
SQL now becomes:
SELECT COUNT(*)
FROM
"transactions"
WHERE
"transactions"."user_id" = ?
AND "transactions"."amount_currency" = "usd"
AND "transactions"."category" = "deposit"
Still very understandable and easy to read without the need to destroy the count method we clearly just used.
It's not clear what object the count message is being sent to because I don't know what FinancialSummary.one_day(user: user, currency: :usd) returns, but it seems like you are saying count is a method on whatever it returns, that you can't change. What does FinancialSummary.one_day(user: user, currency: :usd).class return?
Perhaps one solution would be to alias it on that object by adding alias_method :count, :account_count and then in your test calling expect(subject.account_count(:deposit)).to eq(2)
It would be easier if you could post the FinancialSummary#one_day method in your question.

How to correctly use Devise + Confirmable + fixtures

I'm using Devise + Confirmable for user authentication and Minitest + Capybara + fixtures for testing. I can make working logged in tests for users as long as I included some form of login (either with the helper login_as(#user) or going to the login page with Capybara) and the line#user.confirm before running.
How can I confirm users in the fixture itself though so I don't have to include this line every time.
Right now I have:
confirmed_at: Time.now
in the yml, but it doesn't seem to make a difference.
Here is a working sample test, if it's useful for illustration:
def setup
#user = users(:user)
end
test 'user should be redirected to profile edit on first login' do
#user.confirm
visit(new_user_session_path)
fill_in('user_email', :with => #user.email)
fill_in('user_password', :with => 'foobar')
click_button('Log in')
assert_current_path(edit_user_registration_path)
end
and the user fixture:
user:
email: test1#example.com
confirmed_at: Time.now
encrypted_password: <%= Devise::Encryptor.digest(User, 'foobar') %>
sign_in_count: 0
I updated my answer. The solution of the problem is found here. You need to configure it as in the guide and call the method user.confirm! inside the module ControllerMacros method def login_user
https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)#controller-specs
Open source github page of a complete Devise Rspec Project including testing
https://github.com/RailsApps/rails-devise/blob/master/spec/features/users/sign_in_spec.rb
you are trying to set
User.confirmed_at = Time.now
but confirmed_at has datetime datatype at least in my postegresql db, so this may depend on the db you are using.
confirmed_at: datetime
So this is the Time.now format
pry(main)> Time.now
=> 2017-03-14 11:14:06 +0100
While this is is the User.confirmed_at format
user.confirmed_at
=> Sun, 05 Mar 2017 15:05:03 UTC +00:00
So you should use a compatible format/variable, try to search the DateTime class for a compatible format that includes the UTC as DateTime.now returns:
[40] pry(main)> DateTime.now
=> Tue, 14 Mar 2017 11:19:25 +0100
DateTime has a utc() method. If I run this it is almost what is needed.
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc
=> 2005-02-21 16:11:12 UTC
Check the DateTime api and if needed you can check the utc() method on github
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC
# File activesupport/lib/active_support/core_ext/date_time/calculations.rb, line 168
def utc
utc = new_offset(0)
Time.utc(
utc.year, utc.month, utc.day,
utc.hour, utc.min, utc.sec + utc.sec_fraction
)
end
http://api.rubyonrails.org/classes/DateTime.html#method-i-utc

Comparing ranges of datetimes in Ruby on Rails against one-another

Right now I'm trying to see if a certain show's start and end times overlap another show that's currently recording => true where 'show' is the TV show the user wants to record.
def self.record_show
shows = Box.first.shows.where(:recording => true).flatten
show_start_and_end_times = shows.collect {|x| x.start_time..x.end_time}
current_show_time = show.start_time..show.end_time
overlap = show_start_and_end_times.select {|c| current_show_time.overlaps?(c)}
if overlap.present?
nil
else
show.update_attributes(:recording => true)
show.save
end
end
It runs the method, but I'm having difficulty figuring out how to get it so that it finds the actual currently recording show that's causing the overlap. So for example, let's say in 'shows' I currently have two shows:
[#<Show id: 181, box_id: 78, title: "The Fox", channel: 22, single_recording: true, created_at: "2014-08-12 19:55:49", updated_at: "2014-08-12 20:09:24", start_time: "2014-08-12 19:55:49", end_time: "2014-08-12 20:25:49", recording: true>, #<Show id: 186, box_id: 78, title: "Funniest Home Videos", channel: 45, single_recording: true, created_at: "2014-08-12 19:55:49", updated_at: "2014-08-12 20:09:27", start_time: "2014-08-12 23:20:49", end_time: "2014-08-13 00:20:49", recording: true>]
In show_start_and_end_times I have:
[Tue, 12 Aug 2014 19:55:49 UTC +00:00..Tue, 12 Aug 2014 20:25:49 UTC +00:00, Tue, 12 Aug 2014 23:20:49 UTC +00:00..Wed, 13 Aug 2014 00:20:49 UTC +00:00]
In current_show_time I have:
Tue, 12 Aug 2014 19:55:49 UTC +00:00..Tue, 12 Aug 2014 20:55:49 UTC +00:00
Which means that in overlap I have the start_time..end_time of the first show_start_and_end_times show, which is the one that is causing the overlap:
[Tue, 12 Aug 2014 19:55:49 UTC +00:00..Tue, 12 Aug 2014 20:25:49 UTC +00:00]
I tried comparing the two times against one-another:
(shows.first.start_time..shows.first.end_time) == (overlap.first)
Which gives me false, even though the times are exactly the same. How can I compare the overlap time against the shows list to figure out which show is causing the overlap?
You'll wand to check out:
http://guides.rubyonrails.org/active_record_querying.html
Using info from that you might be able to do something like:
def self.record_show
overlapping_shows = Box.first.shows.where(recording: true).where("start_time <= :show_end AND end_time >= :show_start", {show_start: show.start_time, show_end: show.end_time}).flatten
if overlapping_shows.present?
nil
else
show.update_attributes(:recording => true)
show.save
end
end
This is a pretty common problem, and I'd recommend checking out this SO question for general algorithm advice on how to do the overlap checking:
Determine Whether Two Date Ranges Overlap
UPDATE:
I would change it to something like this:
shows = Box.first.shows.where(:recording => true).flatten
overlapping_show = nil
current_show_time = show.start_time..show.end_time
shows.each do |s|
if current_show_time.overlaps?(s.start_time..s.end_time)}
overlapping_show = s
break # you could alternatively return an array of overlapping
# if you anticipate more than 1 will overlap
end
end
if overlap.present? #...

Resources