読者です 読者をやめる 読者になる 読者になる

Kaorina’s diary

Ruby on Railsとかとか

Rails4でRiot.js(3.3)が動く環境を構築する

Ruby on Rails Riot.js

実行環境

前提

あらかじめNode.jsがローカルにインストールされていること。
$ node -v を実行してバージョンが表示されればOK。

1. npmを使ってriotをインストールする

$ cd (Railsプロジェクト直下のディレクトリ)

プロジェクト直下にpackage.jsonがない場合は下記を実行

$ npm init

=> 基本Entで進み、package.jsonが作成される

packeage.jsonを編集

scripts, devDependenciesの箇所にriotを記載。
今回riotのバージョンを指定したが、しなくてもOK。編集が終わったら保存。

{
  "name": "(プロジェクト名)",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "riot": "riot"
  },
  "devDependencies": {
    "riot": "3.3.2"
  },
  "private": true
}

フロントエンド系のソースをまとめたいのでプロジェクト直下にディレクトリを作成

$ mkdir ./frontend

package.json/frontend 以下へ移動する

$ mv ./package.json frontend/

npmを使ってriotをインストール

今回このプロジェクトのみにインストールしたかったのでgオプションはつけませんでした。 また、指定のディレクトリ以下にインストールしたかったのでprefixオプションをつけました。

$ npm install --prefix ./frontend/

frontend以下にetc, node_modulesディレクトリが作成された!
この時点でfrontend以下はこのようなディレクトリ構造になっていると思います。

frontend/
├── etc
├── node_modules
├── package.json
└── riot
    ├── sample.tag

2. RailsでRiot.jsを使うためにソースをDLしてきて配置する

gemもあるようだけど、うまく動かなかったのでRiotの公式サイトからソースファイルをDLしてきて利用することにした。

curlコマンドでcompilerが含まれたmin.jsファイルをDLしてくる。

公式サイトのGitHubから、riot+compiler.min.jsを取得する。RawボタンからリンクするページのURLを指定すること〜。

oオプションをつけるとこの名前で保存するという指定ができる。 (↓改行されているけどワンライナーです)

$ curl https://raw.githubusercontent.com/riot/riot/master/riot%2Bcompiler.min.js -o ./vendor/assets/javascripts/riot.js

application.jsに追記し、上記のソースが読み込まれるようにする。

app/assets/javascripts/application.js

(省略)
//= require riot

これで環境構築は完了!

3. サンプルファイルを配置し、コンパイルを実行

tagファイルを作成

$ mkdir frontend/riot
$ touch frontend/riot/sample.tag

公式サイトで配布されているサンプルコードをコピーし、sample.tagとして保存

<sample>
  <h3>{ message }</h3>
  <ul>
    <li each={ techs }>{ name }</li>
  </ul>

  <script>
    this.message = 'Hello, Riot!'
    this.techs = [
      { name: 'HTML' },
      { name: 'JavaScript' },
      { name: 'CSS' }
    ]
  </script>

  <style>
    :scope { font-size: 2rem }
    h3 { color: #444 }
    ul { color: #999 }
  </style>
</sample>

html.erbファイルを作成

ブラウザで確認出来るviewファイルのbodyタグ内にsampleタグを配置し、mountする処理を書く。
(例)app/views/articles/index.html.erb

<!-- place the custom tag anywhere inside the body -->
    <sample></sample>
<!-- mount the tag -->
    <script>riot.mount('sample')</script>

コンパイルを実行し、tagファイルからjsファイルを作成

$ ./frontend/node_modules/.bin/riot ./frontend/riot/ app/assets/javascripts/riot/

↑については下記のような意味
[npm内のriotファイル] [tagファイルがあるディレクトリを指定] [jsファイルを作成したいディレクトリを指定]

4. 実行

ブラウザで上記のhtml.erbファイルを確認すると、、
Riotのコードが正しく実行されている(このサンプルの場合はsample.tagで設定した内容が表示されている)ことが確認出来る!

5. 諸々コミット対象から外しておく

node_modules以下、tagファイルはコミット対象に入れない方がベターなので、.gitignoreに追記しておく。
.gitignore

(省略)
/frontend/node_modules/*
/app/assets/javascripts/riot/*

Rails4以降でJSONデータを作る時にはjbuilderがお便利

Ruby on Rails

jbuilderというjsonのテンプレートエンジンを使うと、viewディレクトリ以下にxxx.json.jbuilderファイルを作成し、localhost:3000/xxx.jsonとアクセスするだけでJSON形式のレスポンスを受け取ることができる。

app/views/api/index.json.jbuilder(今回はapiというnamespaceを切っている)

json.prefectures @prefectures do |prefecture| //コントローラー側でDBのデータを取得する@prefecturesを生成している
  json.id prefecture.id
  json.name prefecture.name
end


コントローラーをはさまなくでも直接モデルを参照する書き方ももちろんOK!

json.prefectures Prefecture.all do |prefecture| 
  json.id prefecture.id
  json.name prefecture.name
end


app/controllers/api/prefectures_controller.rb

class Api::PrefecturesController < Api::ApplicationController
  def index
     @prefectures = Prefecture.all
  end
end



これで http://localhost:3000/api/prefectures.json にアクセスすると下記のようなデータが取得できる

{"prefectures":[{"id":1,"name":"東京都"},{"id":2,"name":"埼玉県"},{"id":3,"name":"千葉県"}]}


また、アクセスするURLをjson形式にあらかじめ限定しておくとhttp://localhost:3000/api/prefecturesでアクセス可能

config/routes.rb

Rails.application.routes.draw do
  中略
  namespace :api, { format: 'json' } do
    resources :prefectures, only: [:index, :show]
  end
end



コントローラーのindexアクション内で render :json => オブジェクト とする場合もJSON形式のデータを取得できるが、jbuilderを使う場合がより便利なのは、、

  • updated_atだけで良いのでcreated_atはJSONの項目から消して欲しいなど、出力するカラムの選択ができる
  • 特定の条件の時だけ表示形式を変えたい(日付表示に関して割りとよくある要望)
  • キャッシュしておく仕組みが無い

ということでとても融通が利きそう!


参照元記事:

Rails4でJSONを作るならto_jsonよりjbuilder - しめ鯖日記

子テーブルのレコード件数を取る際に使える!counter_culture

Ruby on Rails

投稿に対するコメント数などを取る際に、複雑なSQLを書こうとしていたのですが、 counter cache(カウンターキャッシュ)というものを使えばとてもスマートに各投稿へのコメント数が取得できました。

さすがRails、痒いところに手が届きます!

投稿数が多くなることを考えて、今回はcounter_cultureというgemを使うことにしました。

下記のような親子関係のテーブル(1対多)

親:Question, 子:Answer


ステップは下記

  1. gemを追加

  2. 親テーブルにカウント数格納用のカラムを追加

  3. 子のモデルにcounter_cultureの記述を追記


1. gemを追加

gem 'counter_culture'

2. 親テーブルにカウント数格納用のカラムを追加

ジェネレーターを使う場合

$ rails generate counter_culture Question answers_count

Migrationファイルを使い、下記のようなカラム追加でもいけます
add_column :questions, :answers_count, :integer, null: false, default: 0


3. 子モデルにcounter_cultureの記述を追加

app/models/questions.rb

class Question < ApplicationRecord
  has_many :answers
end


app/models/answers.rb

class Answer < ApplicationRecord
  belongs_to :question
  counter_culture :question, column_name: 'answers_count'  #任意のカラム名を付ける場合のみcolumn_nameが必要
end


Viewでは他の値と同様に下記のように呼び出せます。

<% @questions.each do |question| %>
  <%= question.title %>
  <%= question.answers_count %>
<% end %>

LEFT OUTER JOINとGROUP BYを使ったSQLを書くのに苦労していたのでとても助かりましたε-(´∀`*)ホッ

参照元

関連レコード数の集計(カウンターキャッシュ) - Qiita

Rails5 マイグレーションファイルでのindexの書き方が変わった

Ruby on Rails

add_indexで書いていた箇所がカラム追加と同じメソッド内にt.indexという形で書けるようになったみたいです。

Class CreateQuestions < ActiveRecord::Migration[5.0]
  def change
    create_table :questions do |t|
      t.string :title
      t.text :content

      t.index :title, unique: true #こんな感じ
    end
  end
end

元の書き方でも動きますが、すっきり!

Rails 5でアプリ作成してみた

Ruby on Rails

installする際に少しハマりましたが、基本はRails4系と変わらなかったのです。

1. Rubyのバージョンを最新にする

Rails5ではRuby 2.2.2 以降が必須らしい。
ローカルのworkingディレクトリに移動し、Rubyのバージョンを確認
$ ruby -v

2.3.0だったので、アップデートは不要。

ちなみに最新版を確認するコマンドは下記

$ rbenv install --list

2. Railsのバージョンを最新にする

現在のバージョンは4.2.5だったので、最新版5.0.0をインストール!

$ gem install rails --pre

--preオプションで最新版を指定できるらしい

エラー発生 (ノω・、)

nio4rというものをinstallする箇所でコケている!

logファイルを確認

$ cat /Users/kaorina/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/extensions/x86_64-darwin-13/2.3.0-static/nio4r-1.2.1/mkmf.l og

xcodeiosをゴニョゴニョするというメッセージが出ていただので(スクリーンショット取り損ねました。。)
xcodeを立ち上げて、ステップに沿ってアップデート。
そして、再度installコマンドを実行したらイケた!

以下のステップはRailsのバージョン問わず同じです。

scaffoldコマンドでサクッとアプリを作成

newコマンドでアプリの土台を作成。(DBはPosgreを指定)

$ rails _5.0.0.1_ new threedprinterq -d postgresql

アプリのフォルダに移動しbundle install

今回のアプリ名は threedprinterqというQ&Aアプリを作ります

$ cd threedprinterq
$ bundle install

config/application.rbを編集

require_relative 'boot'

require 'rails/all'

Bundler.require(*Rails.groups)

module Threedprinterq
  class Application < Rails::Application
    config.time_zone = 'Tokyo'
    #config.i18n.default_locale = :ja #日本語使う指定は後で有効にする
    config.active_record.default_timezone = :local

    config.autoload_paths += %W(#{config.root}/lib)  #lib以下を読み込むように設定

    config.generators do |g|
      g.test_framework :rspec, #テストはrspecを使用。コントローラーのテストのみ自動作成
        fixtures: true,
        view_specs: false,
        helper_specs: false,
        routing_specs: false,
        controller_specs: true,
        request_specs: false
      g.fixture_replacement :factory_girl, dir: "spec/factories" #factory_girlを使用
      g.assets     false
      g.helper     false
    end
  end
end

Rails4では下記も記載していましたが、5ではコールバックの処理方法が変わったのでいらないのかなーと思っています。
config.active_record.raise_in_transactional_callbacks = true
参照: Rails アップグレードガイド | Rails ガイド

posgreを起動

$ postgres -D /usr/local/var/postgres

DBを作成

$ bundle exec rake db:create

質問用のCRUDを作成

カラムはtitile, とtext型のcontentという構成
$ rails g scaffold question title content:text

Migration実行

$ bundle exec rake db:migrate

ブラウザで確認

$ rails sを実行後、ブラウザで http://localhost:3000/ にアクセスすると

素敵なwelcomeページが出てきた!わーい! f:id:Kaorina:20161031132722p:plain

qiitamarkdownのプレビュー画面をboorstrapのモーダルウィンドウで表示する

Ruby on Rails javascript

Q&Aサイト等で投稿内容をMarkdown形式で書いて、投稿前に確認出来るプレビューWindowを作りました。 Bootstrap3でモーダルウィンドウというものがあるので簡単にいい感じのWindowが使えました。

Markdown形式をHTML形式に変換してくれるライブラリqiitamarkdownについてはこちら
GitHub - increments/qiita-markdown: Qiita-specified markdown processor.

イメージ

内容欄に記述後、プレビューボタンを押すとwindowがプレビュー画面が確認できます。 f:id:Kaorina:20161028142058p:plain

方法はこちら

previewページ用のルーティングを追加

今回、質問投稿用の画面(questions)以下に実装する
config/routes.rb

resources :questions do
  post :preview, on: :collection
end

controllerにpreviewアクションを追加

app/views/controllers/questions_controller.rb

  def preview
    markdown = qiita_markdown(params[:text])
    render text: markdown
  end

private

  def qiita_markdown(markdown)
    processor = Qiita::Markdown::Processor.new
    processor.call(markdown)[:output].to_s.html_safe
  end
  

viewの編集

app/views/questions/new.html.erb app/views/questions/edit.html.erb ともに同じパーシャルを呼び出し

<%= render 'form' %>

app/views/questions/_form.html.erb

<%= form_for(@question) do |f| %>
--- 中略 ---
  <div>
    <%= f.text_field :title, class: "form-control", placeholder: "タイトル" %>
  </div>

  <div>
    <%= f.text_area :body, class: "form-control", placeholder: "内容", row: 10, required: 'required' %>
    <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal" id="preview_button">内容欄プレビュー</button>
  </div>
  
  <%= render 'preview_window' %>

モーダルウィンドウ用のviewを作成

app/views/questions/_preview_window.html.erb

<div class="modal fade" id="myModal">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title">内容プレビュー</h4>
      </div>
      <div class="modal-result" id="modal-result">
      </div>
    </div>
  </div>
</div>

previewボタンが押下された時に発火するAjaxを作成

app/assets/javascripts/preview.coffee

$ ->
  update_preview = (text) ->
    $.ajax
      url: '/questions/preview'
      type: 'POST'
      data: {text: text}
      success: (data) ->
        $('#modal-result').html(data)
        return
      error: (xhr, status, err) ->
        $('#modal-result').html 'エラーが発生しました ' + err
        return

  $('#preview_button').click ->
    update_preview($('#question_body').val())
    return

下記を参考にさせていただきたました。
こちらはプレビューWindowでなく、同じページ内にライブプレビューを実装されています。
ありがとうございました :)

GitHub - katoy/qiita_markdown_example: makedown のライブプレビューのサンプル

railsの変数をjavascript内で使えるgonが便利だよ

Ruby on Rails javascript

asset配下のjavascript内でrailsの変数を使いたい場合、 gonというGemを使えばすぐに呼び出せます。

Gemをインストール

Gemfile に下記を追記後、bundle installを実行

gem 'gon'

application.html.erbへ設定を追記

app/views/layouts/admin/application.html.erb

<head>タグ内に設定の一筆を書く

<%= include_gon %>

controller内に変数を書く

app/controllers/hoges_controller.rb

def hohohoho
  gon.user_name = "Kaorina" #ユーザー名Kaorinaをgon.xxxという変数名に入れる
end

javascript側から変数を参照!

app/assets/javascripts/hoge.js

$(document).ready(function() {

    var userName = gon.user_name #コントローラーで設定した変数名 gon.xxxでよびだせる
});

配列、ハッシュも渡すことができるようです。

gon.name = ["Kaorina", "Suzukina"]
gon.name = { first: "Kaorina", last: "Suzukina"}