20200214のRailsに関する記事は19件です。

[Rails]gem devise インストール

deviseとは

deviseとはrailsで作ったwebアプリケーションに簡単に認証機能を実装できるgemのことです。認証機能はログイン、ログアウト機能の事です。

deviseをインストール

1. Gemをインストールするために、Gemfileに追記

基本的に1番下に記載するのが無難です。

gem 'devise'

以下はNG例です。
a.png
上記の記述だとdevelopment環境から、テストや本番環境に移行していくので、その際に対応できなくなります。

2. Gem記載後、実際にdeviseのインストール

bundle install

を実行してgemを読み込みます。

3. deviseが入ったか確認

Gemfile.lockというファイルにはインストールされたgemが一覧表示されているので、ここに'devise'が入っているか確認します。

4. rails g devise:installを実行

deviseの設定ファイルをrailsアプリケーションにインストールするためにbundle installの他にもdevise用のインストールコマンドを実行します。

rails g devise:install

Userモデルを作成する場合

deviseで使用する場合は、通常のrails g modelではなく以下のコマンドを実行します。

rails g devise user

※必要に応じてマイグレーションファイルやmodels/user.rbを訂正しましょう。

マイグレーションを実行

userモデルをもとに、データベースを作成します。

rails db:migrate

コマンドミスを起こした場合

rails d model user
を実行します。
訂正後、
rails db:rollback
を実行します。

ビューファイルの作成

rails g devise:views users

これでapp>views>users ←ここにファイルができます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

schema.rbにマイグレーションファイルが反映されない!

開発環境

ubuntu(WSL)
Rails 5.2
Postgresql

想定エラー内容

ActionView::Template::Error (PG::UndefinedColumn: ERROR:  column tasks.user_id does not exist
LINE 1: SELECT "tasks".* FROM "tasks" WHERE "tasks"."user_id" = $1 O...

原因

一概には言えないですが、おそらくrails:db migrateした後にマイグレーションファイルを書き換えている。

対処法

rails db:migrate:statusで反映されているか確認する。
upは反映済み。downは未反映。

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200213054056  Create users
   up     20200214132021  Add user id to tasks

rails db:migrate:rollbackで戻す。
マイグレーションファイルを書き換えて再び、rails db:migrateを実行する。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】中級者になるためのリファクタリング

Railsのリファクタリングの記事は、良記事がたくさんあります。

でも「初心者向け」を謳っているにしては項目が多くて、いきなり全部を意識するのは無理だなってなりました。初心者向けって謳い文句の記事に、自分のできていないことがたくさん書かれていると「お前は初心者以下だ」って言われているみたいでちょっと嫌な気持ちになりますね。

他の方が書いた記事の中から、私がまず意識しようと思ったことをピックアップしてまとめようと思います。

controllerを薄く、modelを厚く

可能な限り処理をメソッドとして切り出して、controllerを薄くします。
modelがFatになることは、最初は目をつぶります。

viewの中の処理をhelperに移す

viewの中に処理がゴチャゴチャと書かれていると、コードが混沌としてしまうので。

パーシャルをどんどん使う

パーシャルを使って、可読性と再利用性を上げます。

jsはjsファイルに

ページごとのjsファイルを作り、そこに書く。

繰り返し使う文字列をlocaleに定義する

コードをDRYに。時刻のフォーマット等もlocaleを使い、strftimeは基本使わない。

コードの行数を減らす

基本的に以下の記事からのピックアップです。超良記事なのでまだ読んでいない人はぜひ。
[初心者向け] RubyやRailsでリファクタリングに使えそうなイディオムとか便利メソッドとか

最初はとっつきにくくても、短く書ける記法はどんどん使って慣れていくべき。慣れたら難しくないものも多そうだし、他の人が書いたコードを理解する能力も上がる。
とはいえ全部いきなりは大変なので、まずは以下を意識する。

なるべくネストしない

早めにreturnで返すなど、出来るだけネストを減らすことを意識します。

後置if

send_mail_to(user) if user.active?

三項演算子

user.admin? ? "I appreciate for that." : "Thanks"

ぼっち演算子

#if parent.children && parent.children.singleton?
if parent.children&.singleton?

||=でnilだった場合のみ代入

@twitter_client ||= Twitter::REST::Client.new 

その他書き方

if + notではなく、unless

user.destroy unless user.active?

条件にorとかandが含まれる時は複雑になるのでifを使う

戻り値を返すときにreturnを使わない

def build_message(user)
  message = 'hello'
  message += '!!' if user.admin?
  #return message
  message
end

+ではなく#{ }

#"Hello, " + user.name + "!"
"Hello, #{user.name}!"

%w( )、%i( )

#actions = ['index', 'new', 'create']
actions = %w(index new create)
#actions = [:index, :new, :create]
actions = %i(index new create)

いつかやる

・他の便利な文法いろいろ
・RESTなcontrollerを意識する
・Concern(controllerで共通で使う処理をまとめるやつ)を使う
・デコーダー?ViewModel?
・サービスクラス?

参考

[初心者向け] RubyやRailsでリファクタリングに使えそうなイディオムとか便利メソッドとか
脱Rails初心者のためのリファクタリングガイド
実務で学んだRailsの設計・リファクタリング

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】localeの使い方

localeとは

もともとは多言語化用の言語ファイル。
多言語化以外にも、何度も使う文言を一限管理するために使われる。
住所、会社名、時間をフォーマットする時の形式、今日の日付を出力する時の形式などを管理する。

基本

config/application.rbでデフォルトの言語を指定

config/application.rb
config.i18n.default_locale = :ja

config/locales以下にlocaleファイルを作成

config/locales/ja.yml
ja:
  hello: Hello World
  company:
    name: サンプル株式会社
    address: 〒231-0001 神奈川県横浜市中区新港2丁目7−1
viewから呼び出す
<%= t('hello') %> <!-- Hello World -->
<%= t('company.name') %> <!-- サンプル株式会社 -->
modelから呼び出す
I18n.t('hello') #=> 'Hello World'
I18n.t('company.name') #=> 'サンプル株式会社'

ポイント

  • 呼び出し時、jaの部分は書かない
  • localeファイルの名前は何でも良い。
  • viewから呼び出す時はI18n.の部分を省略できる。
  • 同じキーのデータが複数あった場合、後に定義したデータで上書きされる

日付や時刻のフォーマットを定義する

config/locales/ja.yml
ja:
  date:
    formats:
      default: "%Y/%m/%d"
      long: "%Y年%m月%d日(%a)"
      short: "%m/%d"
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
      long: "%Y年%m月%d日(%a) %H時%M分%S秒 %z"
      short: "%y/%m/%d"
I18n.l(Date.today) #=> '2020/02/14'
I18n.l(Date.today, format: :long) #=> '2020年02月14日(金)'

I18n.l(Time.zone.now) #=> '2020/02/14 18:34:25'
I18n.l(user.created_at, format: :short) #=> '20/02/14'

ポイント

  • DateクラスやTimeクラスの値をセットすると、datetimeに定義した形でフォーマットする
  • formatを省略するとdefaultの内容が適用される。
  • lはlocalizeの略。ちなみにtはtranslateの略。
  • created_atupdated_atI18n.l()でラップすると、timeに定義したフォーマットで出力される。

変数を渡す

config/locales/ja.yml
ja:
  airmax: "NIKE AIRMAX %{year}"
I18n.t('airmax', year: 95) #=> 'NIKE AIRMAX 95'

モデルの情報を定義する

主にエラーメッセージ中のモデル名や属性名を日本語化する目的で。

config/locales/ja.yml
ja:
  activerecord:
    # モデル名
    models:
      user: ユーザー
      item: アイテム
    # モデルごとの属性
    attributes:
      user:
        name: 名前
        address: 住所
      item:
        power: パワー
  # 全モデル共通の属性
  attributes:
    created_at: 作成日時
    updated_at: 更新日時

階層分け

・スッキリ管理する
・conflictのリスクを減らす
ために、localeファイルを階層分けしてたくさん作ることも多い。

階層分けをする場合config.i18n.load_pathの設定をすることで、config/locale直下のファイル以外も参照できるように設定します。

config/application.rb
config.i18n.default_locale = :ja
#↓追加
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]

rails-i18n

rails-i18nというgemを使うと、よく使う単語を全部一気に導入できます。

多言語対応をする時には有用だと思いますが、日本語だけの場合はわざわざgemを入れなくても、上記のページに書いてある内容を全部コピペすれば良い気がします。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでFontAwesomeの導入〜大きさを変えるまで

はじめに

railsでFontAwesomeを使いたいと思い調べていたところ、
大きさまで変える記事が見当たらなかったため投稿してみました。

実行環境

Ruby : 2.6.3
Rails : 5.2.4
slim : 4.0.1
font-awesome-sass : 5.12.0
上記の環境で実装いたしました。

FontAwesomeの導入

Gemfileに以下を記述

Gemfile
gem 'font-awesome-sass'

gemをインストール

bundle install

/app/assets/stylesheets/application.css

/app/assets/stylesheets/application.scss
に変更する

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

/app/assets/stylesheets/application.scssに以下を記述

application.scss
@import "font-awesome-sprockets";
@import "font-awesome";

上記の順番通りに記述したことで、FontAwesomeが使えるようになった。

実行結果

font-awesome-sassがインストールされたことにより、ヘルパーメソッドが使えるようになりました。
このメソッドを使うことでアイコンを表示させます。

~.html.slim
= link_to "#" do
  = icon 'fab', 'amazon-pay'

これでアイコンが表示されました。
今回使用したアイコン

Qiita使用画像.png

大きさを変更する

上記で用いたヘルパーメソッドを用いることによって好みの大きさに変更することも可能です。

~.html.slim
= link_to "#" do
  = icon 'fab', 'amazon-pay fa-3x'

このメソッドは次のように構成されています。

icon 'スタイル名', 'アイコン名 (大きさ指定)'

大きさの指定をしない場合は、アイコン名のみを記述し、
大きさ指定をする場合は、アイコン名の後にスペースをあけ、'fa-(サイズ)'で好きな大きさに変更することができます。サイズに関しては公式サイトをご覧ください。
Font Awesome

最後に

初めて記事を投稿してみました。
分かりやすい記事になったかは不安ですが、誰かの助けになれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecでFactoryBotから複数のインスタンスをまとめて作成する【create_listを使用】

RSpecテストを書いているときに、複数のインスタンスをまとめて作れる便利メソッドcreate_listをご紹介します。


例えば下記のようにnoteのファクトリが定義されているとします。

factory/notes.rb
FactoryBot.define do
  factory :note do
    title { 'sample-note' }
    description { 'sample-description' }
  end
end

テストでnoteインスタンスを5つ作りたいと思ったとき、下記のように1つずつ作るのは大変です。

spec/system/xxx_spec.rb
RSpec.describe 'yyyy' do
  let(:note1) { create(:note) }
  let(:note2) { create(:note) }
  let(:note3) { create(:note) }
  let(:note4) { create(:note) }
  let(:note5) { create(:note) }
  ...
end

ここで登場するのがcreate_listです。
これを使えば、まとめてnoteインスタンスを作成することが可能です。

spec/system/xxx_spec.rb
RSpec.describe 'yyyy' do
  notes = create_list(:note, 5)
  ...
end

第1引数に元になるファクトリ、第2引数に作成する数を指定します。
notes =の部分は無くても可)

ちなみに属性の一部を上書きして作成することも可能です(下記参照)。

spec/system/xxx_spec.rb
RSpec.describe 'yyyy' do
  notes = create_list(:note, 5, title: 'Hello, World')
  ...
end

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】wicked pdfの導入方法、ハマったところと対応

導入方法

Gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
$ bundle install
$ rails g wicked_pdf
config/initializers/wicked_pdf.rb
WickedPdf.config = {
  :exe_path => "#{Gem.loaded_specs['wkhtmltopdf-binary'].full_gem_path}/bin/wkhtmltopdf"
}

ここで一度サーバーを再起動

pdfs_controller.rb
def bill
  respond_to do |format|
    format.html
    format.pdf do
      render pdf: 'bill',
             layout: 'pdf',
             encording: 'UTF-8',
             template: 'pdfs/bill'
    end
  end
end
views/layouts/pdf.pdf.erb
<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8' />
  </head>
  <body>
    <div id="content">
      <%= yield %>
    </div>
  </body>
</html>
views/pdfs/bill.pdf.erb
<p>請求書</p>

<style>
p { font-size: 20px; }
</style>
routes.rb
get 'pdf/bill.pdf', to: 'pdfs#bill'
#get 'pdf/bill'と書いて、`pdf/bill.pdf`にアクセスしても動く

ハマったところと対応

フォントサイズが全部小さい

普段のノリでフォントサイズを指定すると、全部小さく表示されます。
フォントサイズを大きい数値に変更して対応。

本番環境のみ、AssetPilelineのコンパイルエラー

レイアウト内のCSS、JS、mysite.jpg読み込み部分を全て削除。
CSSは全部インラインで記述。(別プロジェクトを作り、Sassで書き、コンパイルしたものをコピペする)

本番環境のみ、日本語が表示されない

本番のサーバーに日本語フォントをインストールして対応

$ yum install -y ipa-gothic-fonts
$ yum install -y ipa-mincho-fonts

その他注意点

  • flexboxが使えない
  • 画像、CSS、JS等のパスの指定方法が特殊
通常 wicked_pdf
cssファイル stylesheet_link_tag wicked_pdf_stylesheet_link_tag
jsファイル javascript_include_tag wicked_pdf_javascript_include_tag
iamgeファイル image_tag wicked_pdf_image_tag

参考

Rails で Wicked PDF 使って PDF を出力してみた ( 日本語もバッチリ )
railsでwicked_pdfの使い方

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Modelで定義した定数に他クラスやViewからアクセスする方法

表題の通り、定数にアクセスできずに困った人のための記事です。

結論

Model名::定数名とすればOKです。

解説

下記のUserクラスがあるとします。

models/user.rb
class User < ActiveRecord::Base
  MAX_NUMBER = 10
  ...
end

Viewから参照するには、下記のようにします。

views/xxx.html.erb
最大値は#{User::MAX_NUMBER}です。

 
これが下記のようになっていると、

views/xxx.html.erb
最大値は#{MAX_NUMBER}です。

uninitialized constant ActionView::CompiledTemplates::MAX_NUMBER

そんな定数は無いと怒られてしまいます。

以上です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Modelで定義した定数にViewからアクセスする方法

表題の通り、定数にアクセスできずに困った人のための記事です。

結論

Model名::定数名とすればOKです。

解説

下記のUserクラスがあるとします。

models/user.rb
class User < ActiveRecord::Base
  MAX_NUMBER = 10
  ...
end

Viewから参照するには、下記のようにします。

views/xxx.html.erb
最大値は#{User::MAX_NUMBER}です。

 
これが下記のようになっていると、

views/xxx.html.erb
最大値は#{MAX_NUMBER}です。

uninitialized constant ActionView::CompiledTemplates::MAX_NUMBER

そんな定数は無いと怒られてしまいます。

以上です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveRecordのトランザクションのネストに気をつけること

  • 確認した環境
    • Ruby 2.5.1
    • Rails 5.2.1
    • MySQL 5.7.27

腕試しに以下のスクリプトを実行した場合、最終行の User.first.name は何が得られるか考えてみてください.

User.first.update!(name: 'Alice')

ActiveRecord::Base.transaction do
  User.first.update!(name: 'Bob')

  ActiveRecord::Base.transaction do
    User.first.update!(name: 'Carol')
    raise ActiveRecord::Rollback
  end
end

User.first.name


正解はここをクリック

User.first.name の結果は Carol になります.
正解しましたか?
正解する人もいると思いますが直感と違うかと思います.









TL;DR

  • 可能ならトランザクションをネストしない設計する
  • トランザクションをネストする設計の際は requires_new: true を使う
    • 付けないと思いのよらないゴミができる可能性がある

検証

分かりやすくするため、SQLをコメントに加えました.

User.first.update!(name: 'Alice')
# BEGIN
# UPDATE `users` SET `name` = 'Alice' WHERE `users`.`id` = 1
# COMMIT

ActiveRecord::Base.transaction do
  # BEGIN

  User.first.update!(name: 'Bob')
  # UPDATE `users` SET `name` = 'Bob' WHERE `users`.`id` = 1

  ActiveRecord::Base.transaction do
    User.first.update!(name: 'Carol')
    # UPDATE `users` SET `name` = 'Carol' WHERE `users`.`id` = 1
    raise ActiveRecord::Rollback
  end
end
# COMMIT

User.first.name # => 'Carol'

requires_new を使った場合

いくつかのRDBにはトランザクションにSAVEPOINTをサポートしているものがあります.
このSAVEPOINTを利用するには requires_new を指定することで有効になり直感に近い挙動をする様になります.

User.first.update!(name: 'Alice')
# BEGIN
# UPDATE `users` SET `name` = 'Alice' WHERE `users`.`id` = 1
# COMMIT

ActiveRecord::Base.transaction do
  # BEGIN

  User.first.update!(name: 'Bob')
  # UPDATE `users` SET `name` = 'Bob' WHERE `users`.`id` = 1

  ActiveRecord::Base.transaction(requires_new: true) do
    # SAVEPOINT active_record_1

    User.first.update!(name: 'Carol')
    # UPDATE `users` SET `name` = 'Carol' WHERE `users`.`id` = 1
    raise ActiveRecord::Rollback
  end
  # ROLLBACK TO SAVEPOINT active_record_1

end
# COMMIT

User.first.name # => 'Carol'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ぼっち演算子

Deviseを使ったRailsのコードがあるとします。
これはDeviseを使用したときのcurrent_userに対して、nicknameカラムに
あるデータを取得して@nicknameに代入することを意図したものです。


@nickname = current_user.nickname


ただし、ログインしないときにこれを実行するとnilに対してメソッドを使おうとしてエラーになってしまいます。

これを回避できる記述がRuby2.3からできるようになりました。
それがどのような記述か、またそれはどのような動きをするのかについて説明していきます。

解答
@nickname = current_user&.nickname

&はsafe navigation operator、 lonely operator(ぼっち演算子)などと呼ばれる演算子です。

メソッドに続けて記述すると、そのメソッドがnilでなかった場合のみ右辺のメソッドが実行されます。

もしnilだった場合は全ての演算結果としてnilを返します。つまり@nicknameにnilが代入されます。

とても使い勝手の良い演算子のため、覚えて活用していきましょう。

なお、ぼっち演算子という命名は、&の記号が一人ぼっちで膝を抱えている人に見えるところからきています。

<参考記事>
https://kossy-web-engineer.hatenablog.com/entry/2018/09/20/060701
https://ja.wikipedia.org/wiki/Null%E6%9D%A1%E4%BB%B6%E6%BC%94%E7%AE%97%E5%AD%90
https://matt-note.hatenadiary.jp/entry/2018/11/14/083307

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのform_forで、bootstrap4を使ってフォームを横並びにする方法

概要

Railsのform_forを使っている箇所で、フォームを横並びにするのに少しハマったので、共有させていただきます。

環境

ruby 2.6.3 Rails 5.1.6 bootstrap 4.4.1

方法

bootstrapのグリッドシステム(rowとcol-(数字))を使う。
フォームをdiv.rowで囲って、横並びにしたい要素をdiv.col-
で囲む。
(上記少し誤解を与える表現ですが、下記注意点をご参照ください)

注意点

form_forメソッドの外側でなく、内側にdiv.rowを配置する。

具体的な方法(OKパターン)

home.html.erb
<%= form_for(:session, url: login_path) do |f| %>        
  <div class="row">
    <div class="col-2">
      <div class="form-group">            
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control' %>          
      </div>
    </div>
    <div class="col-2">
      <div class="form-group">            
        <%= f.label :password %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>  
    </div>
    <div class="col-2 align-self-end">
      <div class="form-group">        
        <%= f.submit "ログイン", class: 'btn btn-primary' %>
      </div>
    </div>
  </div>
<% end %>

スクリーンショット 2020-02-14 10.38.23.png

ダメなパターン

home.html.erb
<div class="row">
  <%= form_for(:session, url: login_path) do |f| %>        
    <div class="col-2">
      <div class="form-group">            
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control' %>          
      </div>
    </div>
    <div class="col-2">
      <div class="form-group">            
        <%= f.label :password %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>  
    </div>
    <div class="col-2 align-self-end">
      <div class="form-group">        
        <%= f.submit "ログイン", class: 'btn btn-primary' %>
      </div>
    </div>  
  <% end %>
</div>

スクリーンショット 2020-02-14 10.51.33.png

以上です。

その他

Qiitaにちゃんとした(ちゃんとしてますか!?)投稿をするのが初めてでした。
ご指摘あればぜひお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ルーティングを設定してCSVデータをダウンロードする方法

目的

  • Qiitaの記事に大まかな流れをテンプレート化しておき、作業効率を上げる。
  • 今までの実装では send_data しか使ったことなかったのでメモ

前提条件

  • ruby 2.6.0
  • ruby 5.2.2

手順

  1. routes.rbにCSVダウンロード用のルーティングを設定する
  2. コントローラーに変数を定義、その他諸々を設定
  3. 出力データを作成(アクション名.csv.ruby)
  4. CSVダウンロードボタンを設定したいviewファイルに書く

※コード内容は適当

1. routes.rbにCSVダウンロード用のルーティングを設定する

config/routes.rb
resources :card_infos do
  collection do
    # デフォルト形式をcsvに設定
    get :csv_download, defaults: { format: 'csv' }
  end
end
Helper HTTP Verb Path Controller#Action
csv_download_card_infos_path GET /card_infos/csv_download(.:format) card_infos#csv_download {:format=>"csv"}

2. コントローラーに変数を定義やその他諸々を設定

app/controllers/card_infos_controller.rb
class CardInfosController < ApplicationController
  def csv_download
    @cards = CardInfo.all

    # CSVダウンロードファイルのヘッダーを生成する
    create_csv_file_header("card_info_#{ Time.zone.now.strftime('%Y%m%d') }")
  end
end
共通化メソッド
  # CSVダウンロードファイルのヘッダーを生成する
  def create_csv_file_header(file_name)
    file_name = ERB::Util.url_encode(file_name) if (/MSIE/ =~ request.user_agent) || (/Trident/ =~ request.user_agent)
    headers['Content-Disposition'] = "attachment; filename=\"#{file_name}.csv\""
  end

3. 出力データを作成(アクション名.csv.ruby)

※viewディレクトリ配下に設置する

app/views/card_infos/csv_download.csv.ruby
require 'csv'

columns = %w(
  番号
  会社
  子会社
  カード種類
  カード会社
  管理区分
)

# encoding等のoptionは仕様によって変えてください
CSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv|
  csv << columns
  @cards.each_with_index do |card, number|
    csv << [
      number + 1,
      card.company.parent_company.company_name,
      card.company.company_name,
      card.card_type.card_type_i18n,
      card.card_type.card_company.card_company_name,
      card.management_type_i18n,
    ]
  end
end

4. CSVダウンロードボタンを設定したいviewファイルに書く

(例)app/views/card_infos/index.html.erb
<%= link_to csv_download_card_infos_path, class: "btn_blue btn_big csv_dl", download:"", style: "padding: 8px 25px; font-family: Arial;" ) do %>
  <i><img src="<%= asset_path 'icon_download.svg' %>" alt="CSVダウンロード"></i>CSVダウンロード
<% end %>

📂🌲変更するファイル

.
├── app
|   ├── controllers
|  |   |  
|  |   └── card_infos_controller.rb ②アクションを設定
|  |
|  |
|  |
|   └── views
|      |  
|      └── card_infos
|           |  
|           └── csv_download.csv.ruby ③出力ファイルを設定
|
|
├── config
|    |  
     └── routes.rb ①ルーティングを設定


④ViewファイルにCSVダウンロードボタンを設置

関連URL

send_dataでの日本語ファイル名文字化け対策(IE11対応)
【簡単3ステップ】RailsでCSV出力する方法

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ルーティングを設定してCSVデータのダウンロード(出力)を実装する方法

目的

  • Qiitaの記事に大まかな流れをテンプレート化しておき、作業効率を上げる。
  • 今までの実装では send_data しか使ったことなかったのでメモ

前提条件

  • ruby 2.6.0
  • ruby 5.2.2

手順

  1. routes.rbにCSVダウンロード用のルーティングを設定する
  2. コントローラーに変数を定義、その他諸々を設定
  3. 出力データを作成(アクション名.csv.ruby)
  4. CSVダウンロードボタンを設定したいviewファイルに書く

※コード内容は適当

1. routes.rbにCSVダウンロード用のルーティングを設定する

config/routes.rb
resources :card_infos do
  collection do
    # デフォルト形式をcsvに設定
    get :csv_download, defaults: { format: 'csv' }
  end
end
Helper HTTP Verb Path Controller#Action
csv_download_card_infos_path GET /card_infos/csv_download(.:format) card_infos#csv_download {:format=>"csv"}

2. コントローラーに変数を定義やその他諸々を設定

app/controllers/card_infos_controller.rb
class CardInfosController < ApplicationController
  def csv_download
    @cards = CardInfo.all

    # CSVダウンロードファイルのヘッダーを生成する
    create_csv_file_header("card_info_#{ Time.zone.now.strftime('%Y%m%d') }")
  end
end
共通化メソッド
  # CSVダウンロードファイルのヘッダーを生成する
  def create_csv_file_header(file_name)
    file_name = ERB::Util.url_encode(file_name) if (/MSIE/ =~ request.user_agent) || (/Trident/ =~ request.user_agent)
    headers['Content-Disposition'] = "attachment; filename=\"#{file_name}.csv\""
  end

3. 出力データを作成(アクション名.csv.ruby)

※viewディレクトリ配下に設置する

app/views/card_infos/csv_download.csv.ruby
require 'csv'

columns = %w(
  番号
  会社
  子会社
  カード種類
  カード会社
  管理区分
)

# encoding等のoptionは仕様によって変えてください
CSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv|
  csv << columns
  @cards.each_with_index do |card, number|
    csv << [
      number + 1,
      card.company.parent_company.company_name,
      card.company.company_name,
      card.card_type.card_type_i18n,
      card.card_type.card_company.card_company_name,
      card.management_type_i18n,
    ]
  end
end

4. CSVダウンロードボタンを設定したいviewファイルに書く

(例)app/views/card_infos/index.html.erb
<%= link_to csv_download_card_infos_path, class: "btn_blue btn_big csv_dl", download:"", style: "padding: 8px 25px; font-family: Arial;" ) do %>
  <i><img src="<%= asset_path 'icon_download.svg' %>" alt="CSVダウンロード"></i>CSVダウンロード
<% end %>

📂🌲変更するファイル

.
├── app
|   ├── controllers
|  |   |  
|  |   └── card_infos_controller.rb ②アクションを設定
|  |
|  |
|  |
|   └── views
|      |  
|      └── card_infos
|           |  
|           └── csv_download.csv.ruby ③出力ファイルを設定
|
|
├── config
|    |  
     └── routes.rb ①ルーティングを設定


④ViewファイルにCSVダウンロードボタンを設置

関連URL

send_dataでの日本語ファイル名文字化け対策(IE11対応)
【簡単3ステップ】RailsでCSV出力する方法

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] scriptタグ内でRuby/Railsのメソッドを使用する際のescape_javascriptについて

この記事を書くきっかけ

jQueryを書いていると、scriptタグ内でRailsのメソッド(正確にはapplication_helper内のメソッド)を使用したいときがあります。
正しい書き方をしないと正確に呼び出せないため、失敗した書き方と成功した書き方をまとめます。

前提

current_userの属性によってpathを切り替えるメソッドを
def hogehoge_path_forとし、application_helper.rbに定義します。

application_helper.rb
def hogehoge_path_for
  if ...
   hoge_path
  else
   hogehoge_path
  end
end

これをとあるviewのscriptタグ内で呼び出します。
let path = hogehoge_path_forの返り値
としたいです。

失敗例1

あるview
<script>
  let path = <% hogehoge_path_for %>;
</script>

普通はこう考えるはずですが、これだと呼び出せません。

失敗例2

あるview
<script>
  let path = <% hogehoge_path_for %>;
</script>

これも呼び出せません。

失敗例3

escape_javascriptというメソッドを使用します。
なお、これはaliasとしてjの使用が許されています。

あるview
<script>
  let path = <% escape_javascript hogehoge_path_for %>;
</script>

上記は下記と同じです。

あるview
<script>
  let path = <% j hogehoge_path_for %>;
</script>

どちらも失敗します。

失敗例4

=をつけます。

あるview
<script>
  let path = <%= escape_javascript hogehoge_path_for %>;
</script>

上記は下記と同じです。

あるview
<script>
  let path = <%= j hogehoge_path_for %>;
</script>

これもどちらも失敗します。

失敗例5

=を外して、''で囲います。

あるview
<script>
  let path = '<% escape_javascript hogehoge_path_for %>';
</script>

上記は下記と同じです。

あるview
<script>
  let path = '<% j hogehoge_path_for %>';
</script>

これもどちらも失敗します。

成功例

=をつけて''で囲います!

あるview
<script>
  let path = '<%= escape_javascript hogehoge_path_for %>';
</script>

上記は下記と同じです。

あるview
<script>
  let path = '<%= j hogehoge_path_for %>';
</script>


これで無事呼び出すことができました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby初心者の「結局インスタンスメソッドって何ですか?」にエンジニア歴30年のベテランさん超絶わかりやすいアンサー!!!

結論

インスタンスメソッドとは:インスタンスが(に)使えるメソッド(にくっつくメソッド)

助詞に関しては、インスタンスを主語として考えるか目的語として考えるかの違いです。図で表すとこんな感じスクリーンショット 2020-02-14 8.38.18.png

インスタンスはいわゆる勇者。その勇者の剣がメソッド。つまり「勇者の剣」=インスタンスメソッドなわけだ!

僕の誤解を招いた表現その1

インスタンスメソッドとはインスタンスを含む変数である。*間違い

僕の誤解を招いた表現その2

インスタンス変数にくっつくメソッドである *惜しい、変数ではなくインスタンス自身にくっつくのがインスタンスメソッド

そもそもインスタンスって?

インスタンス化

クラス(クラスがわからない人は下に説明あります!)はnewすると初期化(インスタンスを作成する方法は他にもあるが基本はnew)し、実体化することができます。実体化したものをインスタンスと呼びます。

インスタンス変数

インスタンスは自身の中に複数の値を所有することができます。Rubyでは @ で始まる名前がインスタンスが所有する変数、インスタンス変数です。インスタンスが持つというニュアンスが大切。インスタンスを指し示す変数ではないですよ!
スクリーンショット 2020-02-14 8.49.43.png

そもそもクラスってなんぞや?

1と2どちらも同義ですが、自分にわかりやすい方で構いません!

1. 新しい型を作る方法(型ってのは設計図みたいなものですね。)

2. データと処理のまとまりを部品としてわかりやすく定義する方法

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

組み合わせ爆発を回避せよ!Rails初心者エンジニアの工夫

初心者である私はif~elseの形に慣れ過ぎていた

最初は下記のように書いていた。(statusは絶対に値が入っているので1では省略)

1

if params[:title].present?
 if params[:price_gteq].present? && params[:price_lt].present?
   @products = Product.where("price>=? AND price<=?", "#{params[:price_gteq]}", "#{params[:price_lt]}").where(status: params[:status]).where(title: params[:title])
 else
  @products = Product.where(title: params[:title])
 end
else
 if params[:price_gteq].present? && params[:price_lt].present?
  @products = Product.where("price>=? AND price<=?", "#{params[:price_gteq]}", "#{params[:price_lt]}").where(status: params[:status]).where(title: params[:title])
 else
  @products = Product.all
 end
end

もっと簡単にかけるくない?と思いelseを抜いた。しかしまだゴールではない

2

@products = Product.all
    @products = Product.where(status: params[:status]).where(title: params[:title]) if params[:title].present?
    @products = Product.where("price>=? AND price<=?", "#{params[:price_gteq]}", "#{params[:price_lt]}").where(status: params[:status]) if params[:price_gteq].present? && params[:price_lt].present?
    @products = Product.where("price>=? AND price<=?", "#{params[:price_gteq]}", "#{params[:price_lt]}").where(status: params[:status]).where(title: params[:title])if params[:price_gteq].present? && params[:price_lt].present? && params[:title].present?

まだまだ綺麗にかけるぞ!と思い、さらに絞り込む。ここで考えないといけないのが
ActiveRecord::Relationは、where などで演算した後も ActiveRecord::Relation を返すということ
一瞬はてなマークが頭に浮かぶと思います。簡単にいうと、whereなどのActiveRecord::Relationは同じ型を返し続ける性質があるという。

そのことを踏まえて書いたコードがこちら(ゴール)

3

@products = Product.all
    @products = @products.where(title: params[:title]) if params[:title].present?
    @products = @products.where(status: params[:status]) if params[:status].present?
    @products = @products.where("price>=? AND price<=?", "#{params[:price_gteq]}", "#{params[:price_lt]}") if params[:price_gteq].present? && params[:price_lt].present?

###3番のゴールは雰囲気はこれと同じ。三番はこれを場合分けしている###
@products = @products.where(title: params[:title]).where(status: params[:status]).where("price>=? AND price<=?", "#{params[:price_gteq]}", "#{params[:price_lt]}")

今回のポイント

  1. 条件分岐は「存在するときだけ発動する」みたいなニュアンス
  2. ActiveRecord::Relationは同じ型を返し続ける
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails初心者が知っておきたい基礎知識|gem、Bundler など…

この記事では、《Rails初心者が知っておきたい基礎知識》について、
僕が学習した内容をまとめています。

  • gem とは…?
  • Gemfile とは…?
  • Gemfile.lock とは…?
  • Bundler とは…?

こういった疑問についてまとめています。

※本記事は、自分で学習したことのまとめ用として書いています。
尚、解説で誤った点があれば、スローして頂ければ喜んでキャッチしますのでお願い致します。

gem とは…?

books-918521_1920.jpg

『gem』は・・・

Rubyのライブラリ(パッケージともいいます)です。

『ライブラリ』とは…
プログラムの部品で、便利な機能をひとまとめにしたものです。

ライブラリを利用することで、自分で1からコードを書くことなく機能を実装することができるので、効率的に開発することが可能です。

Rubyのgemには便利なものがたくさんあり、例えば…

  • Rails(Webフレームワーク)
  • Devise(ログイン機能)
  • Kaminari(ページネーション)
  • unicorn(アプリケーションサーバ)
  • Active Admin(管理画面)

などがあります。
gemを使うことでログイン機能やページネーションといった機能も、簡単に実装可能です。

Gemfile とは…?

『Gemfile』は・・・

gemをインストールするための『設計書』のようなもので、基本的にこのGemfileに自分のアプリケーションに必要なgemを記述します。

Gemfile.lock とは…?

『Gemfile.lock』は・・・

gemをインストールした後の『ログ、記録』のようなもので、インストールしたgemの一覧とバージョンが記載されています。

【比較】Gemfile と Gemfile.lock の違い

Gemfile Gemfile.lock
更新方法 手動 自動
更新タイミング コマンド実行前 コマンド実行後
内容 インストールするgem インストールしたgem

【余談】なぜ Gemfile.lock が必要なのか…?

school-1.jpg

gemは、インストールした時期によってバージョンが変わってしまう可能性があるため…
例えば、本番環境と開発環境でインストールされるgemのバージョンが異なってしまう可能性があります。

しかし・・・

Gemfile.lockが使用するgemのバージョンを管理してくれるため、仮にgemがアップデートされても、使用するgemのバージョンは変更させないということが可能になります。

ちなみに・・・

Gemfile.lockを更新したい時は、bundle updateを使います。

Bundler とは…?

『Bundler』は・・・

gemを管理するためのgemで、Bundlerを使用することで…

bundle install
bundle update

などのコマンドが使用できるようになります。

gem同士は関連し合っていることが多いので、実際はGemfileに書いてあるgemの他にも、gemが必要になります。

そこで・・・

Bundlerは、それらも自動でインストールし、Gemfile.lockに記述してくれます。

bundle_movement.png

Bundlerは、特定のRailsアプリケーションに必要なgemパッケージをリストアップし、複数のコンピュータ間で簡単に同期が取れるようにするものです。

例えば・・・

上記の図のように開発者Bさんが、開発者AさんからRailsアプリのソースコードを受け取ったとします。

この時、Bさんはbundle installを実行するだけで、必要なgemをインストールすることができ、簡単に環境を構築できます。

【一覧】 よく使う Bundlerコマンド のオプション

コマンド 概要
bundle init Gemfileを生成
bundle install Gemfileに記述したgemをインストール
bundle update インストール済みのgemのバージョンを更新
bundle list インストール済みのgemを一覧表示する
bundle exec Bundlerでインストールしたgemを使用して、
コマンドを実行

【コマンド】 bundle install とは…?

bundle install

上記を実行すると・・・

Railsは、Gemfile.lockを元にgemのインストールを行います。

この時…

Gemfile.lockに記述されていなくて、且つGemfileに記述されているgem』

がある場合、『該当するgem』と『該当するgemに関連するgem』をインストールした後、Gemfile.lockを更新します。

反対に、Gemfile.lockに記述されていれば・・・

そこに記述されたgemを見て、それがGemfileの内容に矛盾していない限り、Gemfile.lockの内容に従いインストールします。

【コマンド】 bundle update とは…?

bundle update

上記を実行すると・・・

Bundlerは、Gemfileを元にgemのインストールを行い、その後Gemfile.lockを更新します。

【余談】上記2つのコマンドの使い分けを少し解説

camera-coffee-composition-1509428.jpg

bundle updateは・・・

その言葉通り、gemのバージョンを更新する時に使用します。

なぜなら・・・

bundle installコマンドでは、条件に矛盾が生じない限りGemfile.lockに存在するgemに関しては、更新作業を行わないからです。

bundle updateは、本番環境で安易に実行しないように注意しましょう。場合によっては、gemのバージョンにズレが発生し、クラッシュする可能性があります。

ですので・・・

bundle updateは、必ずローカル環境で実行してください。

また、bundle installに関しては・・・

Gemfileに新しくgemを記述した際に使用します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpec で ある要素のみが入った配列かどうかを確認したいときは contain_exactly を使う

概要

a,b,c のリソースがあるときに、a,c のみが順番は関係なく返されることをテストしたいときには、contain_exactly が便利です。

コード

let!(:user_a) { create(:user, name: 'a') }
let!(:user_b) { create(:user, name: 'b') }
let!(:user_c) { create(:user, name: 'c') }

it 'returns ac users do
  expect(User.hoge).to contain_exactly(user_a, user_c)
end

順番もテストするときは素直に eq matcher で配列を使う。

it 'returns ac users do
  expect(User.hoge).to eq [user_a, user_c]
end

参考:
contain_exactly matcher
https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/built-in-matchers/contain-exactly-matcher

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む