railsにmarkdownを実装したよ
markdown 機能実装
記事を書くのにいちいちHTMLで書くのは面倒。
だから使い慣れているmarkdownを導入することにした。
今回欲しかった機能は以下の通り
順番に説明していく。
実装工程
markdown
今回使うgemはこれ
gem 'redcarpet' gem 'rouge'
そしておなじみのやつを打ち込んで
sudo bundle install
以下のファイルを作ってコードをぶち込む
app/helper/markdown_helper.rb
module MarkdownHelper def markdown(text) options = { filter_html: true, hard_wrap: true, space_after_headers: true, with_toc_data: true } extensions = { autolink: true, no_intra_emphasis: true, fenced_code_blocks: true, tables: true } renderer = Redcarpet::Render::HTML.new(options) markdown = Redcarpet::Markdown.new(renderer, extensions) markdown.render(text).html_safe end end
optionsとextentionsの中身(filter_htmlとか)は参考サイトを参照してほしい。もっと丁寧に書いてあって参考になるはず。
これができたらあとは、適当なviewにこれをかく。()の中身は各自対応させる。
app/views/articles/show.html.slim
= markdown(@article.content)
これだけで@article.contentがmarkdownで表示される。
すげええ。
参考
redcarpetでrailsにシンプルにMarkdownを適用する
リアルタイムプレビュー
vue.jsとmarked.jsをheadに追加する
/app/views/layout/application.html.slim
doctype html html head title | hoge link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" /! Realtime preview 始まり script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.10/vue.js" script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.js" /! Realtime preview 終わり = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' body /! 以下略
ほんで、フォームの部分に、投稿部分とプレビュー部分を並べる。
JSも書き忘れないように。
app/views/articles/_form.html.slim
= form_for @article do |f| /! 中略 .field #editor textarea.form-control debounce="50" name="article[content]" rows="20" v-model="input" div v-html=("input | marked") .actions = f.submit "保存" javascript: window.onload = function() { new Vue({ el: '#editor', data: { input: '#{j @article.content}', }, filters: { marked: marked, }, }); };
参考
html.erbファイルでjs直書きでその中にrubyのコードを埋め込んでいる状態のものをslimに置き換えるときの書き方
toc(目次)機能
先ほど作成したヘルパーに追記していく。
toc用のものを作成した。markdownを参考にしたら簡単にできた。
app/helper/markdown_helper.rb
module MarkdownHelper def markdown(text) options = { filter_html: true, hard_wrap: true, space_after_headers: true, with_toc_data: true } extensions = { autolink: true, no_intra_emphasis: true, fenced_code_blocks: true, tables: true } renderer = Redcarpet::Render::HTML.new(options) markdown = Redcarpet::Markdown.new(renderer, extensions) markdown.render(text).html_safe end /!以下追加 def toc(text) toc_option = { nesting_level: 2 } toc_renderer = Redcarpet::Render::HTML_TOC.new toc = Redcarpet::Markdown.new(toc_renderer, toc_option) toc.render(text).html_safe end /! 以上追加 end
あとは、markdownを実装したとき同様こうやると完成。
app/views/articles/show.html.slim
= markdown(@article.content)
参考
datetimepickerを取り入れた際につけたオプション
datetimepickerとは
時刻入力のフォームの際にカレンダーから選択できるようにするもの。
詳細は下の記事を参考にしてください。
今回は、datetimepickerを入れる際に利用したオプション的な機能を紹介します。
オプション的な機能
オートコンプリートをoff
テキストフィールドのオートコンプリート(文字を全部打たなくても補完してくれるやつ)が ちょうど邪魔になってしまったので削除した。
AMPM表記から24時間表記に変更
AMPM表記が調子悪かったので24時間表記を採用した。 時間表記のhh:mmの部分をHH:mmにすれば解決する。
日本語化
「日本語化」以降の部分を参照。 一行書くだけで日本語化されるので、ぜひ導入しておきたい。
こんなとこです
とまあ。この辺りを導入しました。先人の知恵を拝借してばかりで申し訳ないですね。
bootstrap3-datetimepickerをとりあえず入れてみた
bootstrap3-datetimepickerが必要になった
日付と日時を入れるのに使用したのがbootstrap3-datetimepicker。
今回はメモするのが面倒なので、ひたすらにコードを貼っていく。
# Gemfile gem 'momentjs-rails' gem 'bootstrap3-datetimepicker-rails'
で、sudo bundle installして。
// application.js //= require moment //= require moment/ja //= require bootstrap-datetimepicker var data = {'data-date-format': 'YYYY-MM-DD hh:mm' }; $(function(){ $('.datepicker').attr(data); $('.datepicker').datetimepicker({ format: 'YYYY-MM-DD hh:mm', locale: 'ja', dayViewHeaderFormat: 'YYYY年 MM月' }); });
ここでカレンダーの見た目やクリックした際のテキスト出力を変更する。
//= require moment //= require bootstrap-datetimepicker
ここは最低限必要。
//= require moment/ja
これは日本語化するのに必要。下のものと合わせて使う。
locale: 'ja', dayViewHeaderFormat: 'YYYY年 MM月'
以下はひたすらコピペ。
/* application.css */ *= require bootstrap-datetimepicker
# articles_controller.rb def create @article = Article.new(article_params) @article.user_id = current_user.id respond_to do |format| if @article.save format.html { redirect_to @article, notice: 'Article was successfully created.' } format.json { render :show, status: :created, location: @article } else format.html { render :new } format.json { render json: @article.errors, status: :unprocessable_entity } end end end # PATCH/PUT /articles/1 # PATCH/PUT /articles/1.json def update respond_to do |format| if @article.update(article_params) format.html { redirect_to @article, notice: 'Article was successfully updated.' } format.json { render :show, status: :ok, location: @article } else format.html { render :edit } format.json { render json: @article.errors, status: :unprocessable_entity } end end end private def article_params params.require(:article).permit(:title, :meta_description, :content, :published_at) end
publised_atに追加したいからそれを許可する。
/ _form.html.slim = form_for @article do |f| .input-group.date.datepicker = f.label :投稿時間 = f.text_field :published_at, class: 'form-control' span.input-group-addon
でもうまくいかなくて
こうすると、ページ遷移したときにうまくいかないんですよね、、
よくわからない。
今回解消したのは{:method => :get }を追加する方法。
link_to '編集', edit_article_path(@article), {:method => :get }
下のを追加するという方法もあったのだが、なぜか機能しなかったので諦めた。
$(document).on 'ready page:load', ->
参考 ↓
まとめ(個人的なやつ)
機能を実装するときは複数のサイトを見比べることで、
- より良い構成案が浮かぶ
- どのコードがどの役割をしているか予想がつく
などのメリットがあるんですねえ。
参考
[Rails]日付と時間の入力フォームにDateTimePickerを使う(bootstrap3-datetimepicker-rails) – hello-world.jp.net
rails で bootstrap3-datetimepicker を使ってみた - spring of life
kaminariでページが切り替わらなくて焦った
問題
いきさつ
この下のエントリを参考にやってます。
Gemfileに
gem 'kaminari'
を入れて
% bundle install
を実行した。
そして、index.html.slimに
= page_entries_info @articles / 本文 = paginate @articles
として追加した。
また、
@articles = Article.page(params[:article]).order(created_at: :desc)
しかし、なぜかページが変わらない。
2とかnextを押してもリンクが変わるだけで、次のページに行く気配がない。なんだこれ。
問題の要約
つまり、「リンクが変わるのにページが変わらない」というのが問題である。
解決の道筋
下のエントリをみたのがきっかけ
@articles = Article.page(7).order(created_at: :desc)
で、やってみたらpageが7まで、進んだ。 ふむ。こいつがpageを指定しているのはわかった。 いやまてよ。こいつの数が影響しているのではないか。
試しに、articlesのようにsを追加してやってみたら、全く結果が変わらなかったのだ。
@articles = Article.page(params[:articles]).order(created_at: :desc)
つまり、こいつが機能していないのだと。
解決方法
def index @articles = Article.page(params[:page]).order(created_at: :desc) end
:pageにすることで解決したのだった。私が勝手に変なarticleに変えてしまったのか、、
反省点
これからはこういう風に考えていこう、、
- 本当にそのものをコピーしているのか
- できれば、順番に原因になりそうなとこをコメントアウト
- それでもわからなければ、しらみつぶしにいじくってみる
deviseで新規カラムを追加・保存できるようにした(slim)
deviseの追加で困ったお話
deviseでuserモデルを作ったわけですが、そこに新規のカラムを追加したいのです。
usernameは、Twitterのusernameを取得するようにomniauthとかで自動で生成したものなので、今回は使いたくないなと。 そこでnameカラムを新規で作成し、ユーザー名を表示したりできるようになりたいなという魂胆です。
deviseの自動生成に困惑
でもdeviseで自動生成したものなので、手のつけようがない(知識がマジでないので私)。 そもそもviewもcontrollerもないじゃないかと嘆いたときにこのエントリーを発見。
viewとcontrollerを生成できることに気づく
めっちゃ書いてくれてあるやん! これに基づいて編集することにしました。
controllerのsuperについて
あとcontrollerにsuperっていう文字をちょいちょい見かけたんですよ。でも何か全然わかんなくて。
下の記事を見たところ、
「super」は特殊なメソッドと考えて下さい。メソッドの中で「super」メソッドを実行すると、スーパークラスの中でその呼び出されたメソッドと同じメソッド名を持つメソッドを探して実行します。
とのこと。つまり、どこかしらで同じメソッド名が記述されていて、それを引き継いでいるみたいな感じかな。例えば、def editのなかにsuperが書いてあったら、親のクラスのeditも引き継ぐよーってとこですかね。勉強が足らなくて難しい。
実際にやったこと
columnを作成
マイグレーションファイルを作成した。
% rails g migration AddNameToUsers name:string
んで、このあとはおきまりの
rake db:migrate
で、無事カラムをカラムを作成できたと。
viewを作成、編集
デフォルトではviewとcontrollerは編集できない(見えない?)ので、追加していきやす。
% rails generate devise:views
で作られたファイルは、erbなんでslimに変換して
form内に書き込める場所を作ると。(viewの英語表示部分は適当に日本語に変更していますが、ひとまず無視してもらって大丈夫です)
app/views/devise/registrations/edit.html.slim
h2 | ユーザー編集 - resource_name.to_s.humanize = form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| = devise_error_messages! / 以下追加------------------------------------------------------- .field = f.label :name, "名前" = f.text_field :name, :autofocus => true, class:"form-control" / 以上追加------------------------------------------------------- .field = f.label :メールアドレス br = f.email_field :email, autofocus: true, autocomplete: "email" - if devise_mapping.confirmable? && resource.pending_reconfirmation? div | Currently waiting confirmation for: = resource.unconfirmed_email .field = f.label :新規のパスワード i | 変更したくない場合は入力しないでください。 br = f.password_field :password, autocomplete: "off" - if @minimum_password_length br em = @minimum_password_length | 文字以上で入力してください。 .field = f.label :新規のパスワード(確認) br = f.password_field :password_confirmation, autocomplete: "off" .field = f.label :現在のパスワード i | 更新には現在のパスワードの入力が必要です。 br = f.password_field :current_password, autocomplete: "off" .actions = f.submit "更新" h3 | アカウント削除 = button_to "アカウント削除", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete = link_to "戻る", :back
んで、一人でなんとなくて作ったときにエラーが出たのはここ。nameカラムの変更が許可されていならしい。 先ほどの記事を見るとどうやらstrong parameterで引っかかっているらしい。strong parameterってセキュリティ対策で許可されていないカラムは権限がない限り変更できない仕様にするやつです。 ここで登場するのがcontrollerってわけ。
controllerの作成、編集
これでcontrollerを作成して、
% rails g devise:controllers users
早速その、カラムの変更を許可しにいきましょ。
app/controllers/users/registrations_controller.rb
# frozen_string_literal: true class Users::RegistrationsController < Devise::RegistrationsController # before_action :configure_sign_up_params, only: [:create] # before_action :configure_account_update_params, only: [:update] # 以下追加------------------------------------------------------- before_action :configure_permitted_parameters, only: [:update] private def configure_permitted_parameters devise_parameter_sanitizer.for(:account_update) do |u| u.permit(:name, :email, :password, :password_confirmation, :current_password) end end # 以上追加------------------------------------------------------- # 略 end
としたらなんかエラーがでた。
NoMethodError at /users undefined method `for' for #<Devise::ParameterSanitizer:0x007fc8748f96b8> Did you mean? fork
そういえばprivate中にあるsanitize入れてないわ。入れます。 gem 'sanitize'をGemfileに入れてbundle install、と思ったらなぜかできなかった。 できなかった詳細は下のエントリーを参考にしてほしい。 要は、sudo bundle installでやらなきゃいけなかったぽい。
そんなこんなでなんとかsanitizeを入れましたと。それでも同じエラーがでる
いよいよ表示できた
今度こそと思いこいつを参考にした今までforでトラブっていたところをpermitに変えてみるといけるかも?
Rails4からRails5にアップデートしたらDeviceでエラーが出た
で、変えてみたらとうとうできた!そもそもpermitにしておけばよかったのか。 (もしかしてgemのインストールいらなかった、、? あとあとsanitizeは使うからいっか、、)
headerでnameを表示
こちらも一部日本語化とかしてあるけど、追加の一行を書けばおけ。 nameは初期値ではnullになっているので、何も表示されないというのを防ぐため仮としてemaliを入れておいた。 email以外を入れるなりなんなりはお任せします。とりあえずこれでいけましたわ。
doctype html html head title | MusicaMusik = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' body header nav - if user_signed_in? # 下の一行を追加 ---------------------------------------------------------- = link_to current_user.name || current_user.email, users_show_path = link_to 'プロフィール変更', edit_user_registration_path = link_to 'ログアウト', destroy_user_session_path, method: :delete - else = link_to 'サインアップ', new_user_registration_path = link_to 'ログイン', new_user_session_path / p.notice = notice / p.alert = alert = yield
こうして、nameを入力する前はemailが表示され、
name入力後はnameが表示されるようになりましたとさ。めでたし。
存在しないファイルを指定されたから他のエラー(Errno::EACCES)を探して解決したった。
gemをインストールできないっ
gem 'nokogumbo' がなぜかインストールできないという自体になったのです。
(gem 'nokogumbo'をGemfileに書いた前提で話を進めていきます。)
ぱっと見簡単そうな問題なのですが、なんで今回私がこんなに悩んだかと言いますと、、
ディレクトリが存在しない
一部抜粋でエラー文を紹介します。
To see why this extension failed to compile, please check the mkmf.log which can be found here: /var/folders/h5/7qckhn4s59z93wzyj4ggqg_c0000gn/T/bundler20180527-63132-1bjn9r1nokogumbo-1.5.0/extensions/universal-darwin-17/2.3.0/nokogumbo-1.5.0/mkmf.log extconf failed, exit code 1
と書いてあったので、mkmf.logの中身を見れば何でエラーになったのかわかるだろうと。
しかし、このファイルは存在しませんでした。
まあ要領悪くで探せなかっただけなのかもしれないですが、
bundler20180527-63132-1bjn9r1nokogumbo-1.5.0/extensions/universal-darwin-17/2.3.0/nokogumbo-1.5.0/mkmf.log
から下の階層がどうやって探してみても存在しないんですよ。
% find / nokogumbo-1.5.0
で検索してみるもヒット数は0。
ここでどん詰まりました。
他のエラー文をみて見ると
どこか他にヒントはないかと探すと
/System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/fileutils.rb:253:in `mkdir': Permission denied @ dir_s_mkdir - /Library/Ruby/Gems/2.3.0/gems/nokogiri-1.8.2/ports (Errno::EACCES)
という一文を発見。
Errno::EACCES
ってなんかググればでできそうなエラー文やなと思い立ち、下のエントリーにたどり着く。
これで解決
以下のコマンドを打てと書いてあったから脳死でとりあえず打ち込むもうまくいかず。
% sudo gem update
しかし、このエントリーをよく見ると、permission関連でエラーがあったとかいてあるではないか。
もしやと思いsudoで試す。
% sudo bundle install
できた! どうやら権限を欲していたらしい。
できたのはいいのだけどなんで今回はsudoを必要としたのだろうか。いつものbundle installとは何か違ったということだろうか。
とりあえず、permission関連のエラーがあったら権限を付与して実行してみると良さそうだ。