has_many :through not loading records - ruby-on-rails

I've a Rails 5.2.1 app where each step of a relationship works, but the has_many :through version doesn't. The setup is a little strange, but I feel like I've set everything up correctly, so I'm a little stumped.
Given this code:
class Contact < SalesforceModel
self.table_name = 'salesforce.contact'
self.primary_key = 'sfid'
has_many :content_accesses, foreign_key: 'contact__c', class_name: 'ContentAccess'
has_many :concepts, through: :content_accesses, source: :inventory
end
class ContentAccess < ApplicationRecord
self.table_name = 'salesforce.content_access__c'
self.primary_key = 'sfid'
belongs_to :inventory, foreign_key: 'inventory__c', inverse_of: :content_accesses, primary_key: 'sfid', class_name: 'Inventory'
belongs_to :contact, foreign_key: 'contact__c', inverse_of: : content_accesses, primary_key: 'sfid', class_name: 'Contact'
end
class Inventory < SalesforceModel
self.table_name = 'salesforce.inventory__c'
self.primary_key = 'sfid'
has_many :content_accesses, foreign_key: 'inventory__c'
has_many :contacts, through: :content_accesses
end
Each step of the has_many :through works:
# Setup
2.5.1 :001 > contact = Contact.first
Contact Load (30.6ms) SELECT "salesforce"."contact".* FROM "salesforce"."contact" ORDER BY "salesforce"."contact"."sfid" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Contact lastname: "Doe", mailingpostalcode: "90210", name: "John Doe", mobilephone: nil, birthdate: nil, phone: nil, mailingstreet: "123 ABC Street", isdeleted: false, systemmodstamp: "2018-03-16 00:09:01", mailingstatecode: "CA", createddate: "2018-03-15 17:50:44", mailingcity: "LA", mailingcountrycode: "US", firstname: "John", email: "john.doe#realisp.com", sfid: "003m000000txXhwAAE", id: "003m000000txXhwAAE", _hc_lastop: "SYNCED", _hc_err: nil>
# Accessing related ContentAccess works
2.5.1 :002 > contact.content_accesses.count
(2.0ms) SELECT COUNT(*) FROM "salesforce"."content_access__c" WHERE "salesforce"."content_access__c"."contact__c" = $1 [["contact__c", "003m000000txXhwAAE"]]
=> 2
# Accessing related Inventory, through the related ContentAccess works
2.5.1 :003 > contact.content_accesses.first.inventory
ContentAccess Load (0.6ms) SELECT "salesforce"."content_access__c".* FROM "salesforce"."content_access__c" WHERE "salesforce"."content_access__c"."contact__c" = $1 ORDER BY "salesforce"."content_access__c"."sfid" ASC LIMIT $2 [["contact__c", "003m000000txXhwAAE"], ["LIMIT", 1]]
Inventory Load (30.4ms) SELECT "salesforce"."inventory__c".* FROM "salesforce"."inventory__c" WHERE "salesforce"."inventory__c"."sfid" = $1 LIMIT $2 [["sfid", "a1mm0000001S9qzAAC"], ["LIMIT", 1]]
=> #<Inventory createddate: "2018-05-23 15:09:41", isdeleted: false, name: "Some Concept Name", systemmodstamp: "2018-05-23 15:09:42", sfid: "a1mm0000001S9qzAAC", id: "a1mm0000001S9qzAAC", _hc_lastop: "SYNCED", _hc_err: nil>
# Accessing the related inventory through the has_many :through does not work
2.5.1 :004 > contact.concepts.count
(33.0ms) SELECT COUNT(*) FROM "salesforce"."inventory__c" INNER JOIN "salesforce"."content_access__c" ON "salesforce"."inventory__c"."sfid" = "salesforce"."content_access__c"."inventory__c" WHERE "salesforce"."content_access__c"."contact__c" = $1 [["contact__c", "003m000000txXhwAAE"]]
=> 0
Running the generated query in Postgres works, though:
app_development=# SELECT COUNT(*) FROM "salesforce"."inventory__c" INNER JOIN "salesforce"."content_access__c" ON "salesforce"."inventory__c"."sfid" = "salesforce"."content_access__c"."inventory__c" WHERE "salesforce"."content_access__c"."contact__c" = '003m000000txXhwAAE';
count
-------
2
(1 row)
Running Contact.first.concepts.to_sql produces:
SELECT "salesforce"."inventory__c".* FROM "salesforce"."inventory__c" INNER JOIN "salesforce"."content_access__c" ON "salesforce"."inventory__c"."sfid" = "salesforce"."content_access__c"."inventory__c" WHERE "salesforce"."content_access__c"."contact__c" = '003m000000txXhwAAE'
Running that query through psql works fine, returning the proper records from the inventory__c table.
The reverse also has the same problem:
2.5.1 :002 > inventory = Inventory.first
Inventory Load (30.2ms) SELECT "salesforce"."inventory__c".* FROM "salesforce"."inventory__c" ORDER BY "salesforce"."inventory__c"."sfid" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Inventory createddate: "2018-05-23 15:09:41", isdeleted: false, name: "Positive Focus", systemmodstamp: "2018-05-23 15:09:42", inventory_unique_name__c: "Inventory 1", sfid: "a1mm0000001S9qzAAC", id: "a1mm0000001S9qzAAC", _hc_lastop: "SYNCED", _hc_err: nil>
2.5.1 :003 > inventory.content_accesses.first.contact
ContentAccess Load (0.9ms) SELECT "salesforce"."content_access__c".* FROM "salesforce"."content_access__c" WHERE "salesforce"."content_access__c"."inventory__c" = $1 ORDER BY "salesforce"."content_access__c"."sfid" ASC LIMIT $2 [["inventory__c", "a1mm0000001S9qzAAC"], ["LIMIT", 1]]
Contact Load (30.9ms) SELECT "salesforce"."contact".* FROM "salesforce"."contact" WHERE "salesforce"."contact"."sfid" = $1 LIMIT $2 [["sfid", "003m000000txXhwAAE"], ["LIMIT", 1]]
=> #<Contact sfid: "003m000000txXhwAAE", id: "003m000000txXhwAAE", [...etc...] >
2.5.1 :004 > inventory.contacts.count
(30.7ms) SELECT COUNT(*) FROM "salesforce"."contact" INNER JOIN "salesforce"."content_access__c" ON "salesforce"."contact"."sfid" = "salesforce"."content_access__c"."contact__c" WHERE "salesforce"."content_access__c"."inventory__c" = $1 [["inventory__c", "a1mm0000001S9qzAAC"]]
=> 0
So: everything seems to be hooked up correctly, so why isn't the through version working? Any help would be appreciated.
Thanks! ❤️

Thanks to #Zabba's comment asking about SalesforceModel, I was able to track down the problem. Fundamentally, it was this:
class ContentAccess < ApplicationRecord
It should have been:
class ContentAccess < SalesforceModel # <-- it was using the wrong table, effectively.
This has been a harrowing week, but it's better now. 😀

Related

postgres - distinct order_by a jsonb key throws a rails PG::InvalidColumnReference

My user model has the following definition for the follower association:
has_many :passive_follow_actions, -> { where("actions.activity_verb = 'follow'").uniq }, class_name: 'Action', foreign_key: :activity_object_id
has_many :followers, through: :passive_follow_actions, source: :activity_actor, source_type: 'User'
#
User table has a jsonb field called follow_stats which I use to order the follower results.
On its own, a distinct operation works
has_many :followers, -> { distinct }, through: :passive_follow_actions, source: :activity_actor, source_type: 'User'
# SELECT DISTINCT "users".* FROM "users" INNER JOIN "actions" ON "users"."id" = "actions"."activity_actor_id" WHERE "actions"."activity_object_id" = 1 AND (actions.activity_verb = 'follow') AND "actions"."activity_actor_type" = 'User'
#
Also on its own, an order operation works
has_many :followers, -> { order("follow_stats->'followers_count' DESC") }, through: :passive_follow_actions, source: :activity_actor, source_type: 'User'
# SELECT "users".* FROM "users" INNER JOIN "actions" ON "users"."id" = "actions"."activity_actor_id" WHERE "actions"."activity_object_id" = 1 AND (actions.activity_verb = 'follow') AND "actions"."activity_actor_type" = 'User' ORDER BY follow_stats->'followers_count' DESC
#
However, put together,
has_many :followers, -> { distinct.order("follow_stats->'followers_count' DESC") }, through: :passive_follow_actions, source: :activity_actor, source_type: 'User'
# SELECT DISTINCT "users".* FROM "users" INNER JOIN "actions" ON "users"."id" = "actions"."activity_actor_id" WHERE "actions"."activity_object_id" = 1 AND (actions.activity_verb = 'follow') AND "actions"."activity_actor_type" = 'User' ORDER BY follow_stats->'followers_count' DESC
#
it throws the following exception:
PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
#
I tried adding the jsonb query onto the select clause to prevent the error, but that results in an empty response:
has_many :followers, -> { select("follow_stats->'followers_count'").uniq.order("follow_stats->'followers_count' DESC") }, through: :passive_follow_actions, source: :activity_actor, source_type: 'User'
# SELECT DISTINCT follow_stats->'followers_count' FROM "users" INNER JOIN "actions" ON "users"."id" = "actions"."activity_actor_id" WHERE "actions"."activity_object_id" = 1 AND (actions.activity_verb = 'follow') AND "actions"."activity_actor_type" = 'User' ORDER BY follow_stats->'followers_count' DESC
#
Response:
[#<User id: nil, ?column?: 42>, #<User id: nil, ?column?: 4>, #<User id: nil, ?column?: 2>, #<User id: nil, ?column?: 1>, #<User id: nil, ?column?: 0>]
#
When I add a * in the select clause, it returns the same exception PG::InvalidColumnReference
has_many :followers, -> { select("*, follow_stats->'followers_count'").uniq.order("follow_stats->'followers_count' DESC") }, through: :passive_follow_actions, source: :activity_actor, source_type: 'User'
#SELECT DISTINCT *, follow_stats->'followers_count' FROM \"users\" INNER JOIN \"actions\" ON \"users\".\"id\" = \"actions\".\"activity_actor_id\" WHERE \"actions\".\"activity_object_id\" = 1 AND (actions.activity_verb = 'follow') AND \"actions\".\"activity_actor_type\" = 'User' ORDER BY follow_stats->'followers_count' DESC
#
How select distinct and order by a jsonb key at the same time?

prevent different user to see a model through the show method

In my index method for every model I make sure to query starting with current user: current_user.model.all, in order to only show models that belong to the current user.
My show method for all models is quite simple and standard, without current_user.
def show
#logica = Logica.find params[:id]
authorize #logica
end
This does open the chance of a user entering a random id in the url and see the model from a different user. What is the best way to prevent this from happening?
The has_many association has a number of different methods available including find
current_user.model.find(params[:id])
which is similar to
Model.where(user_id: current_user.id).find(params[:id])
If you have to go through several models in order to reach your User model, for instance
class User < ApplicationRecord
has_one :test_one
end
class TestOne < ApplicationRecord
has_one :test_three
belongs_to :user
end
class TestThree < ApplicationRecord
has_many :test_fours
belongs_to :test_one
end
class TestFour < ApplicationRecord
belongs_to :test_three
end
you can set it up in a single query by doing something like
TestFour.joins(:test_three => { :test_one => :user }).where(test_threes: { test_ones: { users: { id: current_user.id}}}).find(1)
# TestFour Load (1.2ms) SELECT "test_fours".* FROM "test_fours" INNER JOIN "test_threes" ON "test_threes"."id" = "test_fours"."test_three_id" INNER JOIN "test_ones" ON "test_ones"."id" = "test_threes"."test_one_id" INNER JOIN "users" ON "users"."id" = "test_ones"."user_id" WHERE "users"."id" = $1 AND "test_fours"."id" = $2 LIMIT $3 [["id", 1], ["id", 1], ["LIMIT", 1]]
#=> #<TestFour id: 1, test_three_id: 1, created_at: "2017-07-12 21:06:51", updated_at: "2017-07-12 21:06:51">
and then if you did this with a user id/test four id that don't match:
TestFour.joins(:test_three => { :test_one => :user }).where(test_threes: { test_ones: { users: { id: current_user.id + 1}}}).find(1)
# TestFour Load (0.8ms) SELECT "test_fours".* FROM "test_fours" INNER JOIN "test_threes" ON "test_threes"."id" = "test_fours"."test_three_id" INNER JOIN "test_ones" ON "test_ones"."id" = "test_threes"."test_one_id" INNER JOIN "users" ON "users"."id" = "test_ones"."user_id" WHERE "users"."id" = $1 AND "test_fours"."id" = $2 LIMIT $3 [["id", 2], ["id", 1], ["LIMIT", 1]]
#=> ActiveRecord::RecordNotFound: Couldn't find TestFour with 'id'=1 [WHERE "users"."id" = $1]

Rails 4: can't update polymorphic nested resource through form

Just in the final stages of transferring an app stuck in Rails 2 on a shared server to Rails 4 on a VPS, but stuck on one thing.
I have an Image model that uses Carrierwave to upload and display attached images. The Image model has a polymorphic association to a couple of other models. The host and associated models are updated together on a combined multipart form.
The form works perfectly well for creating and editing the primary model and for creating and attaching new images, but the image attributes can't be edited, and no error is thrown to help.
Here are the relevant parts of my Image model:
class Image < ActiveRecord::Base
belongs_to :imagings, polymorphic: true, touch: true
mount_uploader :image, ImageUploader, mount_on: :image_file_name
end
And my Review model:
class Review < ActiveRecord::Base
has_many :images, as: :imagings, dependent: :destroy
accepts_nested_attributes_for :images, reject_if: lambda { |t| t['image'].nil? }, allow_destroy: true
end
And my Reviews Controller:
class ReviewsController < ApplicationController
def edit
#review = Review.find(params[:id])
3.times {#review.images.build}
end
def update
#review = Review.find(params[:id])
if #review.update(reviews_params)
redirect_to #review, notice: "Successfully updated review."
else
render :edit
end
end
private
def reviews_params
params.require(:review).permit(:title, :authors, :venue, :startdate, :enddate, :body, :approved, :company, :user_id, { images_attributes: [:title, :credits, :image, :_destroy, :id] })
end
end
I can add images to existing reviews or to a new review perfectly well with this setup, but if I try to change text fields for an existing image, the parameters show in the console but they are ignored:
Started PATCH "/reviews/492" for 127.0.0.1 at 2014-07-16 11:03:42 +0100
Processing by ReviewsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"HvrsCAXXvO4zIqTBw05qSUwU9I2241DXyilhswEbU9o=", "review"=>{"title"=>"Here's a new review", "authors"=>"", "company"=>"", "venue"=>"", "startdate"=>"2014-07-16", "enddate"=>"2014-07-31", "body"=>"<p>Something</p>", "images_attributes"=>{"0"=>{"title"=>"Doggie", "credits"=>"Icons galore", "_destroy"=>"0", "id"=>"1239"}, "1"=>{"title"=>"", "credits"=>""}, "2"=>{"title"=>"", "credits"=>""}, "3"=>{"title"=>"", "credits"=>""}}, "user_id"=>"4", "approved"=>"1"}, "commit"=>"Update Review", "id"=>"492"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
(12.4ms) BEGIN
Image Load (0.4ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 AND "images"."id" IN (1239) [["imagings_id", 492], ["imagings_type", "Review"]]
(0.2ms) COMMIT
Redirected to http://127.0.0.1:3000/reviews/492
Completed 302 Found in 27ms (ActiveRecord: 13.6ms)
You can see here how the images attribute for credit has the change to "Icons galore", but the SQL only fetches the linked image without updating it. Contrast this with the following update to the main model which does a select followed by an update between the BEGIN and COMMIT.
Started PATCH "/reviews/492" for 127.0.0.1 at 2014-07-16 11:26:01 +0100
Processing by ReviewsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"HvrsCAXXvO4zIqTBw05qSUwU9I2241DXyilhswEbU9o=", "review"=>{"title"=>"Here's a new review", "authors"=>"", "company"=>"", "venue"=>"", "startdate"=>"2014-07-16", "enddate"=>"2014-07-31", "body"=>"<p>Something else</p>", "images_attributes"=>{"0"=>{"title"=>"Doggie", "credits"=>"Icons", "_destroy"=>"0", "id"=>"1239"}, "1"=>{"title"=>"", "credits"=>""}, "2"=>{"title"=>"", "credits"=>""}, "3"=>{"title"=>"", "credits"=>""}}, "user_id"=>"4", "approved"=>"1"}, "commit"=>"Update Review", "id"=>"492"}
User Load (6.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
(18.1ms) BEGIN
Image Load (0.5ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 AND "images"."id" IN (1239) [["imagings_id", 492], ["imagings_type", "Review"]]
SQL (5.9ms) UPDATE "reviews" SET "body" = $1, "updated_at" = $2 WHERE "reviews"."id" = 492 [["body", "<p>Something else</p>"], ["updated_at", "2014-07-16 10:26:01.455481"]]
(24.8ms) COMMIT
Redirected to http://127.0.0.1:3000/reviews/492
Completed 302 Found in 120ms (ActiveRecord: 56.0ms)
Yet if I try to make the same update in the Rails console, it sometimes works perfectly:
2.1.1 :062 > review = Review.find(492)
Review Load (0.5ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
=> #<Review id: 492, title: "Here's a new review", authors: "", venue: "", startdate: "2014-07-16", enddate: "2014-07-31", body: "<p>Something else</p>", approved: true, created_at: "2014-07-16 09:38:33", updated_at: "2014-07-16 10:26:01", user_id: 4, company: "">
2.1.1 :064 > review.images.first.title = "Doggies galore"
=> "Doggies galore"
2.1.1 :066 > review.save
(0.8ms) BEGIN
SQL (7.5ms) UPDATE "images" SET "title" = $1, "updated_at" = $2 WHERE "images"."id" = 1239 [["title", "Doggies galore"], ["updated_at", "2014-07-16 10:32:39.109784"]]
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
SQL (1.7ms) UPDATE "reviews" SET "updated_at" = '2014-07-16 10:32:39.129314' WHERE "reviews"."id" = 492
(1.6ms) COMMIT
=> true
And sometimes doesn't:
2.1.1 :082 > review = Review.find(492)
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
=> #<Review id: 492, title: "Here's a new review", authors: "", venue: "", startdate: "2014-07-16", enddate: "2014-07-31", body: "<p>Something else</p>", approved: true, created_at: "2014-07-16 09:38:33", updated_at: "2014-07-16 10:38:30", user_id: 4, company: "">
2.1.1 :083 > review.images.first.credits = "Icons galore"
Image Load (0.5ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 ORDER BY "images"."id" ASC LIMIT 1 [["imagings_id", 492], ["imagings_type", "Review"]]
=> "Icons galore"
2.1.1 :084 > review.save!
(0.2ms) BEGIN
(0.3ms) COMMIT
=> true
2.1.1 :085 > review.images.first
Image Load (0.8ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 ORDER BY "images"."id" ASC LIMIT 1 [["imagings_id", 492], ["imagings_type", "Review"]]
=> #<Image id: 1239, title: "Doggies", credits: "Icons", created_at: "2014-07-16 09:38:33", updated_at: "2014-07-16 10:38:30", image_file_name: "ddad7b05-3430-49ed-b129-fd484ac793ad.gif", image_content_type: nil, image_file_size: nil, image_updated_at: nil, imagings_id: 492, imagings_type: "Review", original_filename: "Doggie.gif">
2.1.1 :086 >
I know I'm missing something obvious. Can anyone help please?
I think that you need to make a small change in reviews_params def as follows:
def reviews_params
params.require(:review).permit(:title, :authors, :venue, :startdate, :enddate, :body, :approved, :company, :user_id, images_attributes => [:title, :credits, :image, :_destroy, :id] )
end
Does that work?
i don't think you need the curly braces. Try removing them. Usually nested attributes are listed as a symbol which maps to an array of symbols(attributes). They are separated from attributes of the model in question with just a comma. So something such as,
params.require(:review).permit(:title, :authors, :venue, :startdate, :enddate, :body, :approved, :company, :user_id, :images_attributes => [:title, :credits, :image, :_destroy, :id] ) should work.

Rails 4 ActsAsTenant complex has_many through doesn't work with setted current_tenant

I have these models in my multi-tenant app:
class Tenant < ActiveRecord::Base
has_many :userroles
has_many :users, through: :userroles
has_many :roles, through: :userroles
has_many :admins, -> { joins(:roles).where("roles.name = 'admin'").uniq }, through: :userroles, class_name: 'User', source: :user
end
class Role < ActiveRecord::Base
has_paper_trail
acts_as_paranoid
has_many :userroles, :dependent => :destroy
has_many :users, :through => :userroles
end
class Userrole < ActiveRecord::Base
acts_as_tenant(:tenant)
has_paper_trail
belongs_to :user
belongs_to :role
end
I use gem ActsAsTenant written by Erwin (source code at github). When current_tenant doesn't set my code work right, but if I set current_tenant, I got errors.
In console I got those errors:
2.1.0 :001 > ActsAsTenant.current_tenant
=> nil
2.1.0 :002 > t = Tenant.first
2.1.0 :004 > t.admins.count
(1.5ms) SELECT DISTINCT COUNT(DISTINCT "users"."id") FROM "users" INNER JOIN "userroles" "userroles_users_join" ON "userroles_users_join"."user_id" = "users"."id" INNER JOIN "roles" ON "roles"."id" = "userroles_users_join"."role_id" AND "roles"."deleted_at" IS NULL INNER JOIN "userroles" ON "users"."id" = "userroles"."user_id" WHERE "users"."deleted_at" IS NULL AND "userroles"."tenant_id" = $1 AND (roles.name = 'admin') [["tenant_id", 1]]
=> 1
2.1.0 :005 > ActsAsTenant.current_tenant = t
2.1.0 :006 > t.admins.count
(2.6ms) SELECT DISTINCT COUNT(DISTINCT "users"."id") FROM "users" INNER JOIN "userroles" "userroles_users_join" ON "userroles_users_join"."user_id" = "users"."id" AND (userroles.tenant_id = 1) INNER JOIN "roles" ON "roles"."id" = "userroles_users_join"."role_id" AND "roles"."deleted_at" IS NULL INNER JOIN "userroles" ON "users"."id" = "userroles"."user_id" WHERE "users"."deleted_at" IS NULL AND "userroles"."tenant_id" = $1 AND (userroles.tenant_id = 1) AND (roles.name = 'admin') [["tenant_id", 1]]
PG::UndefinedTable: ERROR: invalid reference to FROM-clause entry for table "userroles"
LINE 1: ...erroles_users_join"."user_id" = "users"."id" AND (userroles....
^
HINT: Perhaps you meant to reference the table alias "userroles_users_join".
: SELECT DISTINCT COUNT(DISTINCT "users"."id") FROM "users" INNER JOIN "userroles" "userroles_users_join" ON "userroles_users_join"."user_id" = "users"."id" AND (userroles.tenant_id = 1) INNER JOIN "roles" ON "roles"."id" = "userroles_users_join"."role_id" AND "roles"."deleted_at" IS NULL INNER JOIN "userroles" ON "users"."id" = "userroles"."user_id" WHERE "users"."deleted_at" IS NULL AND "userroles"."tenant_id" = $1 AND (userroles.tenant_id = 1) AND (roles.name = 'admin')
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: invalid reference to FROM-clause entry for table "userroles"
Problem is when I set current_tenant. All works right before setting it. What could be the problem? In gem's code I can't find anything strange.
I change has_many condition:
has_many :admins, -> { for_tenanted_roles.where("roles.name = 'admin'").uniq }, through: :userroles, class_name: 'User', source: :user
And in user.rb
scope :for_tenanted_roles, -> { joins('INNER JOIN "roles" ON "roles"."id" = "userroles"."role_id" AND "roles"."deleted_at" IS NULL') }
It's just handy overrided joins(:roles)

Rails has_many through any? / empty?

I found an unusual situation on rails 3 app.
Consider the following model:
class Genre < ActiveRecord::Base
has_many :banner_genres, :dependent => :destroy
has_many :banners, :through => :banner_genres
...
class BannerGenre < ActiveRecord::Base
attr_accessible :banner_id, :banner, :genre_id, :genre, :position
belongs_to :genre
belongs_to :banner
...
class Banner < ActiveRecord::Base
has_many :banner_genres, :dependent => :destroy
has_many :genres, :through => :banner_genres
...
Now if I have a genre with a banner, I get the following lines in rails console:
1.9.3p362 :005 > g = Genre.find 62
Genre Load (0.8ms) SELECT "genres".* FROM "genres" WHERE "genres"."id" = $1 LIMIT 1 [["id", 62]]
=> #<Genre id: 62, ...
1.9.3p362 :006 > g.banner_genres.any?
(0.5ms) SELECT COUNT(*) FROM "banner_genres" WHERE "banner_genres"."genre_id" = 62
=> true
1.9.3p362 :007 > g.banners
Banner Load (1.0ms) SELECT "banners".* FROM "banners" INNER JOIN "banner_genres" ON "banners"."id" = "banner_genres"."banner_id" WHERE "banner_genres"."genre_id" = 62 ORDER BY position
=> [#<Banner id: 446, ...
1.9.3p362 :008 > g.banners.any?
=> false
Why does the .any? returns false? I thy with another has_many through association on the same project and it returns true.
Edit:
I had a typo when passing the code here. It's banner_genres on the has_many. Not genre_banners.
Also the inverse association works as it's supposed to:
1.9.3p362 :004 > b = Banner.find 446
Banner Load (1.1ms) SELECT "banners".* FROM "banners" WHERE "banners"."id" = $1 ORDER BY position LIMIT 1 [["id", 446]]
=> #<Banner id: 446...
1.9.3p362 :005 > b.genres.any?
(0.8ms) SELECT COUNT(*) FROM "genres" INNER JOIN "banner_genres" ON "genres"."id" = "banner_genres"."genre_id" WHERE "banner_genres"."banner_id" = 446
=> true
Edit 2
More strange console output:
1.9.3p362 :007 > g.banners.class
=> Array
1.9.3p362 :008 > g.banners.any?
=> false
1.9.3p362 :004 > g.banners.any? {|b| b}
=> true
1.9.3p362 :006 > g.banners.count
(0.9ms) SELECT COUNT(*) FROM "banners" INNER JOIN "banner_genres" ON "banners"."id" = "banner_genres"."banner_id" WHERE "banner_genres"."genre_id" = 62
=> 1
1.9.3p362 :009 > g.banners.to_a.any?
=> true
Edit 3
g.banner_genres
BannerGenre Load (0.7ms) SELECT "banner_genres".* FROM "banner_genres" WHERE "banner_genres"."genre_id" = 62
=> [#<BannerGenre id: 4, genre_id: 62, banner_id: 446, position: 1, created_at: "2013-03-15 16:41:10", updated_at: "2013-03-15 16:41:10">]
Edit 4 Asked by Aleks
could you please show queries that are shown on g.banners.any? and g.banners.
It's exactly the same query, which it's even stranger. I also didn't override the any? method anywhere.
1.9.3p362 :037 > g.banners(true)
Banner Load (1.0ms) SELECT "banners".* FROM "banners" INNER JOIN "banner_genres" ON "banners"."id" = "banner_genres"."banner_id" WHERE "banner_genres"."genre_id" = 62 ORDER BY position
=> [#<Banner id: 446, ...
1.9.3p362 :038 > g.banners(true).any?
Banner Load (1.2ms) SELECT "banners".* FROM "banners" INNER JOIN "banner_genres" ON "banners"."id" = "banner_genres"."banner_id" WHERE "banner_genres"."genre_id" = 62 ORDER BY position
=> false
1.9.3p362 :039 > g.banners.method(:any?)
=> #<Method: Array(Enumerable)#any?>
any? was supposed to return true....
I was so curious about this issue that I decided to simulate it.
g = Genre.find 1
Genre Load (30.1ms) SELECT "genres".* FROM "genres" WHERE "genres"."id" = $1 LIMIT 1 [["id", 1]]
=> #<Genre id: 1, name: "a", created_at: "2013-03-19 11:44:32", updated_at: "2013-03-19 11:44:32">
g.banner_genres.any?
(0.3ms) SELECT COUNT(*) FROM "banner_genres" WHERE "banner_genres"."genre_id" = 1
=> true
g.banners
Banner Load (0.5ms) SELECT "banners".* FROM "banners" INNER JOIN "banner_genres" ON "banners"."id" = "banner_genres"."banner_id" WHERE "banner_genres"."genre_id" = 1
=> [#<Banner id: 1, name: "1", created_at: "2013-03-19 11:43:00", updated_at: "2013-03-19 11:43:00">, #<Banner id: 2, name: "2", created_at: "2013-03-19 11:43:59", updated_at: "2013-03-19 11:43:59">]
g.banners.any?
=> true
Also,
g.banners.class
=> Array
g.banners.any?
=> true
g.banners.to_a.any?
=> true
There is something else going on in your code...
The real question here is not whether the association is good or bad, but why it is showing false for array that has elements in it.
It is clear that it is returning values, but the question is why it is returning false
The explanation might lay here: Unable to get Ruby's #any? to return false with list of nil objects
See how the any? is implemented, and how it relates to your question, it might give you a hint.
See this link as well : http://apidock.com/ruby/Enumerable/any%3F
EDIT:
What you are saying:
First you have an array of objects. Which will on any? return false
But if you do .to_a you will do something like this:
Time.new.to_a #=> [39, 54, 8, 9, 4, 2013, 3, 99, true, "CET"]
And that is why it will return true, as you will have object that are not null.
EDIT 2
Just to note: any? will return false if all objects from an array are nil or false

Resources