Strange difference between .size and .count when grouping SQL query - ruby-on-rails

I have scope in Usage model:
# == Schema Information
# id :uuid
# service :string
# organization_id :uuid
# document_id :uuid
# created_at :datetime
class Usage < ApplicationRecord
belongs_to :document
scope :grouped, -> {
joins(document: :user)
.select("COUNT(usages.document_id), usages.service, usages.document_id, documents.subject, MAX(usages.created_at)")
.group('service', 'documents.subject', 'document_id', "CONCAT(users.first_name, ' ', users.last_name)", 'usages.created_at::date')
.order("MAX(usages.created_at) DESC")
}
...
When in the rails console I do result = Usage.all.grouped, result:
Usage Load (2.5ms) SELECT COUNT(usages.document_id), usages.service, usages.document_id, documents.subject, MAX(usages.created_at) FROM "usages" INNER JOIN "documents" ON "documents"."id" = "usages"."document_id" INNER JOIN "users" ON "users"."id" = "documents"."user_id" GROUP BY "usages"."service", documents.subject, "usages"."document_id", CONCAT(users.first_name, ' ', users.last_name), usages.created_at::date ORDER BY MAX(usages.created_at) DESC LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Usage id: nil, service: "document", document_id: "f3455d09-0681-46d0-bf2d-54bb3286089b">, #<Usage id: nil, service: "document", document_id: "db3303de-f2ac-4611-8603-da70847e7eeb">, #<Usage id: nil, service: "document", document_id: "642b9fa8-e72e-4bb8-9551-d30f92bf7e67">]>
Then, to expand this query into a hash with the parameters I need, I call the .size method on this scope.
result.size
(1.7ms) SELECT COUNT(*) AS count_all, "usages"."service" AS usages_service, documents.subject AS documents_subject, "usages"."document_id" AS usages_document_id, CONCAT(users.first_name, ' ', users.last_name) AS concat_users_first_name_users_last_name, usages.created_at::date AS usages_created_at_date FROM "usages" INNER JOIN "documents" ON "documents"."id" = "usages"."document_id" INNER JOIN "users" ON "users"."id" = "documents"."user_id" GROUP BY "usages"."service", documents.subject, "usages"."document_id", CONCAT(users.first_name, ' ', users.last_name), usages.created_at::date ORDER BY MAX(usages.created_at) DESC
=> {
["document", "Alena Cartwright II welcome a Gorgon", "f3455d09-0681-46d0-bf2d-54bb3286089b", "Test Test", Tue, 09 Aug 2022]=>1,
["document", "Thanh Harber Sr. view a Ankylosaurus", "db3303de-f2ac-4611-8603-da70847e7eeb", "Test2 Test", Mon, 08 Aug 2022]=>1,
["document", "Rolando Langosh DO bust a Clay Golem", "642b9fa8-e72e-4bb8-9551-d30f92bf7e67", "Test3 Test", Sun, 07 Aug 2022]=>1
}
As we can see, when the .size method is called, another SELECT query comes in.
According to the documentation - .size returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded, and calling collection.length if it has.
But at the same time, I get an error if I try to call the .count method on this scope.
result.count
(1.4ms) SELECT COUNT(COUNT(usages.document_id), usages.service, usages.document_id, documents.subject, MAX(usages.created_at)) AS count_count_usages_document_id_usages_service, "usages"."service" AS usages_service, documents.subject AS documents_subject, "usages"."document_id" AS usages_document_id, CONCAT(users.first_name, ' ', users.last_name) AS concat_users_first_name_users_last_name, usages.created_at::date AS usages_created_at_date FROM "usages" INNER JOIN "documents" ON "documents"."id" = "usages"."document_id" INNER JOIN "users" ON "users"."id" = "documents"."user_id" GROUP BY "usages"."service", documents.subject, "usages"."document_id", CONCAT(users.first_name, ' ', users.last_name), usages.created_at::date ORDER BY MAX(usages.created_at) DESC
Traceback (most recent call last):
1: from (irb):5
ActiveRecord::StatementInvalid (PG::UndefinedFunction: ERROR: function count(bigint, character varying, uuid, character varying, timestamp without time zone) does not exist)
LINE 1: SELECT COUNT(COUNT(usages.document_id), usages...
What is the difference between using .size and .count in this example?
Why does the .count method wrap the existing SQL COUNT method with another COUNT?

From the docs;
If count is used with Relation#select, it will count the selected
columns:
# Person.select(:age).count
SELECT COUNT("people"."age") FROM "people"
So, if your query happens to have a SELECT, then it'll wrap whatever it contains within COUNT (see select_values)
By definition when using size it'll default to count(:all) unless the object is loaded. So you anyway end up using count.

Related

Unnecessary second query for an EXISTS in Rails ActiveRecord

I'm trying to write a simple EXISTS query using ActiveRecord. It seems to be running 2 queries for some reason.
User.where("exists (select 'X' from roles where roles.role = 'teacher' and roles.user_id = users.id)")
From the log
Query 1
(1.2ms) SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "users" WHERE (users.id <> 3 and exists (select 'X' from roles where roles.role = 'teacher' and roles.user_id = users.id)) LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 2], ["OFFSET", 0]]
Query 2
User Load (1.0ms) SELECT "users".* FROM "users" WHERE (users.id <> 3 and exists (select 'X' from roles where roles.role = 'teacher' and roles.user_id = users.id)) LIMIT $1 OFFSET $2 [["LIMIT", 2], ["OFFSET", 0]]
Is there a way to avoid this. I would write the query directly in SQL a bit like this:
SELECT u.*
FROM users u,
roles r
WHERE r.user_id = u.id
AND u.id <> #{#user.id}
AND r.role = 'teacher'

Ruby on Rails query yielding unexpected results

This is a followup to an earlier thread: Ruby on Rails query not working properly.
As noted, I have several listings. In particular, a listing has_many :spaces, through: :designations and has_many :amenities, through: :offerings.
I define filters to restrict the listings that get shown.
The two main ones are:
# filter by amenities
if params[:search][:amenity_ids].present? && params[:search][:amenity_ids].reject(&:blank?).size > 0
#listings = #listings.joins(:amenities).where(amenities: { id: params[:search][:amenity_ids].reject(&:blank?) }).group('listings.id').having('count(*) >= ?', params[:search][:amenity_ids].reject(&:blank?).size)
end
# filter by space type
if params[:search][:space_ids].present? && params[:search][:space_ids].reject(&:blank?).size > 0
#listings = #listings.joins(:spaces).where('space_id IN (?)', params[:search][:space_ids].reject(&:blank?)).uniq
end
(Note that these reflect the solution indicated in the earlier thread.)
The first filter says: get all of the listings that have ALL of the selected amenities.
The second filter says: get all of the listings that match ANY of the selected space types.
But one issue remains. If I filter for space types 1 and 2 and amenities 1 and 2, I get listing A (which has space types 1 and 2 and amenity 2).
But I should presumably get [] since no listing has both amenities 1 and 2.
What is going on with these queries? Should they not be independent, but chainable?
Here is the output (I disabled the other filters for clarity):
Started GET "/listings/search?utf8=%E2%9C%93&search%5Baddress%5D=London%2C+United+Kingdom&search%5Bprice_min%5D=0&search%5Bprice_max%5D=1000.0&search%5Bprice_lower%5D=0&search%5Bprice_upper%5D=1000&search%5Bsize_min%5D=0&search%5Bsize_max%5D=1000&search%5Bsize_lower%5D=0&search%5Bsize_upper%5D=1000&search%5Bspace_ids%5D%5B%5D=1&search%5Bspace_ids%5D%5B%5D=2&search%5Bspace_ids%5D%5B%5D=&search%5Bamenity_ids%5D%5B%5D=1&search%5Bamenity_ids%5D%5B%5D=2&search%5Bamenity_ids%5D%5B%5D=&search%5Bsort_by%5D=Distance&commit=Apply+Filters" for ::1 at 2015-10-31 14:25:58 +0000
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by ListingsController#search as HTML
Parameters: {"utf8"=>"✓", "search"=>{"address"=>"London, United Kingdom", "price_min"=>"0", "price_max"=>"1000.0", "price_lower"=>"0", "price_upper"=>"1000", "size_min"=>"0", "size_max"=>"1000", "size_lower"=>"0", "size_upper"=>"1000", "space_ids"=>["1", "2", ""], "amenity_ids"=>["1", "2", ""], "sort_by"=>"Distance"}, "commit"=>"Apply Filters"}
(1.5ms) SELECT MAX("listings"."price") FROM "listings"
(0.6ms) SELECT MAX("listings"."size") FROM "listings"
Listing Load (4.4ms) SELECT DISTINCT "listings".* FROM "listings" INNER JOIN "offerings" ON "offerings"."listing_id" = "listings"."id" INNER JOIN "amenities" ON "amenities"."id" = "offerings"."amenity_id" INNER JOIN "designations" ON "designations"."listing_id" = "listings"."id" INNER JOIN "spaces" ON "spaces"."id" = "designations"."space_id" WHERE "amenities"."id" IN (1, 2) AND (space_id IN ('1','2')) GROUP BY listings.id HAVING count(*) >= 2 LIMIT 24 OFFSET 0
Image Load (0.5ms) SELECT "images".* FROM "images" WHERE "images"."listing_id" = $1 ORDER BY "images"."id" ASC LIMIT 1 [["listing_id", 1]]
Space Load (0.6ms) SELECT "spaces".* FROM "spaces" INNER JOIN "designations" ON "spaces"."id" = "designations"."space_id" WHERE "designations"."listing_id" = $1 [["listing_id", 1]]
Rendered listings/_map_infowindow.html.erb (56.1ms)
Rendered listings/_price_slider.html.erb (0.7ms)
Rendered listings/_size_slider.html.erb (0.6ms)
Space Load (0.4ms) SELECT "spaces".* FROM "spaces"
Amenity Load (0.4ms) SELECT "amenities".* FROM "amenities"
Rendered scripts/_checkbox_toggle.html.erb (0.5ms)
Rendered listings/_search_filters.html.erb (75.5ms)
(0.4ms) SELECT "spaces"."name" FROM "spaces" INNER JOIN "designations" ON "spaces"."id" = "designations"."space_id" WHERE "designations"."listing_id" = $1 [["listing_id", 1]]
CACHE (0.0ms) SELECT "images".* FROM "images" WHERE "images"."listing_id" = $1 ORDER BY "images"."id" ASC LIMIT 1 [["listing_id", 1]]
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 3]]
Avatar Load (0.7ms) SELECT "avatars".* FROM "avatars" WHERE "avatars"."user_id" = $1 ORDER BY "avatars"."id" ASC LIMIT 1 [["user_id", 3]]
Rendered listings/_listing_grid.html.erb (80.8ms)
(3.1ms) SELECT DISTINCT COUNT(DISTINCT "listings"."id") AS count_id, listings.id AS listings_id FROM "listings" INNER JOIN "offerings" ON "offerings"."listing_id" = "listings"."id" INNER JOIN "amenities" ON "amenities"."id" = "offerings"."amenity_id" INNER JOIN "designations" ON "designations"."listing_id" = "listings"."id" INNER JOIN "spaces" ON "spaces"."id" = "designations"."space_id" WHERE "amenities"."id" IN (1, 2) AND (space_id IN ('1','2')) GROUP BY listings.id HAVING count(*) >= 2
Rendered scripts/_map.html.erb (2.9ms)
Rendered scripts/_shuffle.html.erb (0.3ms)
Rendered listings/search.html.erb within layouts/application (178.7ms)
Rendered layouts/_head.html.erb (475.7ms)
Rendered scripts/_address_autocomplete.html.erb (0.3ms)
Rendered listings/_search_address.html.erb (13.7ms)
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 3]]
(0.5ms) SELECT DISTINCT "conversations"."id" FROM "conversations" WHERE (sender_id = 3 OR recipient_id = 3)
(0.5ms) SELECT DISTINCT "messages"."conversation_id" FROM "messages" WHERE ("messages"."user_id" != $1) AND "messages"."read" = $2 [["user_id", 3], ["read", "false"]]
CACHE (0.0ms) SELECT "avatars".* FROM "avatars" WHERE "avatars"."user_id" = $1 ORDER BY "avatars"."id" ASC LIMIT 1 [["user_id", 3]]
Rendered layouts/_navbar.html.erb (32.5ms)
Rendered scripts/_fade_error.html.erb (0.4ms)
Rendered scripts/_transparent_navbar.html.erb (0.3ms)
Completed 200 OK in 1045ms (Views: 688.6ms | ActiveRecord: 30.6ms)
I have also tried adding raise 'test' in order to do some testing in the better_errors live shell. I discovered:
>> #listings
=> #<ActiveRecord::Relation []>
>> #listings = #listings.joins(:spaces).where('space_id IN (?)', params[:search][:space_ids].reject(&:blank?)).uniq
=> #<ActiveRecord::Relation [#<Listing id: 1, title: "Test 1", address: "New Inn Passage, London WC2A 2AE, UK", latitude: 51.5139664, longitude: -0.1167323, size: 1000, min_lease: 1, price: #<BigDecimal:7f89ec245c98,'0.1E4',9(18)>, description: "Test 1", user_id: 3, state: "public", created_at: "2015-10-30 17:37:04", updated_at: "2015-10-30 17:37:04">]>
>>
Why is this happening and how can I fix it?
Any help would be greatly appreciated.
The issue is with how you are determining that all of the amenities have been matched.
When you are only joining the amenities then the count of the rows (prior to grouping) for a listing is the number of matched amenities, so the having clause does what you want.
When you join the spaces table too, then the number of rows (again prior to grouping) for a listing is the number of matches amenities times the number of matched rows. In your example there are 2 spaces and 1 amenity, so the count is 2 and your having clause is satisfied.
If instead of filtering on count(*) you filtered on count(distinct amenities.id) then you should be counting the number of amenity rows that were joined, which should produce the desired result.
I may have figured out the issue. I did the following in the console to test:
Set #listings = Listing.all.
Set #listings = #listings.joins(:amenities).where(amenities: { id: ['1', '2'].reject(&:blank?) }).group('listings.id').having('count(*) >= ?', ['1', '2'].reject(&:blank?).size).
This produces: => #<ActiveRecord::Relation []>, as desired.
I then checked to see what would happen if I were to do: #listings.joins(:spaces).
This produces: => #<ActiveRecord::Relation [#<Listing id: 1, title: "Test 1", address: "New Inn Passage, London WC2A 2AE, UK", latitude: 51.5139664, longitude: -0.1167323, size: 1000, min_lease: 1, price: #<BigDecimal:7ffcb02ce890,'0.1E4',9(18)>, description: "Test 1", user_id: 3, state: "public", created_at: "2015-10-30 17:37:04", updated_at: "2015-10-30 17:37:04">]>, even though #listings was initially [].
So the problem has to do with the joins(:spaces) in the second filter.
In order to make sure that #listings remains [] in the event that that is the result of the first filter, I added the extra condition && #listings.present? to the second filter, yielding:
if params[:search][:space_ids].present? && params[:search][:space_ids].reject(&:blank?).size > 0 && #listings.present?
That extra condition prevents the second filter from being executed and returning results that should not be returned.
This feels like an ugly hack, and I would welcome better solutions, but it seems to work.

has_many/belongs_to relationships & query questions

I am building an attendance tracker right now. I have three models currently. I am using devise for my User model
class User < ActiveRecord::Base
has_many :clients
has_many :attendances
class Client < ActiveRecord::Base
belongs_to :user
has_many :attendances
class Attendance < ActiveRecord::Base
belongs_to :user
belongs_to :client
The columns on the attendance table are user_id client_id created_at and updated_at
Here's my thought process:
I can get all of the attendances for each User
I can also get all of the attendances for a specific Client
I can get all of the attendance records for a specific User and Client by way of: Attendance.joins(:user, :client).where(:user_id => current_user) which returns an
<ActiveRecord::Relation [#<Attendance id: 18, client_id: 151, created_at: "2015-07-24 21:36:16", updated_at: "2015-07-24 21:36:16", user_id: 4>, #<Attendance id: 19, client_id: 101, created_at: "2015-07-24 21:37:10", updated_at: "2015-07-24 21:37:10", user_id: 4>, #<Attendance id: 20, client_id: 114, created_at: "2015-07-24 21:37:39", updated_at: "2015-07-24 21:37:39", user_id: 4>, #<Attendance id: 21, client_id: 123, created_at: "2015-07-24 21:38:26", updated_at: "2015-07-24 21:38:26", user_id: 4>]>
Can I somehow refer back to Client table to get information like first_name or email with another where, include, or joins statement?
Or am I missing something altogether and maybe need a join table and do a has_many, through: relationship?
Brad's comment above is technically correct, but since the .joins clause on your query only includes those tables for the execution of the actual query, the data from the Users and Clients tables isn't actually going to get loaded in by Rails, so in order to fetch all their data you'll end up executing N+1 queries (a common cause of slow Rails apps!). That is:
irb> User.joins(:groups).each { |u| u.groups.map(&:name) }
User Load (8.6ms) SELECT "users".* FROM "users" INNER JOIN "groups" ON "groups"."user_id" = "users"."id"
Group Load (1.2ms) SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1 [["user_id", 1]]
Group Load (0.6ms) SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1 [["user_id", 1]]
Group Load (0.7ms) SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1 [["user_id", 1]]
Group Load (0.7ms) SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1 [["user_id", 2]]
Group Load (0.6ms) SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1 [["user_id", 3]]
Not so bad now, but imagine if you had a thousand users! We can fix this, though. If you use the .includes method, you'll both join onto the other table(s) and load their data into memory. It still runs 2 queries, but that's an improvement:
irb(main):016:0> User.includes(:groups).each { |u| u.groups.map(&:name) }
User Load (0.6ms) SELECT "users".* FROM "users"
Group Load (1.2ms) SELECT "groups".* FROM "groups" WHERE "groups"."user_id" IN (1, 2, 3, 4)
So, for your case, try this instead:
Attendance.includes(:user, :client).where(user_id: current_user)
For more info on the difference between includes and joins, see this blog post, or Google "rails joins vs includes".

Active Record "Where" Query Failing to find Record

So have a token model that has keys which are strings. I am doing a search for a token that exists and when i do a direct comparrison of the string with the record it should find i get an empty active record relation object.
Anyone know what is going on here? Am i stepping on a models toes by using the Model name 'Token'. I didn't find anything in the googles about it.
I've stored the key as 'a' below and Token.last.key is the database entry that has the matching key.
irb(main):023:0> a
=> "279684d7488254c41bb4039ad0962007"
irb(main):024:0> Token.last.key
Token Load (0.4ms) SELECT "tokens".* FROM "tokens" ORDER BY "tokens"."id" DESC LIMIT 1
=> "279684d7488254c41bb4039ad0962007"
irb(main):025:0> a == Token.last.key
Token Load (0.1ms) SELECT "tokens".* FROM "tokens" ORDER BY "tokens"."id" DESC LIMIT 1
=> true
irb(main):026:0> Token.where(key: a)
Token Load (0.2ms) SELECT "tokens".* FROM "tokens" WHERE "tokens"."key" = '279684d7488254c41bb4039ad0962007'
=> #<ActiveRecord::Relation []>
irb(main):027:0> Token.where(key: a.to_s)
Token Load (0.2ms) SELECT "tokens".* FROM "tokens" WHERE "tokens"."key" = '279684d7488254c41bb4039ad0962007'
=> #<ActiveRecord::Relation []>
Oddly enough, when i do a search through all the records i do return the correct user
#DOES WORK
def current_user(key)
Token.all.first {|t| return t.key == key }
end
What i have will work but what i would like it to do this in the database with something like this
#DOES NOT WORK
def current_user(key)
Token.where(key: key).try(:user)
end
Doing search based on suggestion below returning nil for find_by
irb(main):004:0> a = Token.last.key
Token Load (0.2ms) SELECT "tokens".* FROM "tokens" ORDER BY "tokens"."id" DESC LIMIT 1
=> "279684d7488254c41bb4039ad0962007"
irb(main):005:0> Token.find_by(key: a)
Token Load (0.2ms) SELECT "tokens".* FROM "tokens" WHERE "tokens"."key" = '279684d7488254c41bb4039ad0962007' LIMIT 1
=> nil
Solution was to do this
def find_user_by_api(key)
tokens = Token.arel_table
Token.where(tokens[:key].matches(key)).try(:first).try(:user)
end
Wish it helps
Token.find_by(key: a)

Using pp (Pretty Print) in a method before save allows my model to save successfully, it fails otherwise

I have an object that looks like this:
It is used to allows users with resumes to 'apply to' (job)listings.
# == Schema Information
#
# Table name: jobapps
#
# id :integer not null, primary key
# denied :boolean
# listing_id :integer
# resume_id :integer
# created_at :datetime
# updated_at :datetime
#
class Jobapp < ActiveRecord::Base
belongs_to :listing
belongs_to :resume
validates_uniqueness_of :listing_id, scope: :resume_id
before_save :default_values
. . .
private
def default_values
if self.denied.nil?
self.denied = false
end
end
end
Whenever I create a new Jobapp object, the job always fails.
If I change the default_values method on the Jobapps model to:
def default_values
if denied.nil?
self.denied = false
pp self
end
end
The object is created and saved.
Console Output:
When I attempt to have a Resume object apply_to a Listing object, which should create a Jobapp object:
Started POST "/listings/1/apply?resume=8" for 127.0.0.1 at 2014-07-31 18:43:33 -0500
Processing by ListingsController#apply as HTML
Parameters: {"authenticity_token"=>"htbHjaZnkqRMfgSUAQA5zOXB3ax/MAaHRUx8jRWr/lw=", "resume"=>"8", "id"=>"1"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Listing Load (0.2ms) SELECT "listings".* FROM "listings" WHERE "listings"."id" = $1 LIMIT 1 [["id", "1"]]
(0.2ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
Resume Load (0.2ms) SELECT "resumes".* FROM "resumes" WHERE "resumes"."id" = $1 LIMIT 1 [["id", "8"]]
(0.2ms) BEGIN
Jobapp Exists (0.2ms) SELECT 1 AS one FROM "jobapps" WHERE ("jobapps"."listing_id" = 1 AND "jobapps"."resume_id" = 8) LIMIT 1
(0.1ms) ROLLBACK
Redirected to http://0.0.0.0:3000/listings/1
Completed 302 Found in 102ms (ActiveRecord: 1.5ms)
When I have the pp self line in the default_values method:
Started POST "/listings/1/apply?resume=8" for 127.0.0.1 at 2014-07-31 18:41:36 -0500
Processing by ListingsController#apply as HTML
Parameters: {"authenticity_token"=>"htbHjaZnkqRMfgSUAQA5zOXB3ax/MAaHRUx8jRWr/lw=", "resume"=>"8", "id"=>"1"}
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Listing Load (0.2ms) SELECT "listings".* FROM "listings" WHERE "listings"."id" = $1 LIMIT 1 [["id", "1"]]
(2.3ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
Resume Load (0.3ms) SELECT "resumes".* FROM "resumes" WHERE "resumes"."id" = $1 LIMIT 1 [["id", "8"]]
(0.1ms) BEGIN
Jobapp Exists (0.2ms) SELECT 1 AS one FROM "jobapps" WHERE ("jobapps"."listing_id" = 1 AND "jobapps"."resume_id" = 8) LIMIT 1
"here it is"
#<Jobapp id: nil, denied: false, listing_id: 1, resume_id: 8, created_at: nil, updated_at: nil>
SQL (0.3ms) INSERT INTO "jobapps" ("created_at", "denied", "listing_id", "resume_id", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Thu, 31 Jul 2014 23:41:36 UTC +00:00], ["denied", false], ["listing_id", 1], ["resume_id", 8], ["updated_at", Thu, 31 Jul 2014 23:41:36 UTC +00:00]]
(0.5ms) COMMIT
Redirected to http://0.0.0.0:3000/listings/1
Completed 302 Found in 120ms (ActiveRecord: 4.9ms)
Some referenced methods:
Here is the apply method from the controller:
def apply
set_resume
if #resume.apply_to(#listing)
redirect_to #listing
else
redirect_to #listing, alert: 'Unable to apply for job'
end
end
Here is the Resume.apply_to method:
def apply_to(listing)
jobapp = Jobapp.new(resume: self, listing: listing)
jobapp.save
end
Notes:
It smells like I'm doing something weird with the conditional apply method (which is annoying) but I don't think that's the reason for the failure (it fails if I take out the odd conditional and simplify the Resume.apply_to method to simply create the object rather than using new then save.
Pretty Print ruby doc
From what I can tell, this is because self.denied = false returns false, and so your callback is returning false, which will cancel all future callbacks and associated action:
If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last.
http://api.rubyonrails.org/v4.1.1/classes/ActiveRecord/Callbacks.html
Just make sure your default_values method returns true.
From the ActiveRecord Callbacks documentation:
If a before_* callback returns false, all the later callbacks and the associated action are cancelled.
And in your callback you have:
if self.denied.nil?
self.denied = false
end
So if you keep in mind that all blocks (including if blocks, methods, begin/end blocks, etc.) return the last value in the block... then you're affectively returning false from your callback. That is:
self.denied = false sets self.denied to false an then returns false
The if block surrounding this returns the last value in the block, or false
The method returns the last value in the method (block), or false
Therefore, your callback cancels the save operation.
Furthermore, when you introduced the pp self statement, then that will effectively output self and then return self, which is not false (obviously).
So to fix the non pp self version, I would recommend just returning true at the end since you're most likely not looking to abort the save after evaluating the contents of your callback method. Here it is, slightly simplifed using the idiomatic ||= operator.
def default_values
self.denied ||= false
true
end
The use of ||= above is the same as: self.denied || self.denied = false.

Resources