acts-as-taggable-on と tag-it でタグの補完入力(Rails)

こんな感じのやつを作りました。
 
f:id:rochefort:20161016165848g:plain  
使うのは以下です。
タグのjQueryプラグインはいろいろありますが、tag-it を選択した理由は自動補完機能が付いているからです。

ざっくり

acts-as-taggable-on は公式を参照すれば良いので省略。
mbleigh/acts-as-taggable-on: A tagging plugin for Rails applications that allows for custom tagging along dynamic contexts.

tag-it は、jQuery-UIを利用するので以下を追加してbundle。

# Gemfile
gem "jquery-ui-rails"

続いてtag-it ですが、js/css をダウンロードして以下に配置します。
vendro/assets以下のjavascriptsとstylesheetsに置きたくなりますが、
合わせて管理したいので、今回 vendor/assets/tag-it 以下に配置することにしました。

vendor
└── assets
    ├── javascripts
    ├── stylesheets
    └── tag-it
        ├── jquery.tagit.css
        └── tag-it.js

vendor/assets 以下のディレクトリは、 Rails.application.config.assets.paths に追加されるので 以下に追記するだけで使えます。
application.js

//= require jquery-ui/autocomplete
//= require tag-it

application.css

 *= require 'jquery-ui/autocomplete'
 *= require 'jquery.tagit'

controller
自動補完用にインスタンス変数を作成。ActsAsTaggableOn::Tag.all で全てのタグが取得できます。

@all_tag_list = ActsAsTaggableOn::Tag.all.pluck(:name)

view
複数の値がある場合は、デフォルトカンマ区切りなのでvalueで値を指定します。
jsでタグリストを扱えるように、一旦viewに出力します。

      <div class="form-group">
        <%= f.label :tag_list, "キーワード", class: "col-md-2 control-label" %>
        <div class="col-md-10">
          <%= f.text_field :tag_list, value: @article.tag_list.join(',') %>
        </div>
      </div>


<%= javascript_tag do %>
var myProject = {
  all_tag_list: <%= raw @all_tag_list %>
};

js
singleField で複数値持つことと、availableTags で自動補完できるようにします。

$(document).on 'ready page:load', ->
  $('#article_tag_list').tagit
    singleField: true,
    availableTags: myProject.all_tag_list

Rails + Bootstrap 時のflashのkeyについて

第7章 ユーザー登録 | Rails チュートリアル でも述べられていますが
Bootstrapには、以下の4つのalertsが用意されています。
success, info, warning, danger
Bootstrap Alerts
 
通常、Railsでは notice、warningを使いますが
Bootstrap 使うときは、上記4種を利用するようにした方が効率が良さそうです。

# layoutなど
      <% flash.each do |message_type, message| %>
        <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
      <% end %>
# controllerでflashを利用する場合
flash.now[:warning] = "Login failed"
redirect_to(:users, flash: { success: "Logged out!" })
# form
<% if object.errors.any? %>
<div id="error_explanation">
  <div class="alert alert-danger">
    The form contains <%= pluralize(object.errors.count, "error") %>
  </div>
  <ul>
    <% object.errors.full_messages.each do |msg| %>
    <li><%= msg %></li>
    <% end %>
  </ul>
</div>
<% end %>

Rails Tutorial 第3版 第11章

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう の2周目です。
第11章ユーザーのマイクロポスト | Rails チュートリアル

第11章ユーザーのマイクロポスト

本格的にWebアプリっぽくなってきました。
carrierwave, mini_magick, fog や、今回も各種テストは参考になります。

paginateさせながらのテスト

@user.microposts.paginate(page: 1).each do |micropost|
  assert_match micropost.content, response.body
end

validation にファイル容量制限を入れる

  # model
  validate  :picture_size
  
  private

    def picture_size
      if picture.size > 5.megabytes
        errors.add(:picture, "should be less than 5MB")
      end
    end

acceptパラメータでuploadのファイルを制限する

<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>

jsでファイル容量の制御をする

onじゃなくbindで書いているところは気になりますが、まぁ本筋ではないので。

<script type="text/javascript">
  $('#micropost_picture').bind('change', function() {
    var size_in_megabytes = this.files[0].size/1024/1024;
    if (size_in_megabytes > 5) {
      alert('Maximum file size is 5MB. Please choose a smaller file.');
    }
  });
</script>

テスト時にcarrierwaveのresizeを無効化する

if Rails.env.test?
  CarrierWave.configure do |config|
    config.enable_processing = false
  end
end