Sign in

Зарегистрируйтесь, чтобы стать полноправным участником сообщества Masterpro.ws.

Форма обратной связи на Ruby on Rails

Как сделать форму отправки сообщений в 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</label>
         <input name="post[name]" type="text" placeholder="Name" required="required">
      </div>
      <div>
         <label>Email Address</label>
         <input class="form-control" name="post[email]" type="email" placeholder="Email Address" required="required">
      </div>
      <div>
         <label>Phone Number</label>
         <input name="post[phone]" type="tel" pattern="((\+|00)?[1-9]{2}|0)[1-9]([0-9]){8}" placeholder="Phone Number" required="required">
      </div>
      <div>
         <label>Message</label>
         <textarea name="post[message]" rows="5" placeholder="Message" required="required"></textarea>
      </div>
      <br>
      <%= recaptcha_tags %>
      <br>
      <button type="submit">Send</button>
      <% 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. Как вариант.

Все!

Оставить комментарий

Добавьте ваш комментарий

Комментарии в блоге

  • Сережа Хорошая статья, что редкость в наше время. Спасибо!

    Понедельник, 08 июня 2020

    Опубликовано в: Между VirtualBox и KVM
  • Aleksej Характер коллектива, специфика рабочих профилей специалистов в значительной степени определяются личностью руководителя компании. Взаимосвязь здесь огромная, почти мистическая; не раз…

    Воскресенье, 31 мая 2020

    Опубликовано в: IWU (iwu.team) и Geecko (geecko.ru). Отзывы
  • ПроходилНоОставил Прислали вакансию какого-то "супер-пупер" стартапа. Но обязательна регистрация на их сайте, чтобы откликнуться на вакансию. Компания неизвестная, инфу про нее…

    Вторник, 26 мая 2020

    Опубликовано в: IWU (iwu.team) и Geecko (geecko.ru). Отзывы
  • Evgenij Правильный способ рестарта elasticsearch: _stackoverflow.com /questions /14119062/restart-elasticsearch-node

    Суббота, 04 апреля 2020

    Опубликовано в: Searchkick::MissingIndexError. Elasticsearch
  • Артемaws У меня тоже начались проблемы такие, но сейчас на дворе 20.03.2020. Около полугода работаю с яндекс картами и тут как…

    Пятница, 20 марта 2020

    Опубликовано в: Amazon Web Services отказывается принимать к оплате Яндекс.Деньги?