I am trying to configure Google OAuth and I am facing a very silly mistake I can't solve :
When I click on the Sign in button on the home page:
<% if user_signed_in? %>
Signed in as <%= current_user.name %>. Not you?
<%= link_to "Sign out", destroy_user_session_path,:method => :delete %>
<% else %>
<%= link_to "Sign in with Google", user_omniauth_authorize_path(:google_oauth2) %>
<% end %>
I get this error undefined method find_for_google_oauth2 for #
It says the problem is at that line:
#user = user.find_for_google_oauth2(request.env["omniauth.auth"], current_user)
Here is my app/controllers/users/omniauth_callbacks_controller.rb:
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.from_omniauth(request.env["omniauth.auth"])
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
def google_oauth2
#user = User.find_for_google_oauth2(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.google_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
Here is my app/models/user.rb:
class User < ActiveRecord::Base
before_save :default_values
attr_accessor :delete_photo
validates :first_name, presence: true
validates :last_name, presence: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook,:google_oauth2]
has_many :usertoskills
has_many :skills, through: :usertoskills
has_many :usertoprojects
has_many :projects, through: :usertoprojects
has_many :demands
has_many :news
has_attached_file :photo, :styles => { :small => "150x150>", :regular => "300x300>" },
:url => "/assets/users/:id/:style/:basename.:extension",
:path => ":rails_root/public/assets/users/:id/:style/:basename.:extension",
:default_url => "/assets/users/default/:style/default.jpg"
validates_attachment_content_type :photo, :content_type => ['image/jpeg', 'image/png']
#def name
# "#{self.first_name.camelize} #{self.last_name.camelize}"
#end
def default_values
if self.first_name && self.last_name
self.name = "#{self.first_name.camelize} #{self.last_name.camelize}"
end
end
def admin
self.role == 'admin'
end
def accessible_demands
if self.role == 'admin'
#demands = Demand.all
else
#demands = []
self.projects.each do |p| #demands.concat(p.demands) end
#demands = Demand.includes(:project).where(projects: { collective: true }, demands: {user_id: self.id}) | #demands
end
return #demands
end
def accessible_projects
if self.role == 'admin'
#projects = Project.all
else
#projects = self.projects
end
return #projects
end
def accessible_transactions
if self.role == 'admin'
#transactions = Transaction.all
else
#transactions = Transaction.where(sender_id: self.id)
end
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
#user.first_name = auth.info.first_name
#user.last_name = auth.info.last_name # assuming the user model has a name
#user.image = auth.info.image # assuming the user model has an image
end
def self.find_for_google_oauth2(access_token, signed_in_resource=nil)
data = access_token.info
user = User.where(:provider => access_token.provider, :uid => access_token.uid ).first
if user
return user
else
registered_user = User.where(:email => access_token.info.email).first
if registered_user
return registered_user
else
user = User.create(name: data["name"],
provider:access_token.provider,
email: data["email"],
uid: access_token.uid ,
password: Devise.friendly_token[0,20],
)
end
end
end
end
end
Thanks in advance for your help!
You have mismatched def and end calls in your class - you're missing an end after your def self.from_omniauth(auth) method. The where on the next line is incorrectly indented, hiding it. So in actuality, your find_for_google_oauth2 method is being defined inside your from_omniauth method.
This is one of the reasons that Ruby community conventions are really strict about a two-space indentation policy.
Related
I try to update an existing user:
Controller-Snippet
def account_settings
#user = current_user
end
def set_account_info
old_user = current_user
# verify the current password by creating a new user record.
#user = User.authenticate_by_username(old_user.username, params[:user][:password])
# verify
if #user.nil?
#user = current_user
#user.errors[:password] = "Das eingegebene Passwort ist falsch."
render :action => "account_settings"
else
# update the user with any new username and email
#user.update(params[:user])
# Set the old email and username, which is validated only if it has changed.
#user.previous_email = old_user.email
#user.previous_username = old_user.username
if #user.valid?
# If there is a new_password value, then we need to update the password.
#user.password = #user.new_password unless #user.new_password.nil? || #user.new_password.empty?
#user.save
flash[:notice] = 'Benutzerkonto-Einstellungen wurden übernommen.'
redirect_to :root
else
flash[:error] = #user.username
render :action => "account_settings"
end
end
end
I allready tried post-, put- and patch-method.
Route-Snippet
Calendar::Application.routes.draw do
root "welcome#index"
get "user/account_settings" => "user#account_settings"
patch "user/account_settings" => "user#set_account_info"
end
User-Model
class User < ActiveRecord::Base
attr_accessible :email, :username, :previous_email, :previous_username, :password, :password_confirmation, :new_password, :new_password_confirmation
attr_accessor :password, :new_password, :previous_email, :previous_username
before_save :encrypt_password
validates_confirmation_of :password
validates_confirmation_of :new_password, :if => Proc.new {|user| !user.new_password.nil? && !user.new_password.empty? }
validates_presence_of :password, :on => :create
validates_presence_of :email, :if => Proc.new {|user| user.previous_email.nil? || user.email != user.previous_email}
validates_presence_of :username, :if => Proc.new {|user| user.previous_username.nil? || user.username != user.previous_username}
validates_uniqueness_of :email, :if => Proc.new {|user| user.previous_email.nil? || user.email != user.previous_email}
validates_uniqueness_of :username, :if => Proc.new {|user| user.previous_username.nil? || user.username != user.previous_username}
def initialize(attributes = {})
super # must allow the active record to initialize!
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.authenticate_by_email(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def self.authenticate_by_username(username, password)
user = find_by_username(username)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
The Flash-Notice flash[:error] = #user.username puts the new username, so I dont understand why the user isn't updated in the database.
First I thought my set_account_info-method isn't right, but I have no better idea how to check the inputs.Secondly I changed the form_for method from default(post) to put and then to patch, but that also didn't help.
With rails 4 attr_accessible is no longer used, instead we now have strong params. what this means is that you now need to tell the application what parameters it can update (A whitelist).
This is all done in the controller now, I will give you an example of what I do and see if it helps you in your scenario
class ExampleController < ApplicationController
def create
#object= Object.new(my_params)
if #object.save
redirect_to root_path, notice: 'Object Successfully Created'
else
render action: 'new'
end
end
def update
#object= Object.find(params[:id])
if #object.update_attributes(my_params)
redirect_to root_path, notice: 'Object updated'
else
render action: 'edit'
end
end
private
def my_params
params.require(:object).permit(:id, :title, :overview, :category_id,
nested_attributes: [:id, :gallery_id, :gallery_category_id, :photo, :_destroy])
end
I have a many-to-many relationship between Users and Documents. Users can invite other users to collaborate on their documents by entering their email. The application will look up a user by email and set the user_id in DocumentUser. If a user is not found, I am getting this error: Rails 4 - undefined method 'attributes' for nil:NilClass. This error occurs in DocumentUsersControlller.create. How can I prevent this error using best practices? Also, why doesn't the presence validator prevent this error?
Here are the relevant controllers and models:
class DocumentUsersController < ApplicationController
def new
#document = Document.find_by link_key: params[:document_id]
#document_user = #document.document_users.build
end
def create
#document = Document.find_by link_key: params[:document_id]
#document_user = #document.document_users.build(document_user_params)
user = User.find_by email: params[:document_user][:email]
#document_user.user = user if user
respond_to do |format|
if #document_user.save #error occurs here if user is Nil
format.html { redirect_to #document, notice: 'User Invitation was sent.'}
format.json { render action: 'show', status: :created, location: #document_user }
else
format.html { render action: 'new' }
format.json { render json: #document_user.errors, status: :unprocessable_entity }
end
end
end
def destroy
end
private
def document_user_params
params.require(:document_user).permit(:email, :admin, :editor, :viewer)
end
end
class DocumentUser < ActiveRecord::Base
include KeyCreator
belongs_to :document
belongs_to :user
before_create :before_create
attr_accessor :email
validates :user, uniqueness: { message: "has already been invited" }
validates :user, presence: true
validate :email_matches_user
def to_param
"#{invite_key}".parameterize
end
private
def before_create
now = DateTime.now
self.added_date = now
self.modified_date = now
self.invite_key = create_key
self.api_key = create_key
end
def email_matches_user
unless User.exists? email: email
errors.add(:email, "does not match any existing user")
end
end
end
class User < ActiveRecord::Base
include KeyCreator
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_many :document_users
has_many :documents, :through => :document_users
before_create :before_create
private
def before_create
self.api_key = create_key
end
end
class Document < ActiveRecord::Base
include KeyCreator
has_many :document_users, dependent: :destroy
has_many :users, :through => :document_users
before_create :before_create
def self.created_by(user)
Document.joins(:document_users).where(:document_users => {:user => user, :creator => true}).order(:name)
end
def self.guest_in(user)
Document.joins(:document_users).where(:document_users => {:user => user, :creator => false}).order(:name)
end
def to_param
"#{link_key}".parameterize
end
private
def before_create
now = DateTime.now
self.content = nil
self.created_date = now
self.modified_date = now
self.deleted_date = nil
self.api_key = create_key
self.link_key = create_key
end
end
Change your email_matches_user. It should be matching
email: with self.email:
def email_matches_user
unless User.exists? email: self.email
errors.add(:email, "does not match any existing user")
end
end
I am new to Rails and I am trying to allow users to login ONLY if a user has a variable email_activation_token set to true. For some reason I created the if statement but it allows a user to sign in REGARDLESS IF IT IS VALUE! I went to sqlitebrowser and checked my database and the user I created indeed has email_activation_token set to false.
Be mindful of two things. I am using HAML and I have a cookie to keep user logged in.
(Bonus points for any answer that has a link to a tutorial on the topic as it would be helpful)
app/controllers/sessions_controller.rb
def create
user = User.authenticate(params[:email], params[:password])
if user.email_activation_token = true
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
else
flash.now.alert = "You account has not been activated yet check your email!"
render "new"
end
end
app/controller/users_controller
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
UserMailer.registration_confirmation(#user).deliver
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
def accept_invitation
#user = User.find_by_email_activation_token!(params[:token])
#user.email_activation_token = true
redirect_to root_url, :notice => "Email has been verified."
end
end
end
app/views/sessions/new.html.haml
%h1 Log in
= form_tag sessions_path do
%p
= label_tag :email
= text_field_tag :email, params[:email]
%p
= label_tag :password
= password_field_tag :password
%p.button
%input{name: "commit", type: "submit", value: "Log in"}
.field
= label_tag :remember_me
= check_box_tag :remember_me, 1, params[:remember_me]
%p
= link_to "forgotten password?", new_password_reset_path
app/models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
before_save { |user| user.email = email.downcase }
before_create { generate_token(:auth_token) }
# before_create { generate_token(:email_activation_token) }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
VALID_PASSWORD_REGEX = /^(?=.*[a-zA-Z])(?=.*[0-9]).{6,}$/
validates_confirmation_of :password
validates :password, :on => :create, presence: true, format: { with: VALID_PASSWORD_REGEX }
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
app/config/routes.rb
LootApp::Application.routes.draw do
get "password_resets/new"
get "sessions/new"
resources :users
resources :sessions
resources :password_resets
resources :email_activations
resources :users do
collection do
get :accept_invitation
end
end
# get "users/new"
get "static_pages/home"
get "static_pages/help"
root to: 'static_pages#home'
match "sign_up", to: "users#new"
match '/help', to: 'static_pages#help'
match '/log_in', to: 'sessions#new'
match '/log_out', to: 'sessions#destroy'
end
app/controllers/sessions_controller.rb:
def create
# ...
if user.email_activation_token = true
# ...
Should be:
def create
# ...
if user.email_activation_token == true
# ...
I'm trying to create an email which is sent when admin click to active account for user
user has_one :account and account belongs_to :user
in user.rb
devise :database_authenticatable, :registerable, :Trackable, :rememberable, :recoverable
attr_accessible :account, :email, :password, :password_confirmation, :account_attributes, :approved
has_one :account
end
in account.rb
class Account < ActiveRecord::Base
attr_accessible :address, :approved, :name
belongs_to :user
end
in accounts_controller.rb
def activate
#accounts = Account.find(params[:id])
#users = User.where(:id => #accounts.id)
if (#accounts.update_attributes(approved: true)) && (#users.update_all(approved: true))
AccountMailer.activate_account(#users).deliver
redirect_to accounts_path
else
redirect_to accounts_path
end
end
in account_mailer.rb
class AccountMailer < ActionMailer::Base
default :from => "kapanjadi#gmail.com"
def activate_account(user)
#users = user
#account = account.find_by_user_id(user)
mail(:to => user.email, :subject => "Activation Account", :from => "kapanjadi#gmail.com")
end
end
in account_mailer/activate_account.text.erb
congratulation, your account is active now
setup_mailer.rb
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "localhost:3000",
:user_name => "kapanjadi#gmail.com",
:password => "password",
:authentication => "plain",
:enable_starttls_auto => true
}
but an email not send to email user, and no error..
these did not happened to any other action.
Any suggestions as to what I am doing wrong?
UPDATE 1
in accounts_controller.rb
def activate
#account = Account.find(params[:id])
#user = User.where(:id => #account.id)
if (#account.update_attributes(approved: true)) && (#user.update_all(approved: true))
AccountMailer.activate_account(#user,#account).deliver
redirect_to accounts_path
else
redirect_to accounts_path
end
end
in account_mailer.rb
class AccountMailer < ActionMailer::Base
default :from => "fauzieuy#gmail.com"
def activate_account(user,account)
#account = account
#accounts = Account.find_by_user_id(user)
mail(:to => user.email, :subject => "Activation", :from => "kapanjadi#gmail.com")
end
end
error
undefined method `email' for #<ActiveRecord::Relation:0x3e7c720>
in accounts_controller.rb
def activate
#account = Account.find(params[:id])
#user = User.where(:id => #account.id)
if (#account.update_attributes(approved: true)) && (#user.update_all(approved: true))
user_account = account.find_by_user_id(#user)
AccountMailer.activate_account(#user,user_account,#account.user.mail).deliver
redirect_to accounts_path
else
redirect_to accounts_path
end
end
in account_mailer.rb
class AccountMailer < ActionMailer::Base
default :from => "kapanjadi#gmail.com"
def activate_account(user,account,to)
#users = user
#account = account
mail(:to => to, :subject => "Activation Account", :from=>"kapanjadi#gmail.com")
end
end
The above will work for sure.
Why is everything plural (#accounts and #users)?
def activate
#account = Account.find(params[:id])
redirect_to accounts_path and return if #account.nil? # handles bad `params[:id]`
if #account.update_attributes(approved: true) && #account.user.update_attributes(approved: true)
AccountMailer.activate_account(#account.user).deliver
redirect_to accounts_path and return
end
redirect_to accounts_path
end
So I am trying to setup a view for my friendship model from Amistad, but I keep getting this error: "undefined method `each' for nil:NilClass"
This is my friendship.rb model
class Friendship < ActiveRecord::Base
include Amistad::FriendshipModel
attr_accessible :user_id, :friend_id
end
This is my friendships_controller.rb
class FriendshipsController < ApplicationController
before_filter :authenticate_user!
def index
#friends = current_user.friends
#pending_invited_by = current_user.pending_invited_by
#pending_invited = current_user.pending_invited
end
def create
#Friend = User.find(params[:user_id])
#friendship_created = current_user.invite(#Friend)
end
def approve
#Friend = User.find(params[:user_id])
#friendship_approved = current_user.approve(#Friend)
#friends = current_user.friends
#pending_invited_by = current_user.pending_invited_by
end
def remove
#Friend = User.find(params[:user_id])
#friendship = current_user.send(:find_any_friendship_with, #Friend)
if #friendship
#friendship.delete
#removed = true
end
end
end
My user model user.rb
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
has_many :cereals, dependent: :destroy
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
include Amistad::FriendModel
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login, :avatar, :avatar_cache, :remove_avatar, :firstname, :lastname, :phone, :urlname
# attr_accessible :title, :body
attr_accessor :login
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
def update_with_password(params={})
current_password = params.delete(:current_password)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = if params[:password].blank? || valid_password?(current_password)
update_attributes(params)
else
self.attributes = params
self.valid?
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
clean_up_passwords
result
end
end
and users_controller.rb
class UsersController < ApplicationController
def show
if params[:id].nil? && current_user
#user = current_user
else
#user = User.find(params[:id])
end
#cereal = current_user.cereals.build if signed_in?
#cereals = #user.cereals.paginate(page: params[:page])
end
def first_time
if params[:id].nil? && current_user
#user = current_user
else
#user = User.find(params[:id])
end
end
def edit
if params[:id].nil? && current_user
#user = current_user
else
#user = User.find(params[:id])
end
end
def profile
#username = params[:id]
#title = "User Profile for #{#username}"
#user = User.find_by_username(#username)
#users = User.all :conditions => ["id != ?", current_user.id]
end
end
Anyone have any idea whats going on? I am still new at rails, and could use some insight.
The View:
.row
#dashboard.span12
= #user.username
#profile_pic=link_to image_tag(#user.avatar, border: '0'), action: 'profile', controller: 'users'
#bowl_outline.span9
Bowl
#friends.span3
Friends
%ul
- for #user in #friends
%li
- if current_user.friend_with? user
= user.username
|
You are already friends!
- elsif current_user.invited? user
= user.username
|
Pending request ...
- elsif user.invited? current_user
= user.username
|
= link_to "Confirm friend?", friend_path(user), :method => "put"
- else
= user.username
= link_to "Add friend?", friends_path(:user_id => #user), :method => "post"
You should not have an instance variable as your for loop variable.
for user in #friends
I believe #friends = current_user.friends returns an array so it should work with this fix.
EDIT
If this is loop is in your user/profile view, you need to have #friends defined in your profile action in your users controller.
def profile
#username = params[:id]
#friends = current_user.friends
#title = "User Profile for #{#username}"
#user = User.find_by_username(#username)
#users = User.all :conditions => ["id != ?", current_user.id]
end
and add before_filter :authenticate_user! to your UsersController.
In ruby, it is common to see the for user in #friends line to be written:
#friends.each do |user|
In this configuration, we have an each message being passed to an object stored in #friends, which might be nil and throwing the error.
Make sure #friends = current_user.friends in your FriendshipsController is not returning nil (you can't iterate over it).
If you are receiving nil, add a default by changing the line to this: #friends = current_user.friends || []