I am relatively new to Rails. I have created a simple app, using Ruby 2.5 & Rails 5.2, that will send an email to an admin when a website visitor requests some information. The app does not use a database, because none of the visitor's information is to be saved or stored, therefore ActiveRecord is not used. Just need to get some information from the visitor via a form and email it. Research suggests using ActiveModel instead but I still can't get it to work. No 3rd party mailing apps needed. What am I overlooking? Model, Controller, Views, Mailer code appear below.
#Model: Visitor.rb
class Visitor
include ActiveModel::Model
include ActiveModel::Conversion
extend ActiveModel::Naming
include ActiveModel::Validations
attr_accessor :fname, :lname, :phone, :email, :interest
# Validations & Requirements
validates :fname, presence: true, length: { maximum: 50 }
validates :lname, presence: true, length: { maximum: 50 }
validates :lname, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
validates :interest, presence: true
def initialize(fname, lname, phone, email, interest)
#fname = fname
#lname = lname
#phone = phone
#email = email
#interest = interest
post_initialize
end
# #make in lieu of #create
def self.make(fname, lname, phone, email, interest)
attributes = {fname: fname, lname: lname, phone: phone, email: email, interest: interest}
object = new(attributes)
object.save
#visitor = object
#visitor
end
def persisted?
false
end
def send_mail
# Following method defined in NotifierMailer
NotifierMailer.visitor_info(self).deliver_now
end
private
def post_initialize
if email
email.downcase
end
end
end
# Controller: Visitors_controller.rb
class VisitorsController < ApplicationController
def new
#Visitor = Visitor.new
end
def make
#visitor = Visitor.new(visitor_params)
if #visitor.valid?
#visitor.send_mail # Instance method in Guest model
flash[:notice] = "Your request has been sent! Thank you for contacting us."
redirect_to welcome_path
else
render 'new'
end
end
private
def visitor_params
params.require(:visitor).permit(:fname, :lname, :phone, :email, :interest)
end
end
# Routes: Routes.rb
Rails.application.routes.draw do
get 'favicon', to: 'pages#favicon'
root to: 'pages#index', as: 'welcome'
# ... other routes for static page navigation
get '/reqinfo', to: 'visitors#new'
post '/reqinfo', to: 'visitors#make'
end
# View: New.html.erb
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with model: #visitor, url: reqinfo_path, local: true do |form| %>
<%= render 'layouts/error_messages', object: form.object %>
<%= form.label 'First Name' %>
<%= form.text_field :fname, class: 'form-control' %>
<%= form.label 'Last Name' %>
<%= form.text_field :lname, class: 'form-control' %>
<%= form.label :phone %>
<%= form.text_field :phone, class: 'form-control' %>
<%= form.label 'Email Address' %>
<%= form.text_field :email, class: 'form-control' %>
<%= form.label 'Interest' %>
<%= form.text_field :interest, class 'form-control' %>
<%= form.submit 'Submit', id: "button" %>
<% end %><!-- form_with -->
</div><!-- col-md6-3 -->
</div><!-- row -->
# Mailer: Notifier_mailer.rb
class NotifierMailer < ApplicationMailer
default from: 'noreply#thecompany.com'
def guest_info(visitor)
#visitor = visitor
mail(to: 'admin#thecompany.com', subject: "Website Visitors Request for Information")
end
end
# Message: Visitor_info.html.erb
<h1>Dear Admin;</h1>
<br>
<% timestamp = Time.now %>
<p>
<%= visitor.fname %> <%= #visitor.lname %> visited the TheCompany website on <%= timestamp.localtime %>. He/she is requesting information regarding a <%= #visitor.interest %>. Their contact information phone: <%= #visitor.phone %> and email: <%= #visitor.email %>
</p>
Related
Hi I have a simple app that I am building and am having trouble getting the error messages to appear when someone inputs invalid information or no information at all into a field.
The form I am using is to sign up a new user, the code associated with the user and form are below;
users_controller.rb
Class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#country = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
user.rb
class User < ApplicationRecord
before_save { self.email = email.downcase }
validates :first_name, presence: true, length: { maximum: 25 }
validates :first_name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
has_many :trips
has_many :countries, through: :trips
end
new.html.erb
<div class="container">
<h1 class="text-center">Sign up</h1>
<div class="row">
<div class="col-md-6 offset-md-3 ">
<%=form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: "form-control" %>
<%= f.label :last_name %>
<%= f.text_field :last_name, class: "form-control" %>
<%= f.label :email %>
<%= f.email_field :email, class: "form-control" %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.submit "Create an account", class: 'form-control btn btn-primary' %>
<% end %>
</div>
</div>
</div>
_error_messages.html.erb
<% if #user.errors.any? %>
<div class="alert alert-danger">
The form contains <%= pluralize(#user.errors.count, "error") %>.
</div>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% else %>
<h3>test</h3>
<% end %>
When I load the form I do see the "Test" string, which I put in my _error_messages.html.erb for visibility. However when I enter data in the signup page, it reloads the page (as it should rather than sending it to the user page when all fields are valid). However, the "Test" string still appears at the top rather than the error messages that should.
My assumption is I need some sort of session or something to remember what the errors were, as right now it reloads a completely new page with nothing in memory, however, I cannot find the solution to this anywhere at the moment.
Any help would be much appreciated!
As I said, you need to change
redirect_to '/signup'
to
render 'new'
From the Guides
The render method is used so that the #user object is passed back
to the new template when it is rendered. This rendering is done within
the same request as the form submission, whereas the redirect_to will
tell the browser to issue another request.
That said, so as the redirect_to issues a new request to the browser, the values of #user is lost, in other words #user is a new instance that is instantiated again. That is why <% if #user.errors.any? %> always returns false as if there are no errors in #user.
I have the below contact form and I would like to allow customers to send me an image as an attachment. I have added the field to the form but don't understand the following -
What migration do I create?
How do I attach the image to the
mailer?
View
<%= simple_form_for #inquiry, :method => :post do |f| %>
<%= f.input :spam, as: :hidden %>
<%= f.input :name %>
<%= f.input :phone %>
<%= f.input :email %>
<%= f.input :message %>
<%= f.file_field :image %> ## Attachment input here
<%= f.button :submit, 'Send' %>
<% end %>
Controller
def new
#inquiry = Inquiry.new
end
def create
redirect_to new_inquiry_path and return if params[:spam].present?
#inquiry = Inquiry.new(inquiry_params)
if #inquiry.valid?
InquiryMailer.admin(#inquiry).deliver
redirect_to inquiries_path
else
render :new
end
end
private
def inquiry_params
params.require(:inquiry).permit(:name, :email, :phone, :message)
end
Model
validates :name, presence: true
validates :email, presence: true
validates :phone, presence: true
validates :message, presence: true
inquiry_mailer.rb
class InquiryMailer < ActionMailer::Base
default from: "noreply#foo.com"
def admin(inquiry)
#inquiry = inquiry
mail to: "hello#foo.co.uk", subject: "Website Inquiry"
end
end
admin.text.erb
Website Inquiry
=========
Name: <%= #inquiry.name %>
Phone: <%= #inquiry.name %>
E-Mail: <%= #inquiry.email %>
Message: <%= #inquiry.message %>
You can make use of ActionMailer here
Ill provide an answer based upon your existing setup
You need to edit your mailer to look something like this
class InquiryMailer < ActionMailer::Base
default from: ENV['EMAIL_ADDRESS'] #I like to keep my email address hidden
def admin(inquiry)
#inquiry= inquiry
if inquiry.file
attachment_name = inquiry.file.original_filename
attachments[attachment_name] = inquiry.file.read
end
mail(to: ENV['EMAIL_ADDRESS'], subject: 'Website Inquiry')
end
end
You can keep admin.text.erb as it is and your controller stays the same
and within your form_for dont forget to add
<%= simple_form_for #inquiry, :method => :post, :multipart => true do |f| %>
so you the attachment can be added
Think thats it off the top of my head
Hope that helps, any questions then please ask
Working through the railstutorial.org . Currently on the Update Profile page part of it. When leaving the Password and Password Confirmation fields empty, only the Password is too short error comes up, though in the tutorial screenshot Password confirmation can't be blank message is present. But, it does show up when the Password field is filled and Password Confirmation field is left empty.
edit.html.erb :
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
<%= gravatar_for #user %>
change
</div>
</div>
users_controller.rb:
.
.
.
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
.
.
.
user.rb:
class User < ActiveRecord::Base
has_secure_password
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
before_create :create_remember_token
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
Your User.rb model is out of sync with the one MHartl uses at that point in the tutorial. Specifically, notice that he has an explicit validates :password_confirmation, presence: true in addition to the validates :password, length: { minimum: 6 }. When you call #user.update_attributes, it hits these validators and, in his case, both fail, whereas in your User.rb model there is not the presence validator.
When you have the password field filled in, you're hitting validators defined in has_secure_password rather than in your model, which is why they appear then.
Make sure you include the password and password_confirmation in your allowed parameters in you controller:
private
def user_params
params.required(:user).permit(:name, :email, password,:password_confirmation)
end
EDIT:
As the other had already pointed out you miss the validation of password_confirmation
validates :password_confirmation, presence: true
I am trying to use ActiveModel to create a contact us form. When I click submit i get a NameError uninitialized constant ContactsController::ContactMailer on line ContactMailer.new_contact(#contact).deliver
Can someone show me what I am doing wrong?
Controller:
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
#contact = Contact.new(params[:contact])
if #contact.valid?
ContactMailer.new_contact(#contact).deliver
redirect_to root_path
else
render :new
end
end
end
Model:
class Contact
include ActiveModel::Model
attr_accessor :name, :email, :message
validates :name, presence: true
validates :email, presence: true
validates :message, presence: true, length: { maximum: 300 }
end
Form:
<h1>Contact Us</h1>
<%= form_for #contact do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :message %>
<%= f.text_area :message %>
<%= f.submit 'Submit' %>
<% end %>
Based on our comments, you have not implemented ContactMailer yet. You can do so at app/mailers/contact_mailer.rb.
You can find out more about mailers in the Rails docs.
(It sounds like you already know how to implement mailers, but you're not the only one who will ever read this answer.)
I too had the same problem because the contact_mailer.rb file wasn't matching with the class name that i had given to that file.
I renamed my file name i.e from contacts_mailer.rb to contact_mailer.rb for my class ContactMailer
I've got an address form nested into a user form but cant get the foreign key to fill. I've seen people suggest using a hidden field, but that seems to be a bad idea from a security standpoint. How exactly do you set the foreign key using the controller? Right now I'm getting Address user can't be blank error when I try to submit
MVC below
user\new.html.erb
<div>
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :rank %>
<%= f.text_field :rank %>
<%= f.label :firstName, "First Name" %>
<%= f.text_field :firstName %>
<%= f.label :lastName, "Last Name" %>
<%= f.text_field :lastName %>
<%= f.label :middleInitial, "Middle Initial" %>
<%= f.text_field :middleInitial %>
<%= fields_for :address do |a| %>
<%= a.label :address %>
<%= a.text_field :address %>
<%= a.label :city %>
<%= a.text_field :city %>
<%= a.label :state %>
<%= a.text_field :state %>
<%= a.label :zip, "Zip Code" %>
<%= a.text_field :zip %>
<% end %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :dateOfBirth, "Date of Birth" %>
<%= f.text_field :dateOfBirth %>
<%= f.label :MOS, "MOS" %>
<%= f.text_field :MOS %>
<%= f.label :ets_pcsDate, "ETS/PCS Date" %>
<%= f.text_field :ets_pcsDate %>
<%= f.label :phoneNum, "Phone Number" %>
<%= f.text_field :phoneNum %>
<%= f.label :password %>
<%= f.text_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.text_field :password_confirmation %>
<%= f.submit "Sign up" %>
<% end %>
</div>
<h1>Users#new</h1>
<p>Find me in app/views/users/new.html.erb</p>
Models
User
class User < ActiveRecord::Base
attr_accessible :MOS, :dateOfBirth, :ets_pcsDate, :firstName,
:lastName, :middleInitial, :phoneNum, :rank, :email, :password,
:password_confirmation
has_secure_password
has_one :address, dependent: :destroy
accepts_nested_attributes_for :address
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :rank, presence: true
validates :firstName, presence: true, length: { maximum: 15 }
validates :lastName, presence: true, length: { maximum: 20 }
validates :middleInitial, presence: true, length: { maximum: 1 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :dateOfBirth, presence: true
validates :MOS, presence: true
validates :ets_pcsDate, presence: true
validates :phoneNum, presence: true
validates :password, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Address
class Address < ActiveRecord::Base
attr_accessible :address, :city, :state, :zip
belongs_to :user
validates :address, presence: :true
validates :city, presence: :true
validates :state, presence: :true
validates :zip, presence: true
validates :user_id, presence: true
end
Controller
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :show, :destory]
before_filter :correct_user, only:[:edit, :update]
before_filter :admin_user, only: :destroy
def new
#user = User.new
#user.address.build
end
def create
#user = User.new(params[:user])
#address = #user.build_address(params[:address])
if #user.save
sign_in #user
flash[:success] = "Welcome to B Troop!"
redirect_to #user
else
render 'new'
end
end
def show
#user = User.find(params[:id])
end
def index
#users = User.paginate(page: params[:page])
end
def edit
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User removed"
redirect_to users_path
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to root_path, notice: "Please sign in."
end
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
Removing the user_id validation did the trick.