Один из моих любимых проектов, над которым я работаю как новый разработчик Ruby on Rails, - это создание личного блога. В Интернете есть несколько качественных руководств, и работа над этим проектом учит множеству интересных основных концепций, включая аутентификацию, авторизацию, миграции, ассоциации и многое другое. Однако после того, как вы создали приложение для ведения блога, естественным следующим шагом будет добавление возможности форматирования контента с помощью форматированного текста, такого как размер шрифта, полужирный, курсив, отступы и т. Д. Цель этой статьи - провести вас через стандартную вводимую форму текстового поля Rails и усовершенствовать ее с помощью функций расширенного текстового формата ActionText.

Общие настройки

Для начала давайте создадим новое приложение Rails, которое я назову actiontext_demo.

rails new actiontext_demo

Как только это приложение будет сгенерировано, не забудьте записать cd в новый каталог приложения.

cd actiontext_demo

Давайте создадим шаблонный ресурс Post, который мы будем использовать с нашим редактором Rich Text. Эта команда сделает тяжелую работу по созданию нашей модели, миграции, контроллера и представлений для ресурса Post нашего блога (Примечание: вместо того, чтобы включать поле содержимого в нашу команду scaffold, мы будем использовать атрибут виртуального содержимого ActionText, который мы будем настрою позже).

rails generate scaffold Post title:string

На этом этапе давайте настроим нашу базу данных и перенесем нашу таблицу сообщений.

rails db:migrate

В качестве последнего общего шага настройки давайте теперь добавим корневой маршрут для представления индекса нашей публикации в верхнюю часть нашего routes.rb файла.

Rails.application.routes.draw do
  root to: "posts#index"
  resources :posts
end

Установка ActionText

Нашим первым конкретным шагом ActionText является установка пакета yarn, который, среди прочего, устанавливает необходимые зависимости JavaScript и генерирует миграции для ActiveStorage и ActionText (чтобы этот конкретный пост был кратким, в этом руководстве будет использоваться таблица ActionText, а выход - с использованием функциональности ActiveStorage. чтобы включить хранение вложений и встраивание изображений в будущую публикацию).

bin/rails action_text:install

После успешной установки вы должны увидеть следующие изменения в application.js:

app/javascript/packs/application.js
require("trix")
require("@rails/actiontext")

Кроме того, вам потребуется новая таблица стилей ActionText в вашем application.scss файле, например:

app/assets/stylesheets/application.scss
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.*
*= require_tree .
*= require_self
*/
@import "./actiontext.scss";

Прежде чем двигаться дальше, давайте снова перенесем нашу базу данных, чтобы добавить таблицы ActiveStorage и ActionText в нашу схему БД.

rails db:migrate

Настройка ActionText

Раньше, когда мы создавали нашу исходную миграцию Post с помощью сгенерированного каркаса нашего приложения, мы не использовали поле содержимого в пользу использования виртуального атрибута, доступного нам с помощью ActionText. Мы можем настроить это, добавив следующую строку в нашу модель Post.

app/models/post.rb
class Post < ApplicationRecord
  has_rich_text :content
end

Затем мы добавим группу форм ввода rich_text_area в нашу новую форму публикации.

<div class="field">
  <%= form.label :content %>
  <%= form.rich_text_area :content %>
</div>

Теперь полный файл должен выглядеть так:

app/views/posts/_form.html.erb
<%= 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 class="field">
    <%= form.label :title %>
    <%= form.text_field :title %>
  </div>
  <div class="field">
    <%= form.label :content %>
    <%= form.rich_text_area :content %>
  </div>
  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

Теперь мы можем отображать очищенный форматированный текст в наших представлениях для просмотра и индексации.

app/views/posts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>
<p>
  <strong>Content:</strong>
  <%= @post.content %>
</p>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
...
app/views/posts/index.html.erb
<p id="notice"><%= notice %></p>
<h1>Posts</h1>
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
      <th colspan="3"></th>
    </tr>
  </thead>
<tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.title %></td>
        <td><%= post.content %></td>
        <td><%= link_to 'Show', post %></td>
        <td><%= link_to 'Edit', edit_post_path(post) %></td>
        <td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>
<br>
<%= link_to 'New Post', new_post_path %>

Хотя теперь мы можем видеть потрясающий форматировщик Rich Text Formatter, который дает нам ActionText, если вы попытаетесь создать сообщение прямо сейчас, действие не удастся. Чтобы исправить это, нам нужно разрешить наш виртуальный атрибут :content в контроллере сообщений следующим образом:

app/controllers/posts_controller.rb
 def post_params
   params.require(:post).permit(:title, :content)
 end

Теперь наш posts_controller.rb файл должен выглядеть так:

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
  # GET /posts.json
  def index
    @posts = Post.all
  end
# GET /posts/1
  # GET /posts/1.json
  def show
  end
# GET /posts/new
  def new
    @post = Post.new
  end
# GET /posts/1/edit
  def edit
  end
# POST /posts
  # POST /posts.json
  def create
    @post = Post.new(post_params)
respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
# PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
# DELETE /posts/1
  # DELETE /posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
private
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = Post.find(params[:id])
    end
# Only allow a list of trusted parameters through.
    def post_params
      params.require(:post).permit(:title, :content)
    end
end

В качестве последнего шага для повышения производительности давайте обновим действие index контроллера Post, чтобы использовать вспомогательный метод для предотвращения N + 1 запросов к базе данных.

def index
  @posts = Post.all.with_rich_text_content
end

Заключение

Благодаря ActionText, наше базовое приложение для личного блога сделало важный шаг на пути к тому, чтобы стать увлекательным и эстетичным онлайн-журналом как для разработчиков, так и для читателей. Как вы собираетесь использовать его в своих проектах Ruby on Rails?