if it does't challenge you, it can't change you

RubyのWebエンジニアやってます。主にRuby on Railsのことで勉強になったこと書いていきます。

railsコマンドでファイル名を指定してseedデータを入れる方法

バージョン

テストデータやマスタデータの挿入

Railsにはデフォルトでdb/seeds.rbというファイルが生成されていて、
ここにインサート文などを書いておくと、
rails db:seedでデータをDBに挿入してくれます。

指定したseedファイルだけを挿入

ただ、特定のデータだけ挿入したいというときには、
毎回、seeds.rbを書き換えないといけません。
1回だけならまだいいですが、間隔をおいて何回も挿入しなければいけないときは、だいぶめんどくさいです。
そんなときのために、このような

rails db:seed_from_file SEED_FILENAME='seeds/ファイル名'

railsコマンドの引数でファイル名を渡して、seedデータを挿入する方法を書いていきます。
なお、ディレクトリの構成はこんな感じです。

root/
  lib/
    tasks/
      db.rake
  db/
    seeds/
      01_sample_seeds.rb
      02_sample_seeds.rb
      03_sample_seeds.rb

挿入したいテストデータをdb/seeds配下のファイルに記載している想定です。

タスクファイルを作成する

rails g task ファイル名でタスクファイルを作成することができます。
今回は、rails g task dbとコマンドを叩き、lib/tasks/db.rakeを作成しました。

どのseedファイルを実行するか記載

タスクファイルが作成できたら、中身を書いていきます。
今回叩きたいコマンドrails db:seed_from_file SEED_FILENAME='seeds/03_sample_seeds.rb'に合わせて書いていきます。
今回はこのような感じで書いてみました。

namespace :db do
  desc 'Load the seed data from SEED_FILENAME'
  task :seed_from_file => 'db:abort_if_pending_migrations' do
    seed_file = File.join(Rails.root, 'db', ENV['SEED_FILENAME'])
    if File.exist?(seed_file)
      puts "seeding -- #{ENV['SEED_FILENAME']}"
      load(seed_file)
    else
      puts "the seed file does not exist."
    end
  end
end

以下から各メソッドの解説です。

・namespace

apiのルーティングなどでよく使われるnamespaceメソッドですが、
ここではコマンドを叩くときのコマンド名みたいなものです。
これをネストしていくとコマンドもネストされます。

namespace :apple do 
  namespace :banana do
    namespace :orange do 
      task :eat do
        #省略
      end
    end
  end
end

であればrails apple:banana:orange:eat という風なコマンドになります。

・desc

メソッドの説明文になります。
rails -Tでタスクを表示したときに一緒に説明文が表示されます。

・task

実行するメソッド名です。
成功していないマイグレーションがあるとデータを入れるのはよくないので、先に'db:abort_if_pending_migrations'を実行するようにしています。

・File.join

seedファイルまでのパスを生成するメソッドです。
コマンドの引数であるSEED_FILENAMEからファイル名を受け取り、実行するファイルを特定します。
Rails.root, 'db'と記載しておくことで、コマンドからの引数であるファイルまでのパスを省略できます。
load(seed_file)で実行しています。

まとめ

僕のカップル専用家計簿アプリでは、
月毎に合計値や前月などを計算しています。
そのため、月が変わると計算する値がなく、
真っ白になってしまうページがあり、
実装するときに不便になるので、
今回のようなテストデータを入れられるタスクを作りました。
(まだ毎月挿入するシードデータを作ってないですがw)
いろんなメソッドを使いますが、どれも単純なものなので、
試してみてください。
改善点あれば是非ご指摘ください。

【Ruby】もっと早く知っていればよかったmapメソッド

mapメソッド

mapメソッドとは、配列やハッシュに対して使えるメソッドで、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。
↑と、Rubyリファレンスには書かれております。
このメソッドを知った時は、このメソッドを知らないのは罪だと思いましたwww
なぜなら、このメソッドを知らない自分はこんなことをやったらからです。

mapメソッドを知らないとき

半年ほど前から、カップル専用の家計簿アプリを作っているのですが、家計簿には当然カテゴリ分けがあります。
実装していく中で、特定の条件のカテゴリのidを集めたいと思いました。
まず、何回も使わなければいけないと思ったので、モデルにscopeを追加しました。

extract_category, -> {unscope(:order).select(:category_id).distinct.pluck(:category_id)}

すでにこのスコープを作るのにも一苦労しました。
orderが既にかかったものにselectなどがかけられなかったのです。
次に、このメソッドを呼び出すときです。

category_ids = (current_user_expenses.extract_category  + partner_expenses.extract_category)

このように、scopeを使ったとしても、何回もそのscopeを呼び出していました。

mapメソッドを知った後

それがmapメソッドを使うとこんなにも簡単に書けます。

category_ids = (current_user_expenses + partner_expenses).map{|i| i.category_id}

scopeなどは必要なかったんです。
mapを使えばすごく簡単に書けました。
エイリアスメソッドとして、collectメソッドもあります。
どちらも同じ挙動ですが単語のニュアンスとして、collectのほうが読んでいてしっくり来るときがあります。
実は上記の場合は、collectのほうが僕は好きですね。

上記とは変わっていますが、実際の改修したコード こちらになります。

mapメソッドのようなブロックを使った便利メソッドは他にもいくつかあるので、しっかり勉強しようと思いました。

Rails5.1にReactを導入

react-railsとwebpackerの場合

インストール

Gemfileに以下を追加して、bundle install

gem 'react-rails' 
gem 'webpacker'

installコマンド実行

rails webpacker:install  
rails webpacker:install:react   

これでReactに必要なフォルダやファイルが生成されてReactが入る。 webpackerによりapp/javascript/packsにhello_react.jsxが生成されているのがわかる。
これはReactを使って、「Hello React」を表示させる簡単なものである。 そして、webpackerにより使えるようになったを追加していく。

Reactを使って「Hello React」を表示

コントローラー、ビュー、ルーティングを生成
rails g controller sample index

app/views/layouts/application.html.erbに以下のようにjavascript_pack_tagを追加

<!DOCTYPE html>
<html>
<head>
  <title>CalculatorReact2</title>
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag 'application', media: 'all' %>
  <%= javascript_include_tag 'application' %>
  <%= javascript_pack_tag 'hello_react' %>
</head>
<body>
  <%= yield %>
</body>
</html>

この状態でrailsサーバーを起動すると、「Hello React」が表示される。

react-railsのReactコンポーネントの作成

react-railsではReactコンポーネントは以下のコマンドで作成できる。

rails g react:component コンポーネント名

そうするとapp/javascript/component/ の配下にコンポーネント名.jsが生成される。

例えば、rails g react:component calculator だと

app/javascript/component/calculator.jsが生成される。

あとは好きなviewのそのコンポーネントを使いたいところで
<% react_component 'calculator' %>というようなview helperを記載しておくと、app/javascript/component/calculator.jsを読み込んでくれる。

app/views/layouts/application.html.erbには<%= javascript_pack_tag 'application' %>を記載しておけば読み込んでくれる。

react-rails のapplication.js

webpackerと比べて以下が追加されている。

var componentRequireContext = require.context("components", true)
var ReactRailsUJS = require("react_ujs")
ReactRailsUJS.useContext(componentRequireContext)

rails g react:componet で作成したコンポーネントファイルに以下を書かなくてもいい。

import ReactDOM from 'react-dom';
ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

 

webpackerのみ場合

react-railsがないとrailsサーバーを起動しても、Reactは動いてくれない。

railsサーバー起動後、別ターミナルで以下を叩きコンパイルする

./bin/webpack-dev-server

このままではreactを変更するたびにコンパイルしなければいけないので、

package.jsonに以下を追加し、

"scripts": {
"start": "rails s & bin/webpack-dev-server"
},

yarn startで同時に行ってくれる。

webpackerで読み込むコンポーネント

コンポーネントを入れるフォルダを作成

webpackerのみの場合、app/javascript/配下にはpacksフォルダしか生成されないので、ここで任意のフォルダを作成する。

app/javascript/calculator/

コンポーネントファイルを作成

app/javascript/calculator/calculator.js

import React, { Component } from 'react';

class Calculator extends Component {
 render() {
  return (
   <div>
    <h1>Calculator is working!!</h1>
   </div>
  )
 }
}
export default Calculator;

app/javascript/calculator/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Calculator from './calculator';

document.addEventListener('DOMContentLoaded', ()=> {
  const container = document.body.appendChild(document.createElement('div'));
  ReactDOM.render(<Calculator/>, container)
})

Viewで読み込む

app/javascript/packs/calculator.jsを作成し、その中に読み込むフォルダを記載

import 'calculator'

そうするとapp/javascript/calculator/ 配下のコンポーネントをimportする。

あとはapp/views/layouts/application.html.erbのHEADタグ内にreact-railsの時と同じようにjavascript_pack_tagを追加し、app/javascript/packs/calculator.jsを指定してあげるとviewで読み込んでくれる。

<!DOCTYPE html>
<html>
<head>
  <title>CalculatorReact2</title>
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag 'application', media: 'all' %>
  <%= javascript_include_tag 'application' %>
  <%= javascript_pack_tag 'calculator' %>
</head>
<body>
  <%= yield %>
</body>
</html>

HerokuとS3を連携

S3の初期設定

この記事のバケットの作成のところ(Imagemagikの導入の手前)までの通りすればできました。

【Rails】S3へ『CarrierWave+fog』を使って画像アップロードする方法 | vdeep

Gemfile

こちらを追加し、bundle install

gem 'fog-aws', group: :production
gem 'carrierwave', '~> 1.0'

○_uploader/rbに以下を追記

if Rails.env.production?
    storage :fog
  else
    storage :file
  end

carrier_wave.rb

$ touch config/initializers/carrier_wave.rbでファイル作成
作成したファイルに以下を追記

require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'


CarrierWave.configure do |config|
  if Rails.env.production?
    config.storage = :fog
    config.fog_provider = 'fog/aws'
    config.fog_credentials = {
      # Amazon S3用の設定
      :provider              => 'AWS',
      :region                => ENV['S3_REGION'],
      :aws_access_key_id     => ENV['S3_ACCESS_KEY'],
      :aws_secret_access_key => ENV['S3_SECRET_KEY']
    }

    config.fog_directory     =  ENV['S3_BUCKET']
  else
    config.storage :file
    config.enable_processing = false if Rails.env.test?
  end
end

ここの下の画像の人の例を参考にしました。 f:id:shoheimoment:20171215112549p:plain

herokuの環境変数の設定

S3の初期設定で取得できているcredentials を前項のENVの[ ]内の変数を下記コマンドで設定していく。

heroku config:set S3_REGION=
heroku config:set S3_ACCESS_KEY=
heroku config:set S3_SECRET_KEY=
heroku config:set S3_BUCKET=

※credentialsがわからなくなったら、アクセスキーIDとREGIONは次から確認できる。 ルートアカウント認証情報を使用してサインイン→ヘッダーのユーザ名のdropdownからセキュリティ認証情報を選択→左のユーザー→ユーザーを選択→認証情報タブ secret keyはどう確認するかわからないからユーザーを作った時にcredentials.csvをダウンロードしておこう!

デプロイ

これでデプロイしてみるとできている。できなければ、DATABASEのリセットとかを試してみるといいかも。

アコーディオン機能の三角がくるっと回るやつの設定方法

html

<dt><p><img src="./images/triangle.png" class="arrow">&emsp;学歴</p></dt>
<dd><p>長岡技術科学大学 環境工学課程卒業</p></dd>
<dt><p><img src="./images/triangle.png" class="arrow">&emsp;実績</p></dt>
<dd><p>オーストラリアワーキングホリデー<br>6,000kmヒッチハイク旅制覇</p></dd>
<dt><p><img src="./images/triangle.png" class="arrow">&emsp;SNS</p></dt>

1. クリックファンクションを設定

2. クリックした要素の三角の画像を取り出す$('img', this)

3. アコーディオンを開くと同時に2で取り出した要素にcssのスタイルを設定.toggleClass('dropdown_toggle')

$(function(){
  $('.side_menu dd').hide();
  $('.side_menu dt').click(function(e){
    // toggleClassは開くときにクラスを当てる
    // thisの子孫要素である'img'を取り出す
    $('img', this).toggleClass('dropdown_toggle');
    $(this).next('dd').slideToggle()
  });
})

4. 3で設定される、三角を回転させるためのスタイルを設定

.dropdown_toggle {
    -webkit-transform: rotate(90deg);
            transform: rotate(90deg);
}

バリデーションの日本語表示の仕方

1. 辞書ファイルをダウンロード

まずプロジェクトのトップで下記コマンドを実行して、辞書ファイルをダウンロード。プロジェクトのトップとはアプリ名がsampleであれば「:~/workspace/sample (master) $  」のようにディレクトリを移動してからという意味

# 日本語のテンプレートをDL
wget https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml -P config/locales/

 

2.モデル用の辞書ファイルを分割して作成

そうするとconfig/locales/に「ja.yml」というファイルが作成されているのがわかる。
このファイルは長いので管理が大変になるため、モデル用のファイルはconfig/locales/下に別で作成する。

 

ファイル構成例

config/locales
         ├── en.yml
         ├── ja.yml
         └── model.ja.yml

 

3. ファイルに辞書を記載していく。

---
ja: activerecord: models: contact: お問い合わせ attributes: contact: name: お名前 email: メールアドレス content: お問い合わせ内容

※このときインデントが少しずれているだけで読み込まれないので注意。
上記だとモデルcontactの属性値の辞書を記している。

 

4. しっかり読み込ませる

辞書が完成したら、このファイルは自動では読み込まれないので、config/application.rb に下記を追加して、起動時に読み込まれるようにしておく。

config.i18n.default_locale = :ja

 

これで Rails を起動し直すと、validates とかのエラーメッセージが日本語化されている

Rails5 バリデーションの書き方

バージョン

Rails 5.1.4

 

対象

下図のようなお問い合わせフォームのバリデーションを作成していく。

f:id:shoheimoment:20171014161429p:plain

 

文法

 

validates シンボル1,シンボル2, シンボル3,・・・, バリデーションの種類: {オプション: true} 


※カラムのシンボル名は→「:」+「カラム名
※「バリデーションの種類:」の後スペースが必要。presence:trueのようにスペースがないと:trueをシンボルだと認識してしまう。

バリデーションの種類やそのオプションはガイドを参考に

 ※ガイドに書いてある「%{value}」はコントローラーから送られてくる変数ではなく、バリデーションの種類とそのオプションに応じて使える変数である。too_longの%{count}も同じ

 

こんな感じ・・・

Rails5でバリデーション

 2行目は名前の欄に記入された文字へのバリデーション。
presence: は空欄の場合、エラーを発生させるバリデーションの種類。
length: は文字数設定。
maximum: や too_long: はlengthのオプション。
8行目の
format: は設定したパターンと同じでないときエラーを返すもの
message: はformatのオプション。エラーが発生したときに
「・(カラム名)  (メッサージ)」の形でエラー文を表示
formatだけでなく色んなバリデーションへつけられる。

エラー結果↓↓↓

f:id:shoheimoment:20171015200312p:plain

 

ただ、上記だけでは上のようにモデルの属性値は日本語になりません。

モデルの属性値の日本語化はこちらにまとめました↓↓↓

shoohei.hatenablog.com

 

 フレキシブルなメソッドでの書き方

メソッドであればより柔軟にバリデーションを設定できる。下のコードは上のコードとほぼ同じ結果になる。メソッドを使うことで日本語翻訳設定をしなくても日本語でエラー文を表示できる。

Rails5 バリデーション メソッド形式


メソッドにするときは「validates」ではなく「validate」と単数形で「:メソッド名」
引数の渡し方は不明。
とりあえず「validate :メソッド」にすればバリデーションするためのパラメーターが渡される。
name, email, contentのようにカラム名を引数のような形で扱う。

 

バリデーションを書いているときに・・・

バリデーションのコードを書いていて試したいときにはrails cが使える。
rails cで仮にエラーが出るように登録してみる

f:id:shoheimoment:20171014164838p:plain


・変数.vaild? →検証できてるかどうか

f:id:shoheimoment:20171014164900p:plain

 

・変数.errors.full_messages →エラー内容が確認できる

f:id:shoheimoment:20171014164928p:plain

 

・変数.errors →エラー発生時どんな内容のエラーか確認できる。

f:id:shoheimoment:20171014164946p:plain

 

・変数.errors.count →エラーの数

f:id:shoheimoment:20171014165000p:plain

 

・変数.errors.full_messages →エラーのメッセージ。

f:id:shoheimoment:20171014165016p:plain

 

また、複数のターミナルを開いて、このターミナルではサーバーを起動。こちらのターミナルではrails cを起動。などにしておくと、いちいちサーバーを落としたりしなくてもいい。