I finally got "sign in with Facebook" to work with devise/omniauth, but when a user is signed in, the "sign in with facebook" link doesn't change to "sign out" and/or there is no visible sign out option.
here is my route.rb file
devise_for :users, :controllers => {:omniauth_callbacks => "users/omniauth_callbacks", :registrations => 'registrations'}, :path_names => { :sign_in => 'login', :sign_out => 'logout' } do
get 'login' =>'devise/sessions#new', :as => :new_user_session
post 'login' => 'devise/sessions#create', :as => :user_session
get 'signup' => 'registrations#new', :as => :new_user_registration
get 'signout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
and here is the user file
class User < ActiveRecord::Base
devise :omniauthable, :omniauth_providers => [:facebook]
def self.find_for_facebook_oauth(auth, signed_in_resource=ni)
user = User.where (:provider => auth.provider, :uid => auth.uid).first
unless user
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
end
session controller:
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_path
end
def destroy
session.delete[:user_id] = nil
redirect_to root_path
end
end
Omniauth_callbacks_controller:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
and lastly, the application layout
<% if current_user %>
Signed in as <strong><%= current_user.name %></strong>!
<%= link_to "Sign out", destroy_user_session, id: "sign_out" %>
<% else %>
<li><%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %></li>
<% end %>
Not quite sure waht i keep doing wrong or why I'm having a hard time looking for the answer, so I thought it might be easier to just put up the code. I"m new at this, so any help is appreciated. Thanks!
Short Answer - current_user is nil so your not actually logged in.
If "Sign in with Facebook" is still showing, and Sign Out is not showing, i'm assuming the "Signed in as" Is also not showing.
This is most likely due to this current_user not being set, so this line is failing
<% if current_user %>
and so this code gets triggered
<li><%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %></li>
So I am assuming current_user is not actually getting set, therefore you're not really logged in. You can add the devise controller filter authenticate_user! to see if you're logged in or not. Something like
class ApplicationController
before_filter :authenticate_user!
end
Which will redirect you if you are not logged in.
Use Pry To Find Out
Pry is a great tool for debugging these kinds of things
To debug with pry, you would add to your Gemfile
group :development do
gem "pry", "~> 0.9.12.4"
end
and run bundle install
you can then add this to your code
<%= binding.pry %>
<% if current_user %>
Signed in as <strong><%= current_user.name %></strong>!
<%= link_to "Sign out", destroy_user_session, id: "sign_out" %>
<% else %>
<li><%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %></li>
<% end %>
Go through your normal Login with Facebook workflow, and the Rails Server in your terminal will 'stop' at the `<% binding.pry %> and allow you to run commands, including checking variables.
In the terminal your rails console is running in, you should see something like
current_user ? "There is a current user set" : "No current user is set"
You may also just run
current_user
but the former has more verbose output. Some other useful debugging commands include
help
whereami
exit
!!!
Related
I have the following model:
class Coupon < ActiveRecord::Base
belongs_to :company
validates :description, presence: true, length: { maximum: 50 }, uniqueness: { case_sensitive: false }
validates :fine_print, presence: true
end
and the following method in the coupon controller:
def redeem
if params[:pin] == #coupon.company.pin
redirect_to root_path
else
flash.now[:notice] = "Incorrect Pin"
render :show
end
end
This form is in the a view:
<%= form_for( #coupon, :url => coupons_redeem_path( #coupon ), :html => { :method => :post } ) do |f| %>
<%= label_tag("pin", "Search for:") %>
<%= text_field_tag("pin") %>
<%= f.submit "Close Message" %>
<% end %>
I want the form to call the redeem method in the coupons controller when hitting submit but am getting this error:
No route matches [POST] "/coupons/redeem.1"
EDIT
These are my routes:
resources :companies do
resources :coupons
end
get 'coupons/redeem'
In your routes, coupons are nested resources of companies. So you should choose one of these alternatives:
1st:
resources :companies do
resources :coupons do
post :redeem, on: :member
end
end
This leads to helpers like this: redeem_company_coupon_path(company, coupon) (and send smth there via POST).
If you don't want to include company to your path, you could choose 2nd:
resources :companies do
resources :coupons
end
post 'coupons/:id/redeem', to: 'coupons#redeem', as: :redeem_coupon
After that you could use redeem_coupon_path(coupon) helper
I'm going through the rails tutorial and my login page is throwing an exception after exercise 8.1.5 when I click the login button with no email or pw entered:
http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec-rendering_with_a_flash_message
Error:
NoMethodError in SessionsController#create
undefined method `[]' for nil:NilClass
app/controllers/sessions_controller.rb:7:in `create'
SessionsController matches the final code exactly for the Create method
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:session][:email].downcase) #line 7
if user && User.authenticate(params[:session][:password])
#will fill this in later
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
end
end
I did change the button label to Log in instead of "Sign in" as that is too confusing with "Sign up", but I didn't think that would create the problem. sessions\new.html.erb
<% provide(:title, "Log in") %>
<h1>Log in</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(:sesssion, url: sessions_path) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Log in", class: "btn btn-large btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
This post hints that I need a method in my user model, but adding that didn't help:
NoMethodError in SessionsController#create
I tried adding this to user.rb, but it didn't help
results from find_by_email executed in function differs from console:
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return user.nil? ? nil : user
end
Any ideas would be greatly appreciated!
I've looked at the example from the book and your code and I noticed this line
if user && User.authenticate(params[:session][:password])
your User.authenticate should be lowercased to user.authenticate. Revert back to your original code.
I have just had the same problem and found that in fact i needed [:sessions] with an s on the end!
So line reads
if user && User.authenticate(params[:sessions][:password])
Hope this helps someone in the future!
I had the same problem after doing exercise 1 in chapter 8 where I replaced the use of form_for with form_tag. This resulted in the name attributes in the generated input form fields changing from name="session[email]" and name="session[password]" to name="email" and name="password". Subsequently, I needed to access the params using params[:email] and params[:password] instead of params[:session][:email] and params[:session][:password].
My new Sessions controller looks like this:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email].downcase)
if user && user.authenticate(params[:password])
sign_in user
redirect_to user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
sign_out
redirect_to root_url
end
end
This solved the problem for me. Hope this is helpful to someone else.
In this section from the tutorial chapter 8, all instances of [:session] should be [:sessions]. Hope that helps.
class SessionsController < ApplicationController
.
.
.
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
.
.
.
end
Could you confirm the word 'session' in app/view/session/new.html.erb spelling correct?
I see you wrote:
form_for(:sesssion, url: sessions_path) do |f|
But in app/controllers/sessions_controller.rb, you wrote:
user = User.find_by_email(params[:session][:email].downcase) #line 7
They must be the same.
I am new to rails, I was working on paperclip gem and wanted to save the simple files, saved from paperclip.
I have the My model as follow :-
class UserAttachment < ActiveRecord::Base
attr_accessible :email, :user_id, :attached_file
has_attached_file :attached_file
validates_attachment_presence :attached_file
validates_attachment_size :attached_file, :less_than => 20.megabytes
end
My controller action where the form is called :-
class HomeController < ApplicationController
def index
#uattachment = UserAttachment.new
end
end
Index view code, where the form is located
<%= form_for #uattachment, :url => attachment_get_link_path, :html => { :method => :post, :id => 'attachment_form', :multipart => true }, :remote => true do |f| %>
<%= f.email_field :email, :value=>nil, :placeholder => "Enter your email here", :required => true %><br />
<%= f.file_field :attached_file %>
<%= f.submit "Submit" %>
<% end %>
I want to use some following kind of code to save the data :-
(Code below is not the correct code, it's an excitation to tell what I want to do in my application.)
#uattachment = UserAttachment.new
#uattachment = params[:user_attachment]
#uattachment.save
Params received are as follows :-
(rdb:6) pp params
{"utf8"=>"✓",
"authenticity_token"=>"dfjaskldjadslgjsoidruts48589034lsker=",
"user_attachment"=>
{"email"=>"testing#email.com",
"attached_file"=>
#<ActionDispatch::Http::UploadedFile:0x007fcb58682ba0
#content_type="image/jpeg",
#headers=
"Content-Disposition: form-data; name=\"user_attachment[attached_file]\"; filename=\"someimage.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="800px-Kinnaur_Kailash.jpg",
#tempfile=#<File:/tmp/RackMultipart20121205-8432-1fc1kpi>>},
"commit"=>"Submit",
"controller"=>"attachment",
"action"=>"get_link"}
Got the catch, it was pretty simple, I checked it from the scaffolded assignment,
def index
#uattachment = UserAttachment.new(params[:user_attachment])
#uattachment.save
end
And you get the golden words in response "true".
I'm trying to do something that seems conceptually simple, but I just can't get it working. Here's what I'm trying to do:
I have a simple "search" form on the /users/index page. It
leverages jQuery Tokeninput to autocomplete a user (name/username)
when the current user types into the search field. What I want to do
is let the user type a name, select a user from the list, then click
"submit" and be taken to the selected user's profile (/users/:id/ - which is
Users#show). I have jQuery Tokeinput configured to submit the user_id as :user_token.
I can't seem to get this working. The autocomplete part works correctly, but I can't figure out how to "submit" so that the entered user's profile is shown.
Here's what happens when I hit the "submit" button on the form (pulled from the development log in the terminal):
Started PUT "/users/2" for 127.0.0.1 at 2012-05-08 11:19:56 -0400
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"blah blah=", "user"=>{"user_token"=>"41"}, "commit"=>"Go to profile", "id"=>"2"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", "2"]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1
Completed 500 Internal Server Error in 83ms
NoMethodError (undefined method `downcase!' for nil:NilClass):
app/controllers/users_controller.rb:119:in `update'
So it's calling the update action on the users controller, I assume because "#user" already exists (specifically, it's the current_user who clicks the submit button).
On screen, I see:
The path shown up top is .../users/2 (the id of current user), and in
the browser I see:
NoMethodError in UsersController#update
undefined method `downcase!' for nil:NilClass
I'm getting that because it's trying to run the "update" action in the Users controller, and there's a "downcase!" call on one of the params at the beginning up the update action. That parameter ([:user][:email]) obviously doesn't exist since it's not in the form I'm submitting.
What I really want to do is go to "/users/41" (the show page for the user whose id is passed as params[:user][:user_token]). How do I do this?
Here's all the relevant code:
#users_controller.rb#Index
def index
#title = "All users"
#label = "All users"
#list_users = User.order(:name).page(params[:page]) #generates users shown on index page
#user = current_user
# This is used to populate the autocomplete field in the little search form
#users = User.where("LOWER(name) like ? OR LOWER(username) like ?", "%#{params[:q].downcase}%", "%#{params[:q].downcase}%").order('name ASC').limit(10) if params[:q]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users, :only => [:id, :name, :username] }
end
end
My routes...
#routes.rb
resources :comments
resources :invitations
resources :sessions, :only => [:new, :create, :destroy]
resources :shares, :controller => "item_shares", :as => "item_shares" do
resources :comments
end
resources :posts, :controller => "item_posts", :as => "item_posts" do
resources :comments
end
resources :items
resources :relationships, only: [:create, :destroy]
resources :users do
member do
get :following, :followers
end
end
resources :password_resets
match '/signup', :to => 'users#new'
match '/signup/:invitation_token' => 'users#new', :as => :signup_with_invitation
match '/signin', :to => 'sessions#new'
match '/signout', :to => 'sessions#destroy'
match '/invite', :to => 'invitations#new'
match '/users/:id/shared', :to => 'users#shared'
match '/users/:id/received', :to => 'users#received'
match '/users/:id/saved', :to => 'users#saved'
match '/users/:id/posts', :to => 'users#posts'
match '/reciprocal_followers', :to => 'users#reciprocal_followers'
root :to => 'pages#home'
Here is my form (this definitely does NOT work, although the jQuery Tokeninput does work):
#_user_search_form.html.erb
<div class="form">
<span class="form-label-right round-bottom-left-5 round-top-right-5 gray-gradient">Find someone</span>
<%= form_for #user, :action => "show" do |f| %>
<div class="field">
<%= f.label :user_token, "Name or Username" %></br>
<%= f.text_field :user_token, :placeholder => 'John Doe or JohnDoe123', "data-pre" => (#pre_populate_data.to_json(:only => [:id, :name, :username]) unless #pre_populate_data.nil?) %>
</div>
<div class="actions">
<%= f.submit "Go to profile" %>
</div>
<% end %>
</div>
Here's the relevant part of my user model:
#user.rb
attr_accessible :user_token
attr_reader :user_token
<% form_for #project, :url => { :controller => "project", :action => "thumbnail_save" } do |form| %>
....
<% end %>
Also write something like this in your config file (put it at the top)
get "users/show"
If that doesn't work try this. (you might have to change your form for below to work)
match "project/thumbnail_save", :to => "project#thumbnail_save"
Let us know if any more issues.
This is basically a resource-less form, but the other examples don't seem to work. I'm using sorcery and formtastic. formtastic is working just fine for the new user form, but i can't seem to create a new session...
<%= semantic_form_for 'sessions', :url => sessions_path, :method => :post do |f| %>
<%= f.inputs do %>
<%= f.input :email %>
<%= f.input :password %>
<% end %>
<%= f.buttons %>
<% end %>
this is close, but it outputs
<input id="sessions_email" name="sessions[email]" type="text" />
instead of
<input id="sessions_email" name="email" type="text" />
so when i submit the form, i get a template missing error
`Template is missing
Missing template sessions/create, application/create with {:handlers=>[:erb, :builder, :coffee], :formats=>[:html], :locale=>[:en, :en]}. `
i guess i just need to know how to set the 'name' attribute of the form specifically, but that seems like a bit of a hack. is there a more 'semantic' way to do this? please help thanks.
EDIT: here's the controller code per request...
class UsersController < ApplicationController
before_filter :authenticate, :except => [:show, :new, :create]
before_filter :correct_user, :only => [:edit, :update]
before_filter :admin_user, :only => :destroy
def index
#title = "All users"
#users = User.paginate(:page => params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(:page => params[:page], :per_page => 6)
#title = #user.name
end
def new
#user = User.new
#title = 'Sign up'
end
def create
#user = User.new(params[:user])
if #user.save
Notifier.registration_confirmation(#user).deliver
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
def edit
#user = User.find(params[:id])
#title = "Edit user"
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to #user
else
#title = "Edit user"
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User Removed"
redirect_to users_path
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(:page => params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(:page => params[:page])
render 'show_follow'
end
private
def has_valid_signup_code?(code)
code == valid_signup_code
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
thanks!
You are probably missing some kind of redirect after successful login. This is how the create action in my sessions controller looks like (note my controller is named UserSessionsController and not SessionsController):
def create
if #user = login(params[:session][:email], params[:session][:password])
redirect_back_or_to root_path, :notice => I18n.t("user_sessions.login_success")
else
flash.now[:alert] = I18n.t("user_sessions.login_failure")
render :action => :new
end
end
And this is the login form:
= semantic_form_for 'session', :url => user_sessions_path do |f|
= f.inputs do
= f.input :email
= f.input :password
= f.buttons do
= f.commit_button