Solidus Paypal checkout fails in sandbox for non-PayPal payment variants - ruby-on-rails

I have built a webshop in Ruby on Rails using Solidus.
When I try to checkout in the PayPal sandbox with credit card or SEPA with a German address, it complains that the shop does not deliver there.
It works fine when I use PayPal payment with an account, also to a German address in Berlin...
This is the error in the ruby console:
Started GET "/solidus_paypal_commerce_platform/shipping_rates?order_id=R977809399&order_token=[FILTERED]&address%5Bcity%5D=Berlin&address%5Bstate%5D=&address%5Bcountry_code%5D=DE&address%5Bpostal_code%5D=10117" for 127.0.0.1 at 2022-09-29 20:29:49 +0200
Processing by SolidusPaypalCommercePlatform::ShippingRatesController#simulate_shipping_rates as */*
Parameters: {"order_id"=>"R977809399", "order_token"=>"[FILTERED]", "address"=>{"city"=>"Berlin", "state"=>"", "country_code"=>"DE", "postal_code"=>"10117"}}
Spree::User Load (0.1ms) SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."deleted_at" IS NULL AND "spree_users"."spree_api_key" = ? LIMIT ? [["spree_api_key", "undefined"], ["LIMIT", 1]]
Spree::Order Load (0.1ms) SELECT "spree_orders".* FROM "spree_orders" WHERE "spree_orders"."number" = ? LIMIT ? [["number", "R977809399"], ["LIMIT", 1]]
Spree::User Load (0.1ms) SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."deleted_at" IS NULL AND "spree_users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
CACHE Spree::Order Load (0.0ms) SELECT "spree_orders".* FROM "spree_orders" WHERE "spree_orders"."number" = ? LIMIT ? [["number", "R977809399"], ["LIMIT", 1]]
CACHE Spree::User Load (0.0ms) SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."deleted_at" IS NULL AND "spree_users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
TRANSACTION (0.0ms) begin transaction
Spree::Country Load (0.1ms) SELECT "spree_countries".* FROM "spree_countries" WHERE "spree_countries"."iso" = ? LIMIT ? [["iso", "DE"], ["LIMIT", 1]]
Spree::State Load (0.1ms) SELECT "spree_states".* FROM "spree_states" WHERE "spree_states"."country_id" = ? AND "spree_states"."abbr" = ? ORDER BY "spree_states"."name" ASC LIMIT ? [["country_id", 57], ["abbr", ""], ["LIMIT", 1]]
Spree::State Load (0.1ms) SELECT "spree_states".* FROM "spree_states" WHERE "spree_states"."country_id" = ? AND "spree_states"."name" = ? ORDER BY "spree_states"."name" ASC LIMIT ? [["country_id", 57], ["name", ""], ["LIMIT", 1]]
Spree::Store Load (0.0ms) SELECT "spree_stores".* FROM "spree_stores" WHERE "spree_stores"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Spree::Order Exists? (0.1ms) SELECT 1 AS one FROM "spree_orders" WHERE "spree_orders"."number" = ? AND "spree_orders"."id" != ? LIMIT ? [["number", "R977809399"], ["id", 10], ["LIMIT", 1]]
Spree::Adjustment Load (0.0ms) SELECT "spree_adjustments".* FROM "spree_adjustments" WHERE "spree_adjustments"."order_id" = ? AND "spree_adjustments"."source_type" = ? [["order_id", 10], ["source_type", "Spree::PromotionAction"]]
Spree::LineItem Load (0.0ms) SELECT "spree_line_items".* FROM "spree_line_items" WHERE "spree_line_items"."order_id" = ? ORDER BY "spree_line_items"."created_at" ASC, "spree_line_items"."id" ASC [["order_id", 10]]
Spree::Product Load (0.0ms) SELECT "spree_products".* FROM "spree_products" INNER JOIN "spree_variants" ON "spree_products"."id" = "spree_variants"."product_id" WHERE "spree_variants"."id" = ? LIMIT ? [["id", 45], ["LIMIT", 1]]
TRANSACTION (0.0ms) rollback transaction
Completed 422 Unprocessable Entity in 35ms (Views: 0.2ms | ActiveRecord: 0.8ms | Allocations: 31326)
Would be grateful for any pointers where to adjust that,
as I cannot make head or tail of it.

I don't know why Solidus is erroring internally, but I can tell you that if the rendered PayPal button has an onShippingChange callback yet that callback does not respond with success or times out (due to some error, generally), the PayPal checkout will give that message since you're indicating the address needs to be checked during payment approval yet are not responding that it's OK when queried.
Another possible flow design -- likely not used/offered by Solidus -- is to not specify onShippingChange and to check the address after approval (but before final order capture) and provide a mechanism to patch it when it's not a valid address you'll accept.

Related

Partially working simple image upload using Active Storage

Any help or guidance would be most appreciated.
I've followed the Rails Guide, however, and I can upload an image to a model. However, it produces an error.
I've copied what the error suggests and that produces another error and does not upload an image.
Many, many thanks in advance!
This works but produces an error post form submission. If I hit back and then visit the specific prod id product#show the image has uploaded.
Error
NameError in ProductsController#create
undefined local variable or method `product' for # Did you mean? #product
Products.rb
def create
#list = List.find(params[:list_id])
#product = #list.products.create(product_params)
product.hero.attach(params[:hero])
redirect_to list_path(#list)
end
This does not work however, looks syntactically correct as the "#product" model attaches to the :hero .
Products.rb
def create
#list = List.find(params[:list_id])
#product = #list.products.create(product_params)
#product.hero.attach(params[:hero])
redirect_to list_path(#list)
end
creates the product
refreshes the screen back to the product list so it looks correct.
This breaks producing two errors:
The first error on products#show
ArgumentError in Products#show
Showing /Users/user/rubyonrails/shopping/app/views/products/show.html.erb where line #2 raised:
Can't resolve image into URL: to_model delegated to attachment, but the attachment is nil
Second terminal output presents the upload seems to work then something called Active Storage Purge fires up removing it?
ActiveStorage::Blob Load (0.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 LIMIT $2 [["id", 7], ["LIMIT", 1]]
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Performing ActiveStorage::PurgeJob (Job ID: e9b35a62-ff6c-4a38-8946-3aa9c19668ef) from Async(active_storage_purge) enqueued at 2020-04-15T11:31:41Z with arguments: #<GlobalID:0x00007f92b1da3d60 #uri=#<URI::GID gid://shopping/ActiveStorage::Blob/7>>
[ActiveJob] [ActiveStorage::AnalyzeJob] [b724849f-6993-4130-bef1-a0f8837a3171] (6.4ms) COMMIT
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] (0.8ms) BEGIN
[ActiveJob] [ActiveStorage::AnalyzeJob] [b724849f-6993-4130-bef1-a0f8837a3171] Performed ActiveStorage::AnalyzeJob (Job ID: b724849f-6993-4130-bef1-a0f8837a3171) from Async(active_storage_analysis) in 15.88ms
Started GET "/lists/9" for ::1 at 2020-04-15 12:31:41 +0100
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] ActiveStorage::Attachment Exists? (0.5ms) SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = $1 LIMIT $2 [["blob_id", 7], ["LIMIT", 1]]
Processing by ListsController#show as HTML
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] ActiveStorage::Attachment Load (1.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4 [["record_id", 7], ["record_type", "ActiveStorage::Blob"], ["name", "preview_image"], ["LIMIT", 1]]
Parameters: {"id"=>"9"}
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] ActiveStorage::Blob Destroy (1.0ms) DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 7]]
List Load (0.9ms) SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]]
↳ app/controllers/lists_controller.rb:7:in `show'
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]]
↳ app/controllers/lists_controller.rb:8:in `show'
Rendering lists/show.html.erb within layouts/application
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/views/lists/show.html.erb:2
Product Exists? (0.2ms) SELECT 1 AS one FROM "products" WHERE "products"."list_id" = $1 LIMIT $2 [["list_id", 9], ["LIMIT", 1]]
↳ app/views/lists/show.html.erb:5
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."list_id" = $1 [["list_id", 9]]
↳ app/views/lists/show.html.erb:6
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] (6.4ms) COMMIT
(0.8ms) SELECT COUNT(*) FROM "products" WHERE "products"."list_id" = $1 [["list_id", 9]]
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Disk Storage (0.2ms) Deleted file from key: 31cfuow9pj6vjqhq8i479fdxf1lc
↳ app/controllers/application_controller.rb:5:in `product_list_size?'
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Disk Storage (0.1ms) Deleted files by key prefix: variants/31cfuow9pj6vjqhq8i479fdxf1lc/
Rendered lists/show.html.erb within layouts/application (Duration: 9.5ms | Allocations: 7278)
[ActiveJob] [ActiveStorage::PurgeJob] [e9b35a62-ff6c-4a38-8946-3aa9c19668ef] Performed ActiveStorage::PurgeJob (Job ID: e9b35a62-ff6c-4a38-8946-3aa9c19668ef) from Async(active_storage_purge) in 22.5ms
[Webpacker] Everything's up-to-date. Nothing to do
List item
Egg on my face.
The exclamation mark omission seems to be the issue.
from
#product = #list.products.create(product_params)
to this and it works
#product = #list.products.create!(product_params)

How to use devise_invitable properly with activeadmin ? (issues to send emails)

I am building an app in rails with an admin interface using the activeadmin gem. I have a User model, an Organization Model and an Account Model (polymorphic). A same user can have different types of accounts (Trainer, Team or Athlete) and an organization has many accounts (they are members of this organization).
Only the admin can create organizations and send invitations to join them. I have an OrganizationInvitation model in active admin and I am trying to use devise_invitable.
ActiveAdmin.register OrganizationInvitation do
permit_params :organization_id, :email, :status
form do |f|
f.semantic_errors
f.inputs do
f.input :organization, collection: Organization.all.map { |organization| [organization.name, organization.id] }
f.input :email
end
f.actions
end
controller do
def create
#organization_invitation = OrganizationInvitation.new(organization_invitation_params)
#email = organization_invitation_params[:email]
#user = User.invite!(email: #email)
if #organization_invitation.save
redirect_to admin_organization_invitations_path, notice: 'Invitation successfully sent'
else
redirect_to admin_organization_invitations_path, alert: 'Something went wrong'
end
end
private
def organization_invitation_params
params.require(:organization_invitation).permit(
:organization_id,
:email
)
end
end
end
There will be different small issues to handle the different types of accounts, the case in which a user already has an account etc but for now I am just trying to send an email to invite a new user to an organization. When I test it, the user gets created in the database but apparently no email gets sent. I did the basic setup for devise_invitable and I added this line in my development config file but it still does not work.
config.action_mailer.perform_deliveries = true
I never used these 2 gems before and feel I am doing sth wrong but do not know what. Thank you in advance!
Edit: Here are the logs as well
Started GET "/admin/organization_invitations" for 127.0.0.1 at 2019-03-21 16:10:44 +0000
Processing by Admin::OrganizationInvitationsController#index as HTML
AdminUser Load (3.3ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Rendering /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.4.3/app/views/active_admin/resource/index.html.arb
(1.1ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "organization_invitations" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "organization_invitations" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
(0.4ms) SELECT COUNT(*) FROM "organization_invitations"
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "organization_invitations" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
OrganizationInvitation Load (0.8ms) SELECT "organization_invitations".* FROM "organization_invitations" ORDER BY "organization_invitations"."id" desc LIMIT $1 OFFSET $2 [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Organization Load (1.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Organization Load (1.8ms) SELECT "organizations".* FROM "organizations"
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Rendered /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.4.3/app/views/active_admin/resource/index.html.arb (194.2ms)
Completed 200 OK in 244ms (Views: 224.8ms | ActiveRecord: 13.5ms)
Started GET "/admin/organization_invitations/new" for 127.0.0.1 at 2019-03-21 16:10:46 +0000
Processing by Admin::OrganizationInvitationsController#new as HTML
AdminUser Load (0.5ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Rendering /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.4.3/app/views/active_admin/resource/new.html.arb
Organization Load (0.3ms) SELECT "organizations".* FROM "organizations"
↳ app/admin/organization_invitations.rb:8
Rendered /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.4.3/app/views/active_admin/resource/new.html.arb (105.3ms)
Completed 200 OK in 169ms (Views: 161.7ms | ActiveRecord: 0.8ms)
Started POST "/admin/organization_invitations" for 127.0.0.1 at 2019-03-21 16:10:51 +0000
Processing by Admin::OrganizationInvitationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XkxCRz+/BsvXAmyKUZk4UZhjtE1AcrhOnCxheNQGPQVP+j6tf+rZYVqqiU+CxtYR57pPShgjJdknG9PmiNtGOA==", "organization_invitation"=>{"organization_id"=>"1", "email"=>"myaddress#gmail.com"}, "commit"=>"Create Organization invitation"}
AdminUser Load (2.0ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["email", "antoinecquellier#gmail.com"], ["LIMIT", 1]]
↳ app/admin/organization_invitations.rb:18
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."invitation_token" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["invitation_token", "c94d3b1e55b5cf39eba111085ec94eb1dcccca321308b87017a7f08b1634a3df"], ["LIMIT", 1]]
↳ app/admin/organization_invitations.rb:18
(0.3ms) BEGIN
↳ app/admin/organization_invitations.rb:18
User Update (1.6ms) UPDATE "users" SET "invitation_token" = $1, "invitation_created_at" = $2, "invitation_sent_at" = $3, "updated_at" = $4 WHERE "users"."id" = $5 [["invitation_token", "c94d3b1e55b5cf39eba111085ec94eb1dcccca321308b87017a7f08b1634a3df"], ["invitation_created_at", "2019-03-21 16:10:51.702522"], ["invitation_sent_at", "2019-03-21 16:10:51.702522"], ["updated_at", "2019-03-21 16:10:51.704500"], ["id", 1]]
↳ app/admin/organization_invitations.rb:18
(4.7ms) COMMIT
↳ app/admin/organization_invitations.rb:18
Rendering /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise_invitable-2.0.0/app/views/devise/mailer/invitation_instructions.html.erb
Rendered /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise_invitable-2.0.0/app/views/devise/mailer/invitation_instructions.html.erb (2.4ms)
Rendering /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise_invitable-2.0.0/app/views/devise/mailer/invitation_instructions.text.erb
Rendered /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise_invitable-2.0.0/app/views/devise/mailer/invitation_instructions.text.erb (5.2ms)
Devise::Mailer#invitation_instructions: processed outbound mail in 291.5ms
Sent mail to antoinecquellier#gmail.com (10.4ms)
Date: Thu, 21 Mar 2019 16:10:52 +0000
From: please-change-me-at-config-initializers-devise#example.com
Reply-To: please-change-me-at-config-initializers-devise#example.com
To: antoinecquellier#gmail.com
Message-ID: <5c93b78c2ad5_9da3fc49695b254502d0#MBP-de-Antoine.mail>
Subject: Invitation instructions
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary="--==_mimepart_5c93b78c751_9da3fc49695b25450147";
charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_5c93b78c751_9da3fc49695b25450147
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Hello antoinecquellier#gmail.com
Someone has invited you to http://localhost:3000/, you can accept it through the link below.
http://localhost:3000/users/invitation/accept?invitation_token=LtbgEbDe4ac63rs7oeC3
If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password.
----==_mimepart_5c93b78c751_9da3fc49695b25450147
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<p>Hello antoinecquellier#gmail.com</p>
<p>Someone has invited you to http://localhost:3000/, you can accept it through the link below.</p>
<p>Accept invitation</p>
<p>If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password.</p>
----==_mimepart_5c93b78c751_9da3fc49695b25450147--
(0.2ms) BEGIN
↳ app/admin/organization_invitations.rb:20
Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/admin/organization_invitations.rb:20
OrganizationInvitation Create (1.5ms) INSERT INTO "organization_invitations" ("organization_id", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["organization_id", 1], ["email", "antoinecquellier#gmail.com"], ["created_at", "2019-03-21 16:10:52.023105"], ["updated_at", "2019-03-21 16:10:52.023105"]]
↳ app/admin/organization_invitations.rb:20
(1.6ms) COMMIT
↳ app/admin/organization_invitations.rb:20
Redirected to http://localhost:3000/admin/organization_invitations
Completed 302 Found in 345ms (ActiveRecord: 15.0ms)
Started GET "/admin/organization_invitations" for 127.0.0.1 at 2019-03-21 16:10:52 +0000
Processing by Admin::OrganizationInvitationsController#index as HTML
AdminUser Load (0.5ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Rendering /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.4.3/app/views/active_admin/resource/index.html.arb
(0.6ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "organization_invitations" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "organization_invitations" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
(0.5ms) SELECT COUNT(*) FROM "organization_invitations"
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "organization_invitations" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
OrganizationInvitation Load (0.5ms) SELECT "organization_invitations".* FROM "organization_invitations" ORDER BY "organization_invitations"."id" desc LIMIT $1 OFFSET $2 [["LIMIT", 30], ["OFFSET", 0]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Organization Load (0.5ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.1ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.1ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
CACHE Organization Load (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Organization Load (0.6ms) SELECT "organizations".* FROM "organizations"
↳ /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/log_subscriber.rb:98
Rendered /Users/antoinequellier/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activeadmin-1.4.3/app/views/active_admin/resource/index.html.arb (204.8ms)
Completed 200 OK in 240ms (Views: 233.9ms | ActiveRecord: 3.6ms)
Regarding the mailer config, I'm not sure what you're refering to. Should not this be handled in the DeviseInvitable config?
Config in development.rb
config.action_mailer.default_url_options = { host: 'http://localhost:3000' }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.gmail.com",
port: "587",
domain: "gmail.com",
user_name: "myaddress#gmail.com",
password: "password",
authentication: "plain",
enable_starttls_auto: true
}
config.action_mailer.raise_delivery_errors = false
config.action_mailer.perform_deliveries = true
config.action_mailer.perform_caching = false

Remove N+1 queries for impressionist gem

In my Rails app I have the N+1 problem where I'm making extra call(s) to the database to get associated data over and over again, specially when logging impressions per model.
For example:
Started GET "/" for 127.0.0.1 at 2018-12-02 16:21:05 -0500
Processing by JobsController#index as HTML
Job Load (4.4ms) SELECT "jobs".* FROM "jobs" WHERE "jobs"."published_at" IS NOT NULL ORDER BY "jobs"."published_at" DESC LIMIT $1 OFFSET $2 [["LIMIT", 30], ["OFFSET", 0]]
↳ app/controllers/jobs_controller.rb:22
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 13], ["LIMIT", 1]]
↳ app/controllers/jobs_controller.rb:24
Impression Exists (1.3ms) SELECT 1 AS one FROM "impressions" WHERE "impressions"."impressionable_id" = $1 AND "impressions"."impressionable_type" = $2 AND "impressions"."session_hash" = $3 LIMIT $4 [["impressionable_id", 705], ["impressionable_type", "Job"], ["session_hash", "d80d52dd401011a626d600167140e49f"], ["LIMIT", 1]]
↳ app/controllers/jobs_controller.rb:24
Impression Exists (0.6ms) SELECT 1 AS one FROM "impressions" WHERE "impressions"."impressionable_id" = $1 AND "impressions"."impressionable_type" = $2 AND "impressions"."session_hash" = $3 LIMIT $4 [["impressionable_id", 704], ["impressionable_type", "Job"], ["session_hash", "d80d52dd401011a626d600167140e49f"], ["LIMIT", 1]]
↳ app/controllers/jobs_controller.rb:24
Impression Exists (0.4ms) SELECT 1 AS one FROM "impressions" WHERE "impressions"."impressionable_id" = $1 AND "impressions"."impressionable_type" = $2 AND "impressions"."session_hash" = $3 LIMIT $4 [["impressionable_id", 703], ["impressionable_type", "Job"], ["session_hash", "d80d52dd401011a626d600167140e49f"], ["LIMIT", 1]]
↳ app/controllers/jobs_controller.rb:24
I'm using the impressionist gem and using the impressionist method directly, taking all of the Jobs on the page and logging an impression. The problem is because I'm only recording unique impressions, for records that already have an impression, these additional calls are redundant. I tried to use #jobs.includes(:impressions).each to preload the associated data hoping Rails was smart enough to figure out which records existed, but Rails still outputs numerous Impression Exists queries.
#jobs.each{|job| impressionist(job,'', :unique => [:session_hash])}

Rails - Increasing performance of repeated if statement

I'm using the public_activity gem and in the output, I'm checking if the trackable owner is the same as the current user:
= a.owner == current_user ? 'You' : a.owner.name
did this activity
I get a bunch of cache calls in the log:
User Load (1.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendered public_activity/post/_create.html.haml (1.4ms)
Rendered public_activity/_snippet.html.haml (11.4ms)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendered public_activity/post/_create.html.haml (13.9ms)
Rendered public_activity/_snippet.html.haml (18.9ms)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendered public_activity/comment/_comment.html.haml (0.9ms)
Rendered public_activity/_snippet.html.haml (12.1ms)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendered public_activity/comment/_comment.html.haml (2.7ms)
Rendered public_activity/_snippet.html.haml (56.3ms)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendered public_activity/comment/_comment.html.haml (0.6ms)
Rendered public_activity/_snippet.html.haml (4.5ms)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendered public_activity/content/_comment.html.haml (2.1ms)
Rendered public_activity/_snippet.html.haml (9.5ms)
Is there any way to eager load the conditional?
#jverban is correct that you can compare the record IDs to avoid needless record loading. To answer your question about eager loading though, yes you can eager load using the includes method in the ActiveRecord query chain. For example:
Activity.includes(:owner).latest
That will tell Rails you intend to reference the owner relation and so they should be loaded as well.
I highly recommend adding the bullet gem to your project (only in development and test environments) to detect N+1 queries and warn you when you've got an N+1 query situation like this happening.
You shouldn't need to load the user record, just compare id attributes
= a.owner_id == current_user.id ? 'You' : a.owner.name
The cache calls will likely still happen if multiple activity owners are not the current user (to get the owner name).

Does Rails Really Cache Database Query?

Does Rails actually cache the query result? The documentation says that same query will be never executed twice on the same request:
1.7 SQL Caching
The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
I did an experiment to proof that Rails actually cache the query:
def test
data = ""
User.find(1).update(first_name: 'Suwir Suwirr')
data << User.find(1).first_name
data << "\n"
User.find(1).update(first_name: 'Pengguna')
data << User.find(1).first_name
data << "\n"
render plain: data
end
If the result is cached, i would get the same result for each User.find(1). However, the result was Rails does not actually cache the query; i was expecting the update does not reflected on the result since it was "cached":
Suwir Suwirr
Pengguna
But the console says that it was cached: (Please highlight the CACHE word)
Started GET "/diag/test" for 10.0.2.2 at 2017-02-21 10:30:16 +0700
Processing by DiagController#test as HTML
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.1ms) BEGIN
SQL (0.4ms) UPDATE "users" SET "first_name" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["first_name", "Suwir Suwirr"], ["updated_at", 2017-02-21 03:30:16 UTC], ["id", 1]]
(16.5ms) COMMIT
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.1ms) BEGIN
SQL (0.3ms) UPDATE "users" SET "first_name" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["first_name", "Pengguna"], ["updated_at", 2017-02-21 03:30:16 UTC], ["id", 1]]
(0.9ms) COMMIT
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendering text template
Rendered text template (0.0ms)
Completed 200 OK in 380ms (Views: 3.5ms | ActiveRecord: 21.9ms)
So my question, does Rails actually cache the query result? Or, only several query result on some request?
Update: Using Batch #update_all
I made another experiment to "fool" the query logic. Now Rails does not "cache" the query. Why this behaviour can happen?
# Controller
def test
data = ""
User.where(id: 1).update_all(first_name: 'Suwir Suwirr')
data << User.find(1).first_name
data << "\n"
User.where(id: 1).update_all(first_name: 'Pengguna')
data << User.find(1).first_name
data << "\n"
logger.info 'hi'
render plain: data
end
# Console
Started GET "/diag/test" for 10.0.2.2 at 2017-02-21 10:45:43 +0700
Processing by DiagController#test as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
SQL (13.8ms) UPDATE "users" SET "first_name" = 'Suwir Suwirr' WHERE "users"."id" = $1 [["id", 1]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
SQL (2.9ms) UPDATE "users" SET "first_name" = 'Pengguna' WHERE "users"."id" = $1 [["id", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
hi
Rendering text template
Rendered text template (0.0ms)
Completed 200 OK in 28ms (Views: 0.8ms | ActiveRecord: 17.8ms)
# Browser result
Suwir Suwirr
Pengguna
I was stupid.
Yes, Rails does actually cache the query, but update and destroy will invalidate its query cache. update_all is basically iterating each record with update.
I tried the experiment by really "fooling" the ActiveRecord query mechanism. And yes, it works.
# Controller
def test
data = ""
ActiveRecord::Base.connection.execute('UPDATE "users" SET "first_name" = \'Suwir Suwirr\' WHERE "users"."id" = 1')
data << User.find(1).first_name
data << "\n"
ActiveRecord::Base.connection.execute('UPDATE "users" SET "first_name" = \'Pengguna\' WHERE "users"."id" = 1')
data << User.find(1).first_name
data << "\n"
render plain: data
end
# Browser
Suwir Suwirr
Suwir Suwirr

Resources