Как сделать форму отправки сообщений в Ruby on Rails
Начну с утверждения: все не просто, а очень просто. Даже если вы самый что ни на есть новичок в работе с фреймворком Ruby on Rails - следуя шаг за шагом указаниям этого материала, вы получите в итоге вполне работоспособную форму обратной связи, которую сможете в дальнейшем украсить любыми стилями. Возможно, результат будет напоминать описанный (взгляните демку) здесь, а может(и вероятнее всего) - будет иным, лучше или хуже.
Неважно, лиха беда начало; но начнем с того, что создадим новое Rails-приложение:
rails new my_new_app
В системе у меня - новенький Rails 6 (рекомендую именно его для экспериментов с новыми applications), посему в самом конце получаю:
Webpacker successfully installed
Ок. Помня о том, что Ruby on Rails суть MVC-фреймворк - тут же сразу создаем контроллер, вьюху, модель нашего приложения. Нам достаточно для этого всего лишь двух команд:
rails g controller Posts index
rails g model post name:string email:string phone:string message:string
А еще нам понадобятся (как минимум) эти три джема, которые вписываем в Gemfile и затем бундлим:
gem 'dotenv-rails', :require => 'dotenv/rails-now'
gem "recaptcha", require: "recaptcha/rails"
gem 'devise'
bundle install
, далее сразу приступаем к настройке devise точь-в-точь как описано в этом доке, только пропустите сейчас третий шаг (конфигурированием мейлера займемся чуть позже) и на седьмом шаге вместо модели User создадим Admin-a, он будет у нас единственным пользователем системы:
rails generate devise Admin
, а routes.rb в итоге наших с вами усилий вполне способен выглядеть следующим образом:
Rails.application.routes.draw do
devise_for :admins, controllers: { registrations: "registrations" }
resources :posts
root to: 'posts#landing'
end
Открываем директорию app/views/posts, в которой уже находится index.html.erb, и создаем здесь еще два файла под указанными далее именами. Все вместе для начала примерно так:
index.html.erb
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<% if admin_signed_in? %>
<p>Здравствуйте, <%= current_admin.email %></p>
<%= link_to 'Выйти', destroy_admin_session_path, :method => :delete %>
<% else %>
<%= link_to 'Войти', new_admin_session_path %> или <%= link_to 'Зарегистрироваться', new_admin_registration_path %>
<% end %>
<h1>Posts</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Message</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.name %></td>
<td><%= post.email %></td>
<td><%= post.phone %></td>
<td><%= post.message %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
landing.html.erb
<%= render 'template', post: @post %>
_template.html.erb
<html>
<head>
<title>Форма отправки сообщений</title>
</head>
<body>
<p><%= notice %></p>
<p><%= alert %></p>
<%= form_with(model: post, local: true) do |form| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= label(:name, :name) %>
<%= form.text_field :name, placeholder: "Name", class: 'form-control', required: true %>
</div>
<div>
<%= label(:email, :email) %>
<%= form.email_field :email, placeholder: "Email Address", class: 'form-control', required: true %>
</div>
<div>
<%= label(:phone, :phone) %>
<%= form.telephone_field :phone, placeholder: "Phone", class: 'form-control', :pattern => '((\+|00)?[1-9]{2}|0)[1-9]([0-9]){8}', required: true %>
</div>
<div>
<%= label(:message, :message) %>
<%= form.text_area :message, placeholder: "Message", class: 'form-control', required: true %>
</div>
<br>
<%= recaptcha_tags %>
<br>
<%= form.submit :Send %>
<% end %>
</body>
</html>
Считайте - главное сделано, и работа близится к концу. Осталось всего ничего... самое время открыть app/controllers и заняться нашим posts_controller.rb, который может быть (например) вот таким:
class PostsController < ApplicationController
before_action :authenticate_admin!, only: [:index]
before_action :set_post, only: [:destroy]
def index
@posts = Post.all
end
def landing
@post = Post.new
end
def create
@post = Post.new(post_params)
if verify_recaptcha
respond_to do |format|
if @post.save
FormMailer.with(form: @post).new_form_email.deliver_later
format.html { redirect_to '/', notice: 'Post was successfully created.' }
end
end
end
end
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
end
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:name, :email, :phone, :message)
end
end
Вы можете добавить (если нужно) в application_controller.rb парочку методов, переопределяющих редиректы devise после логина и выхода :
def after_sign_in_path_for(resource)
'/posts'
end
def after_sign_out_path_for(resource)
'/'
end
Теперь в этом же каталоге controllers создадим еще один контроллер, реализующий (вернувшись назад и взглянув на routes.rb, вы убедитесь, что маршруты уже содержат переопределение контроллера регистрации) однопользовательскую систему:
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_action :one_user_registered?, only: [:new, :create]
protected
def one_user_registered?
if Admin.count == 1
if admin_signed_in?
redirect_to root_path
else
redirect_to new_admin_session_path
end
end
end
end
Переходим к настройке почты. Создаем почтовую программу:
rails generate mailer FormMailer
После того, как отработает генератор, идем в каталог app/mailers и редактируем form_mailer.rb, приводя к следующему:
class FormMailer < ApplicationMailer
def new_form_email
@form = params[:form]
mail(to: ENV['SEND_EMAIL_TO'], subject: ENV['SUBJECT_EMAIL'])
end
end
Создадим в директории app/views/form_mailer два новых файла:
new_form_email.html.erb
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<p>You got a new form from <%= @form.name %>!</p>
<p>
form details<br>
--------------------------
</p>
<p>Name: <%= @form.name %></p>
<p>Email: <%= @form.email %></p>
<p>Phone: <%= @form.phone %></p>
<p>
Message:<br>
----------
</p>
<p><%= @form.message %></p>
</body>
</html>
new_form_email.text.erb
You got a new form from <%= @form.name %>!
===============================================
form Details:
--------------------------
Name: <%= @form.name %>
Email: <%= @form.email %>
Phone: <%= @form.phone %>
Message:
<%= @form.message %>
Откроем test/mailers/previews/form_mailer_preview.rb, который отредактируем следующим образом:
class FormMailerPreview < ActionMailer::Preview
def new_form_email
# Set up a temporary order for the preview
form = Post.new(name: "Vasya Pupkin", email: "Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. ", phone: "+79871234567", message: "Hello everyone.")
FormMailer.with(form: form).new_form_email
end
end
Теперь, введя в адресной строке браузера
http://localhost:3000/rails/mailers/form_mailer/new_form_email.html
- любуемся на (а при необходимости редактируем) оба (HTML и plain-text) варианта почтового сообщения, формируемого Ruby on Rails формой.
Открываем config/environments/development.rb, который дописываем следующим образом (это для среды разработки; для продакшна - файл production.rb - понадобилось бы все то же самое, только вместо localhost:3000 в тех же кавычках указать url вашего сайта):
config.action_mailer.delivery_method = :smtp
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
# SMTP settings for gmail
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:user_name => ENV['GMAIL_USER_NAME'],
:password => ENV['GMAIL_PASSWORD'],
:authentication => "plain",
:enable_starttls_auto => true
}
Мы с вами намереваемся использовать SMTP Gmail; понадобится, соответственно, учетная запись этой почты, и обязательно создадим пароль приложения, который и предназначен (ни в коем случае не основной логин-пароль!) к использованию вашей новой веб-формой.
В корне приложения создаем скрытый файл .env, который, я думаю, в пространных комментариях не нуждается:
export RECAPTCHA_SITE_KEY = '****************************'
export RECAPTCHA_SECRET_KEY = '****************************'
export GMAIL_USER_NAME = '****************'
export GMAIL_PASSWORD = '***************'
export SEND_EMAIL_TO = 'info@your_domain.com'
export SUBJECT_EMAIL = 'Hurray! You got a new form!'
Хм... кажется, на этом все, и вроде ничего я не забыл. Запускаем миграцию и затем rails server:
rails db:migrate RAILS_ENV=development
rails s
Тестируем форму, которая, к слову сказать, не только письма отправляет, а еще и записывает отправляемые данные в базу данных. Открыв в браузере
http://localhost:3000/posts
- устанавливаем свой логин/пароль (убедитесь, ввести данные пользователя удастся только один раз, а далее 'Sign up' станет редиректить на 'Sign in'; впрочем, сменить пароль несложно по 'Forgot your password?'), сразу после чего вам доступен полный перечень отправленных на ваш email сообщений (удаляем ненужные и спам, нажимая Destroy).
Сходу вам не удастся протестировать формочку локально, коль скоро ключи reCaptcha привязаны к тому или иному домену; соответственно, в post_controllers.rb в 15 строчке -
if verify_recaptcha
- временно замените if на unless. Как вариант.
Все!
Комментарии в блоге