The title pretty much explains it. I'm having an odd situation where views that allow users to delete notifications using Ajax cause the current_user to be logged out. I don't even know where to begin debugging this...
Here's the controller
class NotificationsController < ApplicationController
def destroy
#notification = Notification.find(params[:id])
#notification.destroy
respond_to do |format|
format.js
end
end
end
This is the entire controller, nothing is abridged. The notifications are generated by the system, so the only action a user can take is to "dismiss" (ie. delete) them.
I also tried this using the newer respond_with syntax and had the same effect.
I'm using Devise, and Rails 3.0.9. Any idea what could be going on -- or suggestions on how to debug??
-- EDIT 1 --
Routes.rb
resources :notifications, :only => [:destroy]
Delete link
%span.delete= link_to( 'dismiss', notification_path(notification), :method => :delete, :remote => true )
-- EDIT 2 --
Well, I noticed something new in the logs -- see **** below.
Started DELETE "/notifications/10" for 127.0.0.1 at 2011-06-21 21:47:15 -0500
Processing by NotificationsController#destroy as JS
Parameters: {"id"=>"10"}
SQL (0.4ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL (0.3ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Slug Load (0.4ms) SELECT "slugs".* FROM "slugs" WHERE ("slugs".sluggable_id = 1 AND "slugs".sluggable_type = 'User') ORDER BY id DESC LIMIT 1
****AREL (0.3ms) UPDATE "users" SET "remember_token" = NULL, "remember_created_at" = NULL, "updated_at" = '2011-06-22 02:47:15.913839', "preferences" = '---
:email_notifications: ''true''
' WHERE "users"."id" = 1
Notification Load (0.2ms) SELECT "notifications".* FROM "notifications" WHERE "notifications"."id" = 10 LIMIT 1
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
AREL (0.3ms) UPDATE "users" SET "notifications_count" = COALESCE("notifications_count", 0) - 1 WHERE "users"."id" = 1
AREL (0.1ms) DELETE FROM "notifications" WHERE "notifications"."id" = 10
Rendered notifications/destroy.js.erb (0.7ms)
Completed 200 OK in 6416ms (Views: 9.6ms | ActiveRecord: 4.1ms)
So, there it is, it looks like part of the users table is getting set to null, particularly the remember_token which I suspect is triggering Devise to end the session, or maybe this is done by Devise after the session is destroyed. But how do I track that down?
The only thing I can think of that causes notifications to interact with Users is there's a counter_cache on Users for notifications_count.
I appreciate thoughts and suggestions on how to debug!
-- EDIT 3 --
After digging with ruby-debug it looks like the issue is related to Devise and changes to the rails.js script. See:
https://github.com/plataformatec/devise/issues/913
https://github.com/ryanb/cancan/issues/280
I'm trying out some of the suggestions on those threads and will post if I find a solution.
I had a similar problem. Solution was as simple as adding
<%= csrf_meta_tag %>
to the layout.
It turns out this had to do with changes to the Rails jQuery UJS driver and Devise. I had updated Devise without updating jQuery UJS -- and Devise was expecting the CSRF token to be handled differently, so it was processing the ajax request as unauthorized which meant destroying the current user's session. Upgrading to the latest jQuery Rails driver fixed the problem.
Are you sure this controller and action are the ones that are triggered by the request? It sounds like the path you are DELETEing isnt right and you are hitting your sessions path instead.
Is it possible that the destroy notification path is redirecting to the session destroy path through JS?
Do you have destroy.js template in your notifications views? try adding one that is empty see if you get a different result.
Related
I have an application with a log out link: <%= link_to('Logout', destroy_user_session_path, method: :delete) %>
Upon clicking the link, I get the following in my dev server logs:
Started DELETE "/users/sign_out" for 172.30.0.1 at 2019-03-06 14:52:49 +1300
Processing by Users::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"JLLEb2GSjGuYx+oBhsAkB0jcP0qZCJEUBvuH5VDmCS9Xbwe/hw085gumBPqJmWTtjyFeW1Io81n32NGxDuKjyQ=="}
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) BEGIN
(0.4ms) COMMIT
Redirected to https://webdev.test:3000/
Completed 302 Found in 28ms (ActiveRecord: 0.9ms)
And the root page then loads again, but the user has not signed out.
I opened up a tab in incognito mode in Chrome and tried logging in and logging out, and it worked fine, so it appears to be something specific to my normal-mode browser. I've tried restarting the browser and it doesn't make a difference.
The user account I'm signed in as in Devise is not increasing its sign-in count each time I do this, nor is the updated_at timestamp changing.
I then put byebug in my root page controller method and ran the following:
1: class HomeController < ApplicationController
2: def index
3: byebug
=> 4: # Omitted for brevity, nothing which touches the current_user object
5: end
6: end
(byebug) Devise.sign_out_all_scopes
true
(byebug) sign_out
(0.8ms) BEGIN
(0.4ms) COMMIT
true
(byebug) continue
And confirmed after that, the user still was not signed out.
Restarting the Rails application and trying all the above again resulted in the same outcome, I still can't sign that user out.
Has anyone seen this before and have any ideas on what's getting stuck and how I can sign out?
Edit
As requested by a commenter, the routes relating to sign_out:
$ rails routes | grep sign_out
destroy_user_session DELETE /users/sign_out(.:format) users/sessions#destroy
$
The output of rails routes | grep destroy_user_session are identical to the above as well, so it shows there's not a named route directing requests to a different controller action.
And the controller at app/controllers/users/sessions_controller.rb (which only overrides the create method, not the destroy method)
class Users::SessionsController < Devise::SessionsController
def create
# Omitted for brevity
end
end
I tried and created user controller to check and seem everything is good. I have only one strange problem, which I am not sure why it is changed. It is about SQL Query. Your response is getting following query.
SELECT `users`.* FROM `users` WHERE `users`.`id` = 4 ORDER BY `users`.`id` ASC LIMIT 1
How come it has 4 id as hard coded, normally it should be prepared statement like following
SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 4], ["LIMIT", 1]]
So to investigate I will try to use different users to see to login and logout and check if ID is changing or not. If it is not changing then I will investigate where this ID is coming from and why it is hard coded.
Changing to a button instead of link fixed this issue for me <%= button_to 'Exit', destroy_user_session_path, method: :delete %>
The link_to in my user name is creating an error and I don't know why.
Error:
Couldn't find StripeAccount without an ID
Controller:
this is inside a separate controller from the StripeAccount controller
def settings
#user = current_user.id
#stripe_account = StripeAccount.find(params[:stripe_account_id])
end
I have tried "#stripe_account = StripeAccount.find(params[:id])" with the same error
View:
<%= link_to user_stripe_account_path(#user, #stripe_account) %>
I have tried using #stripe_account.id, etc.
Models:
stripe_account::
belongs_to :user, optional: true
user::
has_one :stripe_account
Routes:
resources :users do
resources :stripe_accounts
end
Error when i try loading the /settings page:
Here's the CMD from when I use: #stripe_account = StripeAccount.find(params[:stripe_account_id])
app/controllers/dashboard_controller.rb:18:in `settings'
Started GET "/settings" for 127.0.0.1 at 2018-11-17 06:27:04 -0500
Processing by DashboardController#settings as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/controllers/dashboard_controller.rb:17
Completed 404 Not Found in 3ms (ActiveRecord: 0.3ms)
ActiveRecord::RecordNotFound (Couldn't find StripeAccount without an ID):
app/controllers/dashboard_controller.rb:18:in `settings'
When i use #stripe_account = StripeAccount.find(params[:id])
ActiveRecord::RecordNotFound (Couldn't find StripeAccount without an ID):
app/controllers/dashboard_controller.rb:18:in `settings'
Started GET "/settings" for 127.0.0.1 at 2018-11-17 06:28:21 -0500
Processing by DashboardController#settings as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/controllers/dashboard_controller.rb:17
Completed 404 Not Found in 3ms (ActiveRecord: 0.3ms)
ActiveRecord::RecordNotFound (Couldn't find StripeAccount without an ID):
app/controllers/dashboard_controller.rb:18:in `settings'
What am i doing incorrectly?
The only issue i can think of that may be happening is rails/ruby is finding the API ID from stripe_account, which contains a bunch of information from stripe... if so, is there a way i can specifically state using the ID from the table?
You should be able to do #stripe_account = current_user.stripe_account if you wan't to set the variable to the current_user's stripe account (you have no id param on the request). And I recommend you to use #user = current_user or #user_id = current_user.id since it's confusing to read a variable named #user that has an integer value.
When you define "StripeAccount belongs_to User", by default (it's the convention) ActiveRecord looks for a user_id column on stripe_accounts table.
I'd recommend you to read this https://guides.rubyonrails.org/association_basics.html. It explains all types of associations and you can configure your associations even if they are not conventional (different class names, no _id column, etc).
After many attempts, i got one way to work. I'm not sure how efficient this is and i will explore more options.
This ended up working how i wanted to:
#stripe_account = StripeAccount.find_by(params[:id])
The key was using ".find_by" and not ".find". This allows the link_to to operate and goes to the right location.
IMPORTANT NOTE: The id does not matter, it is same in all cases.
I am only seeing the name field, not description, hours, etc even though these aren't null.
I have declared all standard routes through resources (with default format json), not individually.
I have even tried creating a projects/show.json.jbuilder file:
json.name #project.name
json.description #project.description
json.hours #project.hours
json.ownername #project.ownername
My projects/show method:
#project = Project.find(params[:id])
render :json => #project
FIRST EDIT:
I added logger.debug right before defining #project in my show method.
Now in my command prompt window for the local server, I am seeing:
Started GET "/projects/12" for 127.0.0.1 at 2015-02-25 16:36:45 -0500
ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by ProjectsController#show as HTML
Parameters: {"id"=>"12"}
Project Load (0.1ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT 1 [["id", 12]]
#<Project:0x007f95477e72f8>
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 51]]
Completed 200 OK in 42ms (Views: 15.2ms | ActiveRecord: 0.9ms)
I am wondering why I am seeing the "User Load" part, since my Project model does not belong to a User object or have any relationship with it (though in the past it may have had before that relationship was removed). Also, I don't think I saw
#<Project:0x007f95477e72f8>
earlier.
I solved my problem this way.
Basically, projects/12.json currently works and not projects/12. Since I am more interested in obtaining the data client side, it is ok for me. I would need a projects/show.html.erb page that calls all the project data through view helper methods, for the HTML to work.
def show
#project = Project.find(params[:id])
respond_to do |format|
format.html
format.json
end
end
projects/12.json works because I have a projects/show.json.jbuilder file (as I shared in my original question post):
json.name #project.name
json.description #project.description
json.hours #project.hours
json.ownername #project.ownername
So I have a question model that belongs to User. Initially, I'd like to set basic questions with an attribute public: true, and every user can see this kind of questions which I'd like to create in the seed.rb.
Then, subsequently with an Answer model. Each users answer belongs to a certain question.
Now the issue is creating these pre-made questions for the users to answer I've tried the following in seed.rb:
u = User.new(email: "test#gmail.com", password: "testpass", password_confirmation: "testpass", gender: "M")
questions = u.questions.build(title: "What is your favourite food?")
u.save
And I'd like to call the same default questions for all users in the view with
questions_controller.rb
def index
#questions = Question.all
end
But this simply doesn't seem to be working, i.e. when I go to rails c and run u, it's an undefined method.. and the u.questions is an empty array. I've run these lines in console manually and they worked so I'm not sure what's happening here.
What am I doing wrong?
Update dev log
^[[1m^[[36mUser Load (16.9ms)^[[0m ^[[1mSELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1^[[0m
^[[1m^[[35mQuestion Load (645.9ms)^[[0m SELECT "questions".* FROM "questions" WHERE "questions"."user_id" = $1 [["user_id", 2]]
^[[1m^[[36mActiveRecord::SchemaMigration Load (48.4ms)^[[0m ^[[1mSELECT "schema_migrations".* FROM "schema_migrations"^[[0m
^[[1m^[[36mActiveRecord::SchemaMigration Load (1.0ms)^[[0m ^[[1mSELECT "schema_migrations".* FROM "schema_migrations"^[[0m
^[[1m^[[35m (0.3ms)^[[0m BEGIN
^[[1m^[[36mUser Exists (34.7ms)^[[0m ^[[1mSELECT 1 AS one FROM "users" WHERE "users"."email" = 'anthony#gmail.com' LIMIT 1^[[0m
^[[1m^[[35m (0.3ms)^[[0m ROLLBACK
DEPRECATION WARNING: You didn't set config.secret_key_base. Read the upgrade documentation to learn more about this new config option. (called from service a\
t /usr/local/rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/webrick/httpserver.rb:138)
I think the issue was that the OP had bad data in the development database.
Running rake db:drop db:create db:setup appeared to fix the issue.
I've tried looking at other answers for this but I can't seem to figure out why my redirect isn't working.
So I'm using Devise with Rails 3.1, and I'm making a shopping site. Visitors aren't allowed to add things to their cart unless they are signed in. This is what I'm having trouble with: if they aren't signed in, I want to redirect them to the Items index page. Here's what I have:
class ItemsController < ApplicationController
def add_to_cart
#item = Item.find(params[:id])
if current_user
#item.update_attributes(:cart_id => #current_cart.id)
redirect_to :back
else
redirect_to categories_path, notice: 'You must sign in to add an item to your cart.'
end
end
.
.
.
end
As of right now, when I click the link to add to cart, the method gets executed (I can see Rails loading and defining #item in the server log), and it reaches the 'else' statement, but no redirect happens.
I've already generated scaffolding for the index, new, etc. (all the RESTful actions). Also, I'm sure that I'm reaching the add_to_cart method because I've tried debugging with some puts statements. What's happening here?
EDIT:
Also, another weird thing which may be of use... The server seems to try to execute this method twice, and tries to 'get' categories twice:
Started GET "/items/3/add_to_cart" for 127.0.0.1 at 2012-01-12 16:53:11 -0800
Processing by ItemsController#add_to_cart as JS
Parameters: {"id"=>"3"}
Category Load (0.3ms) SELECT "categories".* FROM "categories"
Item Load (0.2ms) SELECT "items".* FROM "items" WHERE "items"."id" = $1 LIMIT 1 [["id", "3"]]
Redirected to http://localhost:3000/categories
Completed 302 Found in 26ms
Started GET "/items/3/add_to_cart" for 127.0.0.1 at 2012-01-12 16:53:11 -0800
Processing by ItemsController#add_to_cart as JS
Parameters: {"id"=>"3"}
Category Load (0.2ms) SELECT "categories".* FROM "categories"
Item Load (0.2ms) SELECT "items".* FROM "items" WHERE "items"."id" = $1 LIMIT 1 [["id", "3"]]
Redirected to http://localhost:3000/categories
Completed 302 Found in 25ms
Started GET "/categories" for 127.0.0.1 at 2012-01-12 16:53:12 -0800
Processing by CategoriesController#index as JS
Category Load (0.2ms) SELECT "categories".* FROM "categories"
CACHE (0.0ms) SELECT "categories".* FROM "categories"
Rendered categories/index.html.erb within layouts/application (0.0ms)
Completed 200 OK in 35ms (Views: 28.5ms | ActiveRecord: 4.2ms)
Started GET "/categories" for 127.0.0.1 at 2012-01-12 16:53:12 -0800
Processing by CategoriesController#index as JS
Category Load (0.2ms) SELECT "categories".* FROM "categories"
CACHE (0.0ms) SELECT "categories".* FROM "categories"
Rendered categories/index.html.erb within layouts/application (0.0ms)
Completed 200 OK in 37ms (Views: 30.6ms | ActiveRecord: 4.2ms)
EDIT 2 (as requested by Delba)
resources :items do
member do
get 'add_to_cart'
end
end
EDIT 3: changing the else statement to respond to javascript
respond_to do |format|
format.js { redirect_to items_path, notice: 'You must sign in to add an item to your cart.' }
end
For anyone who may need answers to this question, simply replace redirect_to statements with the following:
respond_to do |format|
format.js
end
Then, in your views under items, make a add_to_cart.js.erb page, consisting of javascript to make notices, or do whatever. Here's what I put in mine:
alert("Need to be signed in")
EDIT: Also, for the part where it executes twice: this is somewhat unrelated, but for some reason by default Rails includes duplicate Javascripts. Specifically, look at application.js: it says require jquery and require jquery_ujs. Disable one of these and you're home free.
To disable one of these javascripts:
Go to assets/application.js
Remove the comments (the // ) before require jquery, require tree .
This way, Rails doesn't assume the default and instead includes only jquery and whatever other javascripts you have in assets/javascripts