1. 開発環境
- M1 Mac Monterey 12.0.1
- ruby3.0
- rails6.1.4.1
githubはこちらです。
オリジナルアプリにActionTextかMarkdownかで迷ったのでミニアプリを作って比べてみることにしました。
2. 実装開始
2.1 アプリ作成、初期設定
まずは新しいアプリの作成をしていきます。
rails _6.1.4.1_ new markdown-blog -d mysql
database.ymlの編集をします。
default: &default
adapter: mysql2
# encoding: utf8mb4
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
socket: /tmp/mysql.sock
2.2 turbolinksの無効化
以下の2つのファイルを編集、修正していきます。
import Rails from "@rails/ujs"
// import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
Rails.start()
Turbolinks.start()
ActiveStorage.start()
<!DOCTYPE html>
<html>
<head>
<title>MarkdownBlog</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<!-- 'data-turbolinks-track': 'reload'を削除 -->
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
<!-- ここまで -->
</head>
<body>
<%= yield %>
</body>
</html>
2.3 必要なもののインストール
まずは追記をします。
gem 'image_processing', '~> 1.2'
続いてターミナルで以下を実行していきます。
bundle install
rails db:create
rails active_storage:install
rails db:migrate
yarn add simplemde
yarn add inline-attachment
ここでは
- ActiveStorage(ファイルアップロードの機能)
- SimpleMDE(ブラウザ上のtextareaタグをMarkdownエディタを変換するOSS)
- Inline Attachment(ブラウザ上からドラッグ&ドロップでのAjaxでのファイル送信を簡単に実装)
を導入しています。
2.4 model, controller, viewの編集
scaffoldでサクッと作成していきます。
rails g scaffold post content:text
rails db:migrate
続いて以下のファイルを編集します。
<!-- 追記(画面で読み込むjsを読み込む) -->
<%= javascript_pack_tag 'post' %>
<%= form_with(model: post) 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.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :content %>
<!-- 編集 -->
<%= form.text_area :content, { id: "markdown-editar"} %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
続いてapp/javascript/packs配下にpost.jsを作成します。
そして以下のように記述していきます。ここではSimpleMDEの導入と、ドラッグ&ドロップの処理を記述しています。
//インストールした機能をimportする
import "inline-attachment/src/inline-attachment";
import "inline-attachment/src/codemirror-4.inline-attachment";
import 'simplemde/dist/simplemde.min.css'
import SimpleMDE from "simplemde";
import Rails from '@rails/ujs'
window.onload = function () {
// textareaをMarkdownエディタにする
const simplemde = new SimpleMDE({
element: document.getElementById("markdown-editar"),
});
// エディタに画像がドラッグ&ドロップされた際の処理
inlineAttachment.editors.codemirror4.attach(simplemde.codemirror, {
uploadUrl: "/posts/attach", // POSTする宛先Url
uploadFieldName: "image", // ファイルのフィールド名(paramsで取り出す時のkey)
allowedTypes: ['image/jpeg', 'image/png', 'image/jpg', 'image/gif'],
extraHeaders: { "X-CSRF-Token": Rails.csrfToken() }, // セキュリティ対策
});
};
2.5 ajaxの処理
rails g model attachment
で以下のように編集します。
class Attachment < ApplicationRecord
has_one_attached :image
end
続いてはコントローラーの処理です。ここでjson形式にして値を返します。
class PostsController < ApplicationController
before_action :set_post, only: %i[ show edit update destroy ]
# GET /posts or /posts.json
def index
@posts = Post.all
end
# 略
# Ajax通信の記述を追記
def attach
attachment = Attachment.create! image: params[:image]
render json: { filename: url_for(attachment.image) }
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(:content)
end
end
最後に以下を追記してください。
post 'posts/attach', to: 'posts#attach'
これで画像の処理ができました。しかし、これではmarkdownがhtmlに変換されずに出力されてしまいますので直していきます。
まずは現状確認からしていきましょう。
2.6 redcarpetの導入
現状確認
うまくアップロードできていますが、create postをクリックすると...
これはまずいですね。そのまま表示されています。
これからmarkdownがhtmlに変換していきます。gemで解決します。
以下を追記してください。
gem 'redcarpet'
bundle install
これでひとまず導入が完了です。
続いてapp/helper
にmarkdown_helper.rb
ファイルを作成します。さらに以下のように記述していきます。
module MarkdownHelper
def markdown(text)
unless @markdown
options = {
filter_html: true,
autolink: true,
space_after_headers: true,
no_intra_emphasis: true,
fenced_code_blocks: true,
tables: true,
hard_wrap: true,
xhtml: true,
lax_html_blocks: true,
strikethrough: true
}
renderer = Redcarpet::Render::HTML.new(options)
@markdown = Redcarpet::Markdown.new(renderer)
end
@markdown.render(text).html_safe
end
end
さらに詳細ページを編集していきます。
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<!-- ここを編集 -->
<%= markdown(@post.content) %>
</p>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
これで完了です!