Using Wicked with Devise for a sign up wizard - ruby-on-rails

I am using devise with wicked to create a sign up wizard, but I am unsure about a problem I am having creating profiles. After a user provides their email & password they are forwarded to a step to create a profile based on whether they have specified they are a shipper or carrier. However I am unsure what the code should be in the controller and the forms to generically create a profile. Here is the code I have for the application:
The steps controller:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :carrier_profile, :shipper_profile
def create
#user = User.last
case step
when :carrier_profile
#profile = CarrierProfile.create!(:dot => params[:dot])
if #profile.save
render_wizard #user
else
flash[:alert] = "Record not saved"
end
when :shipper_profile
#profile = ShipperProfile.create!(params[:shipper_profile)
if #profile.save
render_wizard #user
else
flash[:alert] = "Record not saved"
end
end
end
end
end
def show
#user = User.last
#carrier_profile = CarrierProfile.new
#shipper_profile = ShipperProfile.new
case step
when :carrier_profile
skip_step if #user.shipper?
when :shipper_profile
skip_step if #user.carrier?
end
render_wizard
end
end
The form for a carrier profile:
<% form_for #carrier_profile , url: wizard_path, method: :post do |f| %>
<div>
<%= f.label :dot, "Please enter your DOT Number:" %>
<%= f.text_field :dot %>
</div>
<%= f.submit "Next Step", class: "btn btn-primary" %>
<% end %>
The form for a shipper profile:
<% form_for #shipper_profile , url: wizard_path, method: :post do |f| %>
<div>
<%= f.label :company_name, "What is your company name?" %>
<%= f.text_field :company_name %>
</div>
<%= f.submit "Next Step", class: "btn btn-primary" %>
<% end %>
The user model:
class User < ActiveRecord::Base
has_one :carrier_profile
has_one :shipper_profile
end
How would I be able to write a generic new and create method to handle creating both profiles? With the current code it is stating that the user_steps controller has no POST method, although if I run rake routes I find that this is untrue.

Add a step :profile_select to find which profile should be created
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :profile_select, :carrier_profile, :shipper_profile
def update
#user = current_user
case step
when :profile_select
if params[:user][:profile_type] == 'carrier'
#profile = current_user.carrier_profile.build
else
#profile = current_user.shipper_profile.build
end
when :carrier_profile
current_user.carrier_profile.update_attributes(params[:carrier_profile])
when :shipper_profile
current_user.shipper_profile.update_attributes(params[:shipper_profile])
end
render_wizard #user
end
def show
#user = current_user
case step
when :carrier_profile
skip_step if #user.shipper?
when :shipper_profile
skip_step if #user.carrier?
end
render_wizard
end
end
views/user_steps/profile_select.html.erb:
Note: method :put so that update gets called for all steps
<%= form_for current_user, url: wizard_path, method: :put do |f| %>
<%= f.label :profile_type %>
<%= f.text_field :profile_type %>
<%= f.submit "Next Step", class: "btn btn-primary" %>
<% end %>
views/user_steps/carrier_profile.html.erb:
<% form_for #profile, url: wizard_path, method: :put do |f| %>
<div>
<%= f.label :dot, "Please enter your DOT Number:" %>
<%= f.text_field :dot %>
</div>
<%= f.submit "Next Step", class: "btn btn-primary" %>
<% end %>
views/user_steps/shipper_profile.html.erb:
<% form_for #profile, url: wizard_path, method: :put do |f| %>
<div>
<%= f.label :company_name, "What is your company name?" %>
<%= f.text_field :company_name %>
</div>
<%= f.submit "Next Step", class: "btn btn-primary" %>
<% end %>

Related

Add a join key to a group

I'm creating an app to store game results between friends. In my app groups will be called Leagues.
To keep the leagues private I'd like to add a join key to it. When a league is created the creator chooses a join key. Later other users can join by choosing a league from a dropdown and filling in the exact join key. The user can only if the join key matches the selected league.
Controller:
def edit
#user = User.find(current_user.id)
#league_list = League.all.map{|l| [ l.league_name, l.id ] }
# #league_keys = League.all.map{|l| [ l.join_key, l.id ] }
end
def update
#user = User.find(current_user.id)
if #user.update(user_join_league_params) #&& #league_list.league_id == #league_keys.league_id
#user.save
redirect_to root_path, :notice => "Successfully joined this league!"
else
render 'index'
end
end
As you can see I have to edit the users league_id to complete the join.
View:
<div class="panel panel-default" style="margin-right:10px; margin-left:10px; text-align:center">
<div class="panel-heading">
JOIN A LEAGUE
</div>
<div class="panel-body">
<%= simple_form_for(#user) do |f| %>
<%= f.select(:league_id, #league_list) %><br> <br>
# <%= f.input_field :join_key %><br> <br>
<p>!! Watch out this cannot be changed after !!</p>
<br> <br> <%= f.submit "Join league", class: "btn-submit" %>
<% end %>
</div>
</div>
So I'd like this form to only perform the update action if the selected league matches the filled in key.
I've been looking for an answer in numerous posts on stackoverflow but I couldn't find a post with a simular issue.
How can I get this done?
EDIT:
Relations:
class User < ActiveRecord::Base
belongs_to :league
end
class League < ActiveRecord::Base
has_many :games
has_many :multiplayergames
has_many :users
end
SECOND EDIT:
So I've tried to implement the solloution.
I was using simple_form for <%= f.select(:league_id, #league_list) %>
I tried to do it like this:
<%= form_tag(user_path(#user), method: :put) do %>
<%= select_tag :league_id, options_for_select(#league_list) %><br> <br>
<p>Join Key</p>
<%= text_field_tag :join_key %><br> <br>
<p>!! Watch out this cannot be changed after !!</p>
<br> <br> <%= submit_tag "Join league", class: "btn-submit" %>
<% end %>
However this gives the error:
param is missing or the value is empty: user
THIRD EDIT:
Controller:
# JOIN A LEAGUE
def edit
#user = User.find(current_user.id)
#league_list = League.all.map{|l| [ l.league_name, l.id ] }
end
def update
#user = User.find(current_user.id)
#league = League.find_by_id(params[:league_id])
if #league.join_key == params[:join_key] && #user.update(user_join_league_params)
redirect_to root_path, :notice => "Successfully joined this league!"
else
redirect_to edit_user_path(current_user), notice: "Could not join league check if join key is correct!"
end
end
def user_join_league_params
params.permit!(:league_id,:join_key)
end
VIEW:
<%= form_tag(user_path(#user), method: :put) do %>
<%= select_tag :league_id, options_for_select(#league_list, :league_id) %><br> <br>
<p>Join Key</p>
<%= text_field_tag :join_key %><br> <br>
<p>!! Watch out this cannot be changed after !!</p>
<br> <br> <%= submit_tag "Join league", class: "btn-submit" %>
<% end %>
update action can look as follows:
def update
#user = User.find(current_user.id)
#league = League.find_by_id(params[:league_id])
if #league.join_key == params[:join_key] && #user.update(league_id: params[:league_id])
redirect_to root_path, :notice => "Successfully joined this league!"
else
render 'index'
end
end
form can look as follows:
<%= form_tag(user_path(#user), method: :put) do %>
<%= select_tag :league_id, options_for_select(#league_list) %><br> <br>
<%= text_field_tag :join_key %><br> <br>
<p>!! Watch out this cannot be changed after !!</p>
<br> <br> <%= submit_tag "Join league", class: "btn-submit" %>
<% end %>
Note: I have assumed join_key is an attribute in your League model.
Make sure you pass params accordingly in your user_join_league_params.
Edit:
Your should permit your params:
def user_join_league_params
params.permit!(:league_id,:join_key)
end

Ruby Incorrect link generation redirect_to products_path(#product) generate /products.1 instead of /products/1

I'm trying to add a reviews on my single product page. But when I click Submit - It takes me to the /products.1 page, instead of /products/1
class CommentsController < ApplicationController
def create
#product = Product.find(params[:product_id])
#comment = #product.comments.new(comment_params)
#comment.user = current_user
#comment.save
redirect_to products_path(#product)
end
def destroy
end
private
def comment_params
params.require(:comment).permit(:user_id, :body, :rating)
end
end
and the comment.html.erb
<div class="row">
<div class="col-sm-6">
<% if signed_in? %>
<h4>Add a review:</h4>
<%= form_for([#product, #product.comments.build]) do |f| %>
<p>
<%= f.label :body, "Comment" %><br>
<%= f.text_area :body, class: "form-control" %>
</p>
<p>
<%= f.label :rating %><br>
<%= f.text_field :rating, class: "rating form-control" %>
</p>
<p>
<%= f.submit "Submit", class: "btn" %>
</p>
<% end %>
<% end %>
</div>
</div>
Try redirect_to #product instead of redirect_to products_path(#product).
Did you check your routes.rb under config? Try running rake routes in the terminal and you can debug from there.

im stuck at assert_template 'bookings/show' test in rails.

I'm working on a simple project just to get a deeper understanding of rails and I'm stuck at a simple test assert_template 'bookings/show' which lead to this error, i have been checking many times, can't find a clue :
expecting <"bookings/show"> but rendering with <[]>
test/integration/booking_submit_test.rb:17:
The related code is on below:
test/integration/booking_submit_test.rb
require 'test_helper'
class BookingSubmitTest < ActionDispatch::IntegrationTest
test "invalid booking submit information" do
get booknow_path
assert_no_difference 'Booking.count' do
post bookings_path, booking: {date_of_tour: "2017-05-06", hotel_name: "xxx hotel", phone_number:123456 , number_of_pax:34 , pick_up_time: "9:00"}
end
assert_template 'bookings/new'
end
test "valid booking submit information" do
get booknow_path
assert_difference 'Booking.count', 1 do
post bookings_path, booking: {date_of_tour: "2017-05-06", hotel_name: "xxx hotel", phone_number:12345678901 , number_of_pax:34 , pick_up_time: "9:00"}
end
assert_template 'bookings/show'
end
end
show.html.rb:
<% provide(:title, #booking.date_of_tour) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= #booking.hotel_name %>
</h1>
</section>
</aside>
</div>
new.html.rb:
<% provide(:title, 'Book the package now') %>
<h1>Book now</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#booking) do |f| %>
<%= render 'shared/errorbooking_messages' %>
<%= f.label :date_of_tour %>
<%= f.text_field :date_of_tour, class: 'form-control' %>
<%= f.label :hotel_name %>
<%= f.text_field :hotel_name, class: 'form-control' %>
<%= f.label :phone_number %>
<%= f.text_field :phone_number, class: 'form-control' %>
<%= f.label :number_of_pax %>
<%= f.text_field :number_of_pax, class: 'form-control' %>
<%= f.label :pick_up_time %>
<%= f.text_field :pick_up_time, class: 'form-control' %>
<%= f.submit "Book now", class: "btn btn-primary" %>
<% end %>
</div>
</div>
bookings_controller.rb:
class BookingsController < ApplicationController
def show
#booking = Booking.find(params[:id])
end
def new
#booking = Booking.new
end
def create
#booking = Booking.new(booking_params) # Not the final implementation!
if #booking.save
flash[:success] = "You have submited the information successfully!"
redirect_to #booking
else
render 'new'
end
def edit
#booking = Booking.find(params[:id])
end
end
private
def booking_params
params.require(:booking).permit(:date_of_tour, :hotel_name, :phone_number, :number_of_pax, :pick_up_time )
end
end
routes.rb
get 'booknow' => 'bookings#new'
resources :bookings
When the input is correct, the action executes a redirect_to, so the test you should use assert_redirected_to :
test "valid booking submit information" do
get booknow_path
assert_difference 'Booking.count', 1 do
post bookings_path, booking: {date_of_tour: "2017-05-06", hotel_name: "xxx hotel", phone_number:12345678901 , number_of_pax:34 , pick_up_time: "9:00"}
end
assert_redirected_to booking_path(assigns(:booking))
end

Ruby / ActionMailer / Saving mail

I am creating a customer support app where clients can create, view, edit and comment support tickets.
I have this portion of the app working fine, but I want to have the data they submit into the ticket form emailed to me.
I have a separate "contact us" form that emails the data to me perfectly, but I want to combine the two forms into one.
It should work like this: client creates ticket, ticket is saved into the database, a copy of the ticket is emailed to me.
I can't figure out how to make all of these actions happen from one form.
Here is my tickets controller:
class TicketsController < ApplicationController
def new
#ticket = Ticket.new
end
def create
#ticket = Ticket.new(ticket_params)
#ticket.save
redirect_to #ticket
end
def show
#ticket = Ticket.find(params[:id])
end
def index
#tickets = Ticket.all
end
def edit
#ticket = Ticket.find(params[:id])
end
def update
#ticket = Ticket.find(params[:id])
if #ticket.update(ticket_params)
redirect_to #ticket
else
render 'edit'
end
end
def destroy
#ticket = Ticket.find(params[:id])
#ticket.destroy
redirect_to tickets_path
end
private
def ticket_params
params.require(:ticket).permit(:name, :email, :phone, :help)
end
end
Here is my new ticket view:
<%= link_to "View an Existing Ticket", tickets_path, :class =>'btn btn-danger btn-sm'%>
<h1>New Ticket</h1>
<%= form_for :ticket, url: tickets_path do |f| %>
<p>
<%= f.label "Name:" %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label "Email:" %>
<%= f.text_field :email %>
</p>
<p>
<%= f.label :"Phone #:" %>
<%= f.text_field :phone %>
</p>
<p>
<%= f.label :"How can we help?" %>
<p><%= f.text_area :help, :cols=> 38, :rows => 8 %></p>
</p>
<p>
<button type="submit" class="btn btn-danger btn-sm">Submit Ticket</button>
</p>
<% end %>
<p><%= button_to "Back", root_path, :class => "btn btn-danger btn-sm", :method => :get %></p>
Here is my email controller:
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
Here is my email view:
<%= form_for #message, :url => contactcreate_path do |form| %>
<fieldset class="fields">
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<div class="field">
<%= form.label :subject %>
<%= form.text_field :subject %>
</div>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
</fieldset>
<fieldset class="actions">
<%= form.submit "Send" %>
</fieldset>
<% end %>
Ticket Model:
class Ticket < ActiveRecord::Base
after_create :send_new_ticket_to_email
private
def send_new_ticket_to_email
NotificationsMailer.send_new_ticket(self).deliver
end
end
Notifications Mailer:
class NotificationsMailer < ActionMailer::Base
def send_new_ticket(ticket)
#ticket = ticket
mail(:subject => "HelpDesk: #{message.subject}")
default :from => "HelpDeskApp#ascendstudioslive.com"
default :to => "Support#ascendstudioslive.com"
end
Let me know if there is anything else you would like to see. Basically, I want to have one form that saves a ticket to the database and then emails a copy of it out.
Thank you!
You can create an after_create callback in your Ticket model to e-mail the saved ticket to yourself.
class Ticket < ActiveRecord::Base
after_create :send_new_ticket_to_email
private
def send_new_ticket_to_email
UserMailer.send_new_ticket(self).deliver
end
end
and in your ActionMailer class:
class UserMailer < ActionMailer::Base
def send_new_ticket(ticket)
#ticket = ticket
/* here you configure the variables for your email */
mail(to: your#email.com, subject: 'New ticket...')
end
end
then you will be able to use the #ticket object in your mailer views whatever way you please.
Have you tried this? I don't know if it is going to work, but all the same:
#new_message = NotificationsMailer.new_message(#message)
save_to_db(#new_message) # custom method to write it into your db somehow
#new_message.deliver
Not really sure what a contact is versus a ticket, but, broadly, the way to do what you want is, in your create action:
def create
#message = Message.create(params[:message])) # create will save and validate the message
if #message.valid?
NotificationsMailer.new_message(#message).deliver # #message gets set to
# else...
end

Form_for helper

I have 2 models that are associated with each other via a join table:
class Book < ActiveRecord::Base
has_many :reviews
end
class Reader < ActiveRecord::Base
has_many :reviews
end
class Reviews < ActiveRecord::Base
belongs_to :reader
belongs_to :book
end
Right now, I am able to update a review (which I created manually in the console) on route:
readers/:id/books
The above route was create using rails' member method:
resources :readers
member do
get 'books'
end
end
The update action in reviews controller (reviews#update) is defined like so:
def update
#reader = current_reader
#review = Review.find_by_reader_id(#reader.id)
#book = Book.find(params[:review][:book]
if #reader.books.include?(#book)
#review.update_attributes(review_params)
redirect_to (#reader)
else
flash[:error] = 'You can only edit reviews that belong to you'
end
end
My form_for reviews (reviews#update) looks like this:
Reader Reviews:
<% book.reviews.each do |review| %>
<% if current_reader == (review.reader) %>
<%= review.content %> written by <%= review.reader.name %>
<% if current_reader.reviews.include?(review) %>
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Update", class: "btn btn-large btn-primary" %>
<% else %>
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
<% end %>
<% end %>
The above works for update. But the 2nd form doesn't.
My intent is to check for a review => if there is one - display a form so that reader can update review; if there isn't one then display a form so that reader can create a review. I have a private method in reviews controller that checks to make sure that a reader has a book before either action is carried out (a before_action method I guess).
The first form works well (the update form) but the second does not - the form is not displayed at all. I have tried various things to get the form to display but no luck. Can you please me determine the best way to resolve this issue?
Thank you very much!
There could be several issues:
elsif
The likely reason your other form won't show will be that your elsif logic won't be correct
I had a look at the .include? Ruby function, and it seems to just be for arrays. Why not try using .exists? instead?
<% elseif !current_reader.reviews.exists?(review) %>
You may have to use review.id or similar to get the correct response. Failing that, why don't you just use <% else %>?
form_for
The second issue may be with your second form_for
<%= form_for [:book, review] do |f| %>
You're currently passing a local variable called book to the form_for builder. Although this is probably correct (I can't find your reference to creating book), I've found it best to put a symbol in the nested form (to show Rails which data it needs to use)
Could you try using else instead of elsif !current_reader.reviews.include?(review)?
Also, it's elsif and not elseif. The problem should not because of this - The page would not have loaded in the first place if this is the case.
UPDATE
I fixed my first error by updating my forms:
Reader Reviews:
<% book.reviews.where(reader_id: current_reader.id).each do |review| %>
<li>
<span><%= review.content %> writen by <%= review.reader.name %> </span
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Update", class: "btn btn-large btn-primary" %>
<% end %>
</li>
<% if book.reviews.where(reader_id: current_reader.id).size == 0 %>
<%= form_for ([book, book.reviews.build]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
<% end %>
This displays the forms (both post and update). BUT I got this error when I tried to post a new review:
(rdb:35) #review
#<Review id: nil, reader_id: 101, content: "Trial", created_at: nil, updated_at: nil, book_id: nil>
(rdb:35) review_params
Unpermitted parameters: book
{"content"=>"Trial"}
(rdb:35)
So I changed my create action for review to make sure book_id isn't nill:
def create
#reader = current_reader
# #book = Book.find(params[:book_id])
#book = Book.find(params[:review][:book])
if #reader.reviews.where(book_id: #book.id).exists?
flash[:error] = "You already reviewed this book"
else
#review = current_reader.reviews.create(:book_id => params[:book_id.to_i, :content => review_params[:content])
debugger
if #review.save
flash[:success] = "Review created"
redirect_to reader_path(#reader)
else
flash[:error] = "You can only review books that are in your library"
redirect_to reader_path(#reader)
end
end
end
Also changed how I defined review_params:
def review_params
params.require(:review).permit(:content, :book_id)
end
All this changes gave the desired results. My code isn't dry AT ALL but the most important thing to me at this point is getting things to work. Here is to hoping I don't break it again. Thanks for your help #RichPeck

Resources