5分でRailsアプリにマークダウンとプレビューを実装。
Railsアプリにマークダウン機能を実装します。
今回はQiitaマークダウンというGemを使用します。
アプリの作成
$ rails g model post content:text $ rails g controller posts $ rake db:migrate
/app/controller/posts_controller.rb
def new @post = Post.new end def create @post = Post.new(post_params) @post.save redirect_to @post end def show @post = Post.find(params[:id]) @post.save! end private def post_params params.require(:post).permit(:content) end
/app/views/new.html.erb
<%= form_for(@post) do |f| %> <%= f.text_area :content %> <%= f.submit %> <% end %>
/app/views/show.html.erb
<%= @post.content %>
/config/routes.rb
resources :posts, only: %i(create show new)
これで投稿できるようになります。
Qiitaマークダウンの導入
Gemfile
gem 'qiita-markdown' gem 'github-linguist' bundle
bundleが失敗する人は2つのプラグインが足りていません
以下を実行してください。
ちなみにCloud9ではエラーは出ません。
$ sudo yum -y install libicu-devel $ gem install charlock_holmes -v '0.7.3' $ sudo yum -y install cmake $ gem install rugged -v '0.25.0b4'
これでマークダウンが使えます。
show.html.erbを以下のように修正してください。
<%= qiita_markdown(@post.content) %>
helperも必要になります。
/application_helper.rb
module ApplicationHelper def qiita_markdown(markdown) processor = Qiita::Markdown::Processor.new(hostname: "example.com") processor.call(markdown)[:output].to_s.html_safe end end
プレビューを作成
書いた記事が横もしくは下でリアルタイムで表示されます。
マークダウン記法では自分の記事がうまく描かれるかチェックしなければならないので必須でしょう。
ここでは、そのプレビューも作成してみましょう。
Vue.jsとmarked.jsを読み込みます。
app/views/application.html.erb
<script src='https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.10/vue.js'></script>
formを編集します。
/new.html.erb
<%= form_for(@post) do |f| %> <div class="form-group"> <div id='editor'> <textarea name="post[content]" id="n" class="form-control" rows="20" v-model='input' debounce='50'></textarea> <div class="di_inline_t preview"> <div v-html='input | marked' style: :"width: 650px;"></div> </div> </div> </div> <%= f.submit %> <% end %> <script type="text/javascript"> window.onload = function() { new Vue({ el: '#editor', data: { input: '<%== j @post.content %>', }, filters: { marked: marked, }, }); }; </script>
Bootstrapの導入
bootstrapを導入しないと一部のマークダウンが反映されません。
gem 'bootstrap-sass'
なんでもいいです.scss
@import "bootstrap-sprockets"; @import "bootstrap";
再起動します。
これで使えるようになります。
リロードしないと使えないという人はapplication.jsの//= require turbolinksを決してください。
あるとうまく動かないことがあります。
おしゃれなマークダウン
コードをみやすくする為にscssに以下のコードを追加するといいかもしれません。
//------------------------シンタックスハイライト .code-frame { background-color: #eee; .code-lang { span { padding: .5em; color: #fff; background-color: #666; } } .highlight { .hll { background-color: #ffffcc } .c { color: #999988; font-style: italic } /* Comment */ .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .k { color: #000000; font-weight: bold } /* Keyword */ .o { color: #000000; font-weight: bold } /* Operator */ .cm { color: #999988; font-style: italic } /* Comment.Multiline */ .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */ .c1 { color: #999988; font-style: italic } /* Comment.Single */ .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .ge { color: #000000; font-style: italic } /* Generic.Emph */ .gr { color: #aa0000 } /* Generic.Error */ .gh { color: #999999 } /* Generic.Heading */ .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .go { color: #888888 } /* Generic.Output */ .gp { color: #555555 } /* Generic.Prompt */ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #aaaaaa } /* Generic.Subheading */ .gt { color: #aa0000 } /* Generic.Traceback */ .kc { color: #000000; font-weight: bold } /* Keyword.Constant */ .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */ .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */ .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */ .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */ .kt { color: #445588; font-weight: bold } /* Keyword.Type */ .m { color: #009999 } /* Literal.Number */ .s { color: #d01040 } /* Literal.String */ .na { color: #008080 } /* Name.Attribute */ .nb { color: #0086B3 } /* Name.Builtin */ .nc { color: #445588; font-weight: bold } /* Name.Class */ .no { color: #008080 } /* Name.Constant */ .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */ .ni { color: #800080 } /* Name.Entity */ .ne { color: #990000; font-weight: bold } /* Name.Exception */ .nf { color: #990000; font-weight: bold } /* Name.Function */ .nl { color: #990000; font-weight: bold } /* Name.Label */ .nn { color: #555555 } /* Name.Namespace */ .nt { color: #000080 } /* Name.Tag */ .nv { color: #008080 } /* Name.Variable */ .ow { color: #000000; font-weight: bold } /* Operator.Word */ .w { color: #bbbbbb } /* Text.Whitespace */ .mf { color: #009999 } /* Literal.Number.Float */ .mh { color: #009999 } /* Literal.Number.Hex */ .mi { color: #009999 } /* Literal.Number.Integer */ .mo { color: #009999 } /* Literal.Number.Oct */ .sb { color: #d01040 } /* Literal.String.Backtick */ .sc { color: #d01040 } /* Literal.String.Char */ .sd { color: #d01040 } /* Literal.String.Doc */ .s2 { color: #d01040 } /* Literal.String.Double */ .se { color: #d01040 } /* Literal.String.Escape */ .sh { color: #d01040 } /* Literal.String.Heredoc */ .si { color: #d01040 } /* Literal.String.Interpol */ .sx { color: #d01040 } /* Literal.String.Other */ .sr { color: #009926 } /* Literal.String.Regex */ .s1 { color: #d01040 } /* Literal.String.Single */ .ss { color: #990073 } /* Literal.String.Symbol */ .bp { color: #999999 } /* Name.Builtin.Pseudo */ .vc { color: #008080 } /* Name.Variable.Class */ .vg { color: #008080 } /* Name.Variable.Global */ .vi { color: #008080 } /* Name.Variable.Instance */ .il { color: #009999 } /* Literal.Number.Integer.Long */ } }
以上です!