Couldn't find product without ID using Stripe - ruby

I am trying to complete my payment set up in Ruby but am struggling to gt the order confirmation screen to print. I have set up my partial for the payment as such.
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-image="<%= asset_path(#product.image_url) %>"
data-name="<%= #product.name %>"
data-description="<%= #product.description %>"
data-amount="<%= #product.price*100.to_i %>">
</script>
My payment controller.
class PaymentsController < ApplicationController
before_action :authenticate_user!
def create
#product = Product.find(params[:product_id])
#user = current_user
token = params[:stripeToken]
# Create the charge on Stripe's servers - this will charge the user's card
begin
charge = Stripe::Charge.create(
amount: #product.price, # amount in cents, again
currency: "eur",
source: token,
description: params[:stripeEmail]
)
if charge.paid
UserMailer.order_confirmation(#user, #product).deliver_now
Order.create!(
:product_id => #product.id,
:user_id => #user.id,
:total => #product.price_show
)
flash[:success] = "Your payment was processed successfully"
end
rescue Stripe::CardError => e
body = e.json_body
err = body[:error]
flash[:error] = "Unfortunately, there was an error processing your payment: #{err[:message]} Your card has not been charged. Please try again."
end
redirect_to product_path(#product), notice: "Thank you for your purchase."
end
end
and my routes file.
Rails.application.routes.draw do
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout' },
controllers: {registrations: "user_registrations"}
resources :products do
resources :comments
end
post 'payments/create'
resources :users
resources :orders, only: [:index, :show, :create, :destroy]
resources :users, except: [:index]
get 'simple_pages/about'
get 'simple_pages/contact'
root 'simple_pages#landing_page'
post 'simple_pages/thank_you'
mount ActionCable.server => '/cable'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
The form within product show.html.erb
<%= form_with(url: '/payments/create') do |form| %>
<%= render partial: "shared/stripe_checkout_button" %>
<%= hidden_field_tag(:product_id, #product.id) %>
<% end %>
However, when I try to complete a test payment, I have the action controller pop up with "Couldn't find Product without an ID". I thought this was defined in the create section but I am not sure how to rectify this. Any suggestions would be greatly appreciated.

The :product_id is not set in your form post,
1. Quick Solution:
your form url could be like this:
<%= form_with(url: "/payments/create?#{product_id=#product.id}") do |form| %>
2. Better Way:
configure you routes for payments already has product id:
# routes.rb
resources :products do
resources :comments
resources :payments
end
form url:
<%= form_with(url: product_payments_path(#product)) do |form| %>

Related

Rails 5.2 ActionMailer Mail won't send. Can't see any error in server

Trying to send a volunteering form through SMTP using Gmail, Sendgrid or anything really however I have Action Mailer all set up and I presume I should really be seeing the email come through when I look at the email. I can see this error below but cannot see any error or receive any email at all. I've tried writing puts in my create action but I do not see those either so this is hopefully an easy fix as I'm missing something or have something not follow the usual Rails convention.
I'm using rails 5.2
What I see in the server:
Started GET "/volunteer?utf8=%E2%9C%93&authenticity_token=AZ%2FAflVf%2BlqRUYm45Jo82wAMpq%2B%2BY%2F93piLbeRXdK5n%2FQIWhuaUL3Oe2%2FSwzR%2FCLvj%2FAKW%2BBgD8dPd8vJYRDBA%3D%3D&volunteer_message%5Bname%5D=test+name&volunteer_message%5Bemail%5D=email%40test.com&volunteer_message%5Bphone_number%5D=88888888&volunteer_message%5Bbody%5D=test+email&commit=Send" for 127.0.0.1 at 2018-10-10 17:29:42 +0100
Processing by PagesController#volunteer as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"AZ/AflVf+lqRUYm45Jo82wAMpq++Y/93piLbeRXdK5n/QIWhuaUL3Oe2/SwzR/CLvj/AKW+BgD8dPd8vJYRDBA==", "volunteer_message"=>{"name"=>"test name", "email"=>"email#test.com", "phone_number"=>"88888888", "body"=>"test email"}, "commit"=>"Send"}
Ok so here's my code and the way it's all layed out.
PagesController.rb contains a page called volunteer and this contains the partial (Called _new.html.erb) which is the form which is in it's own folder called volunteer_messages
views/volunteer_messages/_new.html.erb
<%= simple_form_for #volunteer_message, url: create_volunteer_message_url do |f| %>
<%= #volunteer_message.errors.full_messages.join(', ') %>
<%= f.input :name, placeholder: 'name' %>
<%= f.input :email, placeholder: 'email' %>
<%= f.input :phone_number, placeholder: 'phone number' %>
<%= f.input :body, placeholder: 'body' %>
<%= f.submit 'Send' %>
<% end %>
routes created
# is this causing a problem do you think?
get '/volunteer' => 'pages#volunteer'
# volunteer message routes
get '/volunteer', to: 'volunteer_message#new', as: 'new_volunteer_message'
post '/volunteer', to: 'volunteer_message#create', as: 'create_volunteer_message'
models/volunteer_message.rb
class VolunteerMessage
include ActiveModel::Model
attr_accessor :name, :email, :phone_number, :body
# validates :name, :email, :phone_number, :body, presence: true
end
mailers/volunteer_message_mailer.rb
class VolunteerMessageMailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.volunteer_message_mailer.volunteer.subject
#
def volunteer(volunteer_message)
#body = volunteer_message.body
#subject = "Volunteer Request sent from www.myapp.com"
mail to: "myemail#example.com", from: volunteer_message.email
end
end
volunteer_messages_controller.rb
class VolunteerMessagesController < ApplicationController
def new
#volunteer_message = VolunteerMessage.new
end
def create
#volunteer_message = VolunteerMessage.new volunteer_message_params
if #volunteer_message.valid?
VolunteerMessageMailer.volunteer(#volunteer_message).deliver_now
redirect_to new_volunteer_message_url
flash.now[:notice] = "We have received your volunteer request and will be in touch soon!"
else
flash.now[:notice] = "There was an error sending your volunteer request. Please try again."
render :new
end
end
private
def volunteer_message_params
params.require(:volunteer_message).permit(:name, :email, :phone_number, :body)
end
end
pages_controller.rb
class PagesController < ApplicationController
def volunteer
#lead = Lead.new
#volunteer_message = VolunteerMessage.new
end
end
development.rb
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_deliveries = true
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'gmail.com',
enable_starttls_auto: true,
user_name: 'me#gmail.com',
password: 'gmailapppassword',
authentication: 'plain'
}
Not sure what else I can add here. I do not think I even need he stmp_settings to see action mailer actually show the template of the email being sent?
Also I'm guessing it is somehting to do with the form and the route because ive added puts messages into the create action on the volunteer_message controller and do not see them in my server log.
I'm not getting any errors when pressing submit. I do notice the params going into the url but that's it. it stays there and when refreshes stays on the same screen.
example:
http://localhost:3000/volunteer?utf8=%E2%9C%93&authenticity_token=AZ%2FAflVf%2BlqRUYm45Jo82wAMpq%2B%2BY%2F93piLbeRXdK5n%2FQIWhuaUL3Oe2%2FSwzR%2FCLvj%2FAKW%2BBgD8dPd8vJYRDBA%3D%3D&volunteer_message%5Bname%5D=test+name&volunteer_message%5Bemail%5D=email%40test.com&volunteer_message%5Bphone_number%5D=88888888&volunteer_message%5Bbody%5D=test+email&commit=Send
Is the clue in the server text in the image that shows params going to the pages controller. I do not really want that. Just want the partial to be there but the action to work of the create action in the volunteer_messages controller.
Any help be much appreciated. Thanks.

Rails 4: comment partial not rendering

Trying to display comments for photo, photo belongs to profile. html does not render.
The pertinent code:
routes.rb
resources :profiles do
resources :photos do
resources :comments do
resources :comments
end
end
end
comments/_comment.html.haml
= comments.each do |comment|
%li
= comment.body
\-
%small Submitted
= #{time_ago_in_words(comment.created_at)}
= semantic_form_for [#profile, #photo, comment, Comment.new] do |f|
= f.inputs do
= f.input :body, placeholder: "Add a Reply"
%br/
= f.actions do
= f.action :submit, :as => :input, label: "Reply"
%ul
- render partial: 'comments/comment', locals: {comments: comment.comments}
models/photo.rb
class Photo < ActiveRecord::Base
belongs_to :profile
has_many :comments, as: :commentable, :dependent => :destroy
end
models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :profile
belongs_to :commentable, polymorphic: true
has_many :comments, as: :commentable, :dependent => :destroy
end
models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
has_many :photos, :dependent => :destroy
has_many :comments, :dependent => :destroy
end
app/controllers/phtos_controller.rb
class PhotosController < ApplicationController
before_action :set_photo, only: [:show, :edit, :update, :destroy]
before_action :set_profile
load_and_authorize_resource
def index
#photos = Photo.where(:profile => #profile)
end
def show
end
def new
#photo = Photo.new(:profile => #profile)
end
def edit
end
def create
#photo = Photo.new(photo_params.merge(:profile_id => #profile.id))
respond_to do |format|
if #photo.save
format.html { redirect_to [#profile, #photo], notice: 'Photo was successfully created.' }
format.json { render :show, status: :created, location: #photo }
else
format.html { render :new }
format.json { render json: #photo.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #photo.update(photo_params)
format.html { redirect_to [#profile, #photo], notice: 'Photo was successfully updated.' }
format.json { render :show, status: :ok, location: #photo }
else
format.html { render :edit }
format.json { render json: #photo.errors, status: :unprocessable_entity }
end
end
end
def destroy
#photo.destroy
respond_to do |format|
format.html { redirect_to profile_photos_url, notice: 'Photo was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_photo
#photo = Photo.find(params[:id])
end
def set_profile
#profile = Profile.find(params[:profile_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def photo_params
params.require(:photo).permit(:description, :attachment)
end
end
/app/views/photos/show.html.haml
= render partial: "layouts/sub_header", locals: {heading: #photo.profile.name + "'s", sub_heading: "photo", current_bread_crumb: #photo.profile.name + "'s photo", include_crumbs: true}
/ Intro Content
.row
.col-md-6
= image_tag #photo.attachment.url(:large), :class => "img-responsive"
.col-md-6
%p
%h2 About this photo...
= simple_format(#photo.description)
,
/ /.row
%h3 Comments
= semantic_form_for [#profile, #photo, Comment.new] do |f|
= f.inputs do
= f.input :body, :as => :text
= f.actions do
= f.action :submit, :as => :input
%ul
- render partial: 'comments/comment', locals: {comments: #photo.comments}
- if current_user == #profile.user
= link_to 'Edit', edit_profile_photo_path(#profile, #photo)
|
= link_to 'Back', :back
Data is being inserted into the database (except the profile_id, but I will save that for another post). I manually updated the profile_id in the db to make see if it was just a data integrity issue, still nothing.
I have tried moving the resources around in the routes.rb, I have tried refactoring the views to load the collection directly without the partial, nothing seems to work.
Additionally, if I comment out the partial and use this code I do see comment bodies on the page, so it is definitely something I am doing wrong in the call to the partial or inside the partial itself.
%ul
- #photo.comments.each do |comment|
= comment.body
I cannot seem to crack this one and I know it isn't magic, but I clearly not seeing something.
Thanks for any help!
Change show.html.haml to:
%ul
- render 'comments/comment', locals: {comments: #photo.comments}
Reason being, is that you can't render a view within a view, so the above will assume you are looking for a partial named _comment.html.haml within the comments folder.
Thanks Marc and Jarvis for all your help, I still don't know why that wasn't working, but looking at the ActionView::PartialRender at api.rubyonrails.org I found that this does work...
- #photo.comments.each do |comment|
= render partial: 'comments/comment', locals: { comment: comment }
I basically have to do the iterating myself even though clearly in the guide Marc referenced it said I should be able to do what I had written.
Oh well, on to the next problem.
Thanks again!

rails 5 strong params updating record sets other columns to nil

For some reason, when I submit my form and update my user profile record, my user profile records have all the other columns updated to nil for some reason.
Lets say User.last.user_profile has an attribute "linkedin_url" and it is set to "www.yahoo.com". Once I submit the form, the User :phone number, and UserProfile :work_address, :work_zipcode gets updated, but the "linkedin_url" gets set to nil. IDK why!
class UserProfilesController < ApplicationController
def update
#user = User.last
if #user.update(user_profile_params)
respond_to do |format|
format.html {
redirect_to user_profile_path(#user)
}
format.json {
render :show, status: :ok, location: #preference
}
end
end
end
def user_profile_params
params.require(:user).permit( :phone_number, user_profile_attributes: [:work_address, :work_zipcode])
end
form
= form_for #user, url: user_profile_path(#user), method: :put do |f|
= f.fields_for :user_profile do |ff|
= ff.label :work_address
%br
= ff.text_field :work_address
%br
= ff.label :work_zipcode
%br
= ff.text_field :work_zipcode
%br
= f.label :phone_number
%br
= f.text_field :phone_number
%br
= f.submit "Submit", class: "btn"
def user_profile_params
params.require(:user).permit(
:phone_number,
user_profile_attributes: [:work_address, :work_zipcode] # ⇐ HERE
)
end
In the method above you should explicitly list :linkedin_url:
def user_profile_params
params.require(:user).permit(
:phone_number,
user_profile_attributes: [:work_address, :work_zipcode, :linkedin_url]
)
end
Just solved my own problem, it was because i didn't whitelist :id in the strong parameters.

Rails 4 nested forms link_to edit not working in loop

I have a nested routes/models/forms in rails. On my index page I am listing the todo_lists with the todo_items underneath. I want to be able to click on my todo list title and then it takes me to the edit page. I research polymorphic routes and nested routes.
UPDATE
This was my fix to stop it from creating the dummy todo list.
<% #current_todo_lists.each do |list| %>
<% if list.id %>
<div class="panel">
<p><strong><%= link_to list.title ,edit_todo_list_path(list)%></strong></p>
<% list.todo_items.each do |todo_item| %>
<p><%= todo_item.description %></p>
<% end %>
</div>
<% end %>
<% end %>
github link
Link_to rails nested form edit
polymorphic_path not generating correct path
I have done a lot quite a bit of research looking to cocoon, rails guides on polymorphic routes and several other stackoverflow links.
I have not been successful in making any of these work.
Here is the index page where all my todo_lists with todo_items are listed. It goes through a loop to list each todo list with the corresponding items created with it
Update:
I already tried <%= link_to list.title, edit_todo_list_path(list) %>and <%= link_to list.title, edit_todo_list_path(#list) %>.
The error message I get is :
ActionController::UrlGenerationError at /todo_lists
No route matches {:action=>"edit", :controller=>"todo_lists", :id=>nil} missing required keys: [:id]
This same configuration with #todo_list gives the same error.
Basically it can't find the Todo List with an id.
In the console does give me a result. So I am missing something.
>> t = TodoList.find(1)
=> #<TodoList id: 1, title: "First todo List with a modal", created_at: "2014-09-09 23:02:27", updated_at: "2014-09-09 23:02:27", user_id: 1>
>>
Update 2: This is where the error is happening in my todo list controller. It can't find without id.
def set_todo_list
#todo_list = TodoList.find(params[:id])
end
<% #todo_lists.each do |list| %>
<p><strong><%= link_to list.title, edit_polymorphic_path(#todo_list) %></strong></p>
<% list.todo_items.each do |todo_item| %>
<p><%= todo_item.description %></p>
<% end %>
<% end %>
So far <p><strong><%= link_to list.title, edit_polymorphic_path(#todo_list) % parameters have been #todo_list(s), # todo_lists(s), todo_items and so on.
Models:
class TodoList < ActiveRecord::Base
has_many :todo_items, dependent: :destroy
accepts_nested_attributes_for :todo_items, allow_destroy: true
validates_presence_of :title
end
class TodoItem < ActiveRecord::Base
belongs_to :todo_list
end
Controllers:
Class TodoListsController < ApplicationController
before_filter :authenticate_user!
before_filter except: [:index]
before_action :set_todo_list, only: [:show, :edit, :update, :destroy]
# GET /todo_lists
# GET /todo_lists.json
def index
##todo_lists = TodoList.all
#find current user todo lists/items
#todo_lists = current_user.todo_lists
#todo_items = current_user.todo_items
#create a new user todo list
#todo_list = current_user.todo_lists.new
# builder for todo list _form
3.times{ #todo_list.todo_items.build }
end
# GET /todo_lists/1
# GET /todo_lists/1.json
def show
end
# # GET /todo_lists/new
def new
#todo_list = current_user.todo_lists.new
3.times{ #todo_list.todo_items.build }
end
# GET /todo_lists/1/edit
def edit
##todo_list = TodoList.find(todo_list_params)
#todo_list = TodoList.find(params[:id])
end
# POST /todo_lists
# POST /todo_lists.json
def create
##todo_list = TodoList.new(todo_list_params)
#todo_list = current_user.todo_lists.new(todo_list_params)
respond_to do |format|
if #todo_list.save
format.html { redirect_to #todo_list, notice: 'Todo list was successfully created.' }
format.json { render :show, status: :created, location: #todo_list }
else
format.html { render :new }
format.json { render json: #todo_list.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /todo_lists/1
# PATCH/PUT /todo_lists/1.json
def update
#todo_list = TodoList.find(params[:id])
respond_to do |format|
if #todo_list.update(todo_list_params)
format.html { redirect_to #todo_list, notice: 'Todo list was successfully updated.' }
format.json { render :show, status: :ok, location: #todo_list }
else
format.html { render :edit }
format.json { render json: #todo_list.errors, status: :unprocessable_entity }
end
end
end
# DELETE /todo_lists/1
# DELETE /todo_lists/1.json
def destroy
##todo_list.TodoList.find(params[:id])
#todo_list.destroy
respond_to do |format|
format.html { redirect_to todo_lists_url, notice: 'Todo list was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def owns_todolist
if current_user != TodoList.find(params[:id]).user
redirect_to todo_lists_path, error: "You can't do that!"
end
end
# Use callbacks to share common setup or constraints between actions.
def set_todo_list
#todo_list = TodoList.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def todo_list_params
params.require(:todo_list).permit(:title, todo_items_attributes: [:description, :_destroy])
end
end
class TodoItemsController < ApplicationController
before_action :set_todo_item, only: [:show, :edit, :update, :destroy]
before_action :set_todo_list
# GET /todo_items
# GET /todo_items.json
def index
#todo_items = TodoItem.all
end
# GET /todo_items/1
# GET /todo_items/1.json
def show
#todo_item = TodoItem.find(params[:id])
end
# GET /todo_items/new
def new
#todo_item = #todo_list.todo_items.build
end
# GET /todo_items/1/edit
def edit
#todo_item = TodoItem.find(params[:id])
end
# POST /todo_items
# POST /todo_items.json
def create
#todo_item = #todo_list.todo_items.build(todo_item_params)
respond_to do |format|
if #todo_item.save
format.html { redirect_to [#todo_list,#todo_item], notice: 'Todo item was successfully created.' }
format.json { render :show, status: :created, location: #todo_item }
else
format.html { render :new }
format.json { render json: #todo_item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /todo_items/1
# PATCH/PUT /todo_items/1.json
def update
#todo_item = TodoItem.find(params[:id])
respond_to do |format|
if #todo_item.update(todo_item_params)
format.html { redirect_to #todo_item, notice: 'Todo item was successfully updated.' }
format.json { render :show, status: :ok, location: #todo_item }
else
format.html { render :edit }
format.json { render json: #todo_item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /todo_items/1
# DELETE /todo_items/1.json
def destroy
#todo_item = TodoItem.find(params[:id])
#todo_item.destroy
respond_to do |format|
format.html { redirect_to todo_list_todo_items_url, notice: 'Todo item was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_todo_item
#todo_item = TodoItem.find(params[:id])
end
def set_todo_list
#todo_list = TodoList.find(params[:todo_list_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def todo_item_params
params.require(:todo_item).permit(:description, :text, :todo_list_id)
end
end
and finally the form. Right now it allows for adding a todo_list and a few todo_items just as practice. I plan on using some Ajax to allow for dynamic creation later on. And having a different form for editing.
<%= form_for(#todo_list) do |f| %>
<% if #todo_list.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#todo_list.errors.count, "error") %> prohibited this todo_list from being saved:</h2>
<ul>
<% #todo_list.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div>
<%= f.fields_for :todo_items do |builder| %>
<%= builder.label :description, "Items" %>
<%= builder.text_field :description %>
<%= builder.check_box '_destroy' %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I think the issue's actually a little trickier, because edit_todo_list_path(list) seems to throw the same error.
What's going on is that the #todo_lists variable (first an array of persisted lists) is altered when you run #todo_list = current_user.todo_lists.new. That command actually adds a new (unpersisted) list to the end of the #todo_lists array (seems like buggy behavior to me, but it's happened to me before), so that when your view is looping through them, the last one has no id, and a path cannot be created for it.
The solution is (I think) to make that variable after you've used the #todo_lists variable.
Take #todo_list out of the controller, and where you use it in the view, instead do current_user.todo_lists.build. That should instantiate a new list without changing the #todo_lists variable.
You are trying to create a link to a resource that has not been persisted yet:
#create a new user todo list
#todo_list = current_user.todo_lists.new
Since you never call #todo_list.save the record does not have a ID and cannot be routed to.
I think what you are trying to do is:
<% #todo_lists.each do |list| %>
<p><strong><%= link_to list.title, edit_polymorphic_path(id: list.to_param) #not #todo_list! %></strong></p>
<% list.todo_items.each do |todo_item| %>
<p><%= todo_item.description %></p>
<% end %>
<% end %>
And I would seriously consider renaming #todo_list -> #new_todo_list since it´s pretty confusing as is.
Update
"I can even click a button to open a modal which will create a new todo list all from the same page."
What you should do then is have the user create a new a resource by posting the form with an AJAX POST request to /todo_list and then update the page with the new list (and an edit link).
What you are doing now is creating a new todo_list which only exists in the servers memory before rails finishes processing the request.

Rails3 devise / omniauth - password_required? and form_for :validate => true don't work together

I'm using devise, omniauth and client-side-validation like described in ryan bates railscasts.
I'm now facing a problem with the password validation which should be omitted when registering via omniauth 3rd party provider.
Registration form (html.erb):
Sign up Register
via 3rd party networks <%= form_for(resource, :as => resource_name, :url =>
registration_path(resource_name), :validate => true) do |f| %>
Register directly <%= f.label :firstname %> <%=
f.text_field :firstname %> <%= f.label :lastname %> <%=
f.text_field :lastname %> <%= f.label :email %> <%=
f.text_field :email %> <%= f.label :password %> <%=
f.password_field :password %> <%= f.label
:password_confirmation %> <%= f.password_field :password_confirmation
%> <%= f.submit "Register now", :class => "button-big" %>
<% end %>
My user model has a - see UPDATE below
validates :password, :presence => true, :confirmation =>true
clause and the password_required? definition
def password_required?
(authentications.empty? || !password.blank?)
end
When I'm registering via omniauth 3rd party provider the registration form correctly pops up and the user is asked to enter an email address. Unfortunately the user has to enter a password although he shouldn't be prompted due to registration via 3rd party.
Can anybody give me a hint, how to accomplish?
Thanks and best regards
Jan
UPDATE: to give a more specific view I added some more code snippets
AuthenticationsController:
class AuthenticationsController < ApplicationController
def index
#authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
#authentication = current_user.authentications.find(params[:id])
#authentication.destroy
flash[:notice] = "Successfully destroyed authentication."
redirect_to authentications_url
end
end
RegistrationsController:
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
User Model:
class User < ActiveRecord::Base
# associations
has_many :authentications
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, :email_regexp => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
# Setup accessible (or protected) attributes for your model
attr_accessible :firstname, :lastname, :email, :password, :password_confirmation, :remember_me
# validations
validates :firstname, :presence => true
validates :lastname, :presence => true
validates :email, :presence => true, :uniqueness => true
validates_email :email
validates :password, :presence => true, :confirmation =>true
# omniauth reference 3rd party
def apply_omniauth(omniauth)
if self.firstname.blank?
self.firstname = omniauth['info']['first_name'].presence || omniauth['info']['name'].presence || " "
end
if self.lastname.blank?
self.lastname = omniauth['info']['last_name'].presence || omniauth['info']['name'].presence || " "
end
if self.email.blank?
self.email = omniauth['info']['email'].presence
end
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
# omit validation for omniauth session
def password_required?
(authentications.empty? || !password.blank?) && super
end
end
Authentication Model
class Authentication < ActiveRecord::Base
# associations
belongs_to :user
# Setup accessible (or protected) attributes for your model
attr_accessible :user_id, :provider, :uid
end
While debugging I found out that the 'authentications.build()' in the 'apply_omniauth(omniauth)' method produces an empty object, so that the 'password_required?' is always be true and a password must be provided.
ADDITIONAL QUESTION:
why does 'authentications.empty?' always return 'false'?
Thanks in advance
Jan
First of all, the password_required? method should be defined like this:
def password_required?
(authentications.empty? || !password.blank?) && super
end
Then, in your view, you should wrap your password fields like this:
# app/views/registrations/new.html.erb
<% if #user.password_required? %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<% else %>
...
<% end %>
Then in your controller or model, when creating an authentication method for the first time, you should use build and not create or new because that way authentications.empty? will return true and you'll be asked to enter a password.
Finally, as for validations, you should take a look at the client_side_validations' wiki about custom validations. You'll probably need to override the password validation.

Resources