Enumerizeでenum値を扱う
Railsのモデルでenum値を扱うとき、enumerizeというgemを使うと便利です。
# Gemfile gem 'enumerize'
class Task extend Enumerize enumerize :status, in: { pending: 0, todo: 1, done: 2 } end
と書くと、
Task = Task.new(status: 'pending') #=> valid Task = Task.new(status: 'unknown') #=> not valid
のようにバリデーションがかけられたり、
Task.where(status: :pending)
のように検索を行うことが可能です。
また、以下のようにlocaleを設定することで
# ja.yml enumerize: task: status: pending: 待機中 todo: 対応する done: 対応済
Task.status.options # => [["待機中", "pending"], ["対応する", "todo"], ["対応済", "done"]]
のように値を返すようになり、enum値の入力でよく使うselectフォームで
<%= form_for @task do |f| %> <%= f.select :task, Task.status.options %> <% end %>
のように簡潔に記述できるようになります。
参考
定数値を環境ごとに分岐したいだけならRails.env.xxx? やconfig/environments以下のファイルに値を書くより環境変数にくくり出した方がよさそう
Railsで開発していると
if Rails.env.production? # production環境用の処理 elsif Rails.env.staging? # staging環境用の処理 else # それ以外の環境用の処理 end
という風に環境ごとの処理を書いたり、config/environments以下のproduction.rbやstaging.rb, development.rbに config.xxx
と各環境用の値を設定して、Rails. configuration.xxxと呼び出すことができます。
しかし、環境ごとに変わる部分が単なる定数値であれば環境変数にして
ENV['SOME_VALUE']
のように参照することでproduction.rbやstaging.rbに値をハードコーディングせずに済ませることができます(代わりにdotenvなどを使い、.envファイルを共有したりします)。
こうしておくと環境ごとの値を変更したい場合リポジトリにコードをコミットせず済ませることができるので、処理の流れは変更しないが利用する外部リソースのURIなどを試行錯誤するときなどでは楽になります。
参考
画面の横幅が大きい(小さい)ときのみ要素を表示する
画面を作成していると単純に要素を横に伸ばしたりでスマホとPCの表示が作れず、要素の順番や配置が変わってしまうことがあります。
その度合いがまるきり別物ならPC版とスマホ版で別のテンプレートを使えばよさそうですが、順番が数カ所違う程度ならスマホ版だけ、PC版だけ要素を表示する、という方法が取れればよさそうです。
BootStrapを使っている場合、visible-xx/hidden-xxを使うとxxのところに入った大きさに画面がなったら要素の表示/非表示を切り替えてくれます。
スマホの場合、たとえば画面サイズがxs(767px)以下なら表示するなら、visible-xsをクラスに追加し、画面サイズが768px以上なら表示しないならhidden-xsをクラスに追加することで実現できます。
<div class="hidden-xs" style="background-color: red;"></div><!-- 画面幅が768px以上のときだけ赤い要素が表示される --> <div class="visible-xs" style="background-color: blue;"></div><!-- 画面幅が767px以下になると青い要素が表示される -->
参考
少しだけ動作が速くなるコードの書き方3つ
レコードがあることを条件にしたいとき、findの代わりにexists?を使う
if User.find_by(email: "...") # something.todo end
のとき、findやfind_byではActiveRecordのインスタンスが生成されてしまう分遅いので、存在確認をしたいだけなら下記のようにexistsを使うとよいようです。
if User.exists?(email: "...") # something.todo end
多数のレコードを処理するとき、eachではなく、find_in_batchesを使って一度に生成するインスタンス数を制限する
User.all.each do |user| # something.todo end
としたいとき、
User.find_in_batches(of: 100) do |users| users.each do |user| # something.todo end end
正規表現にマッチするかどうか調べるとき、=~よりmatch?の方が速い
str =~ /regexp/
より
str.match?(/regexp/)
Railsのuniquenessバリデーションについて
uniquenessバリデーションを使うと、その項目の中で一意であるようにバリデーションがかけられます。
class User validates :email, uniqueness: true end
とすると、emailはUserの中で一意となるようにRails側でバリデーションをかける。
これを、Userが会社に属していて会社の中で一意になるようにバリデーションをかけようとすると
class User validates :email, uniqueness: { scope: :company_id } end
のように、scopeを使って、一意になる範囲を指定する。これをさらに、(例がいいかは少しおいておいて)会社の中で複数の部署に属するユーザはその都度User登録しようとかいう話になって、ある会社のある部署の中で一意というのが書きたいときは、
class User validates :email, uniqueness: { scope: [:company_id, :group_id] }
のように、scopeに配列を渡す形となります。
参考
RSpecでテストデータ用にファイルを読み込む
RSpecでテスト用のファイルを読み込む
RSpecでテスト用のファイルを読み込む際はデフォルトでは spec/fixtures/files
にファイルを置いて
file_fixture("example.txt").read
とすると、ファイルの中身がspec中で呼び出せる。
ファイル中にHTMLやJSONのレスポンスを入れておいてHTTPリクエストをモックしたい場合などに使える。
既にfixturesに他にファイルがあってそれらに合わせてファイルの場所を既定のパスから変更したい場合は
RSpec.configure do |config| config.file_fixture_path = "spec/custom_directory" end
で可能となる。
RSpecでファイルをアップロードする
テストデータでファイルを使いたい状況としては、HTTPリクエストのモック以外にもファイルのアップロードのテストなどがある。
その場合は fixture_file_upload
を以下のように使う。
post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
このメソッドで使うファイルのパスは config.file_fixture_path
ではなく、config.fixture_path
で指定するので注意。
参考
配列の要素全てが該当する条件を記述するallマッチャ
scopeみたいにフィルタリングした要素について、フィルタリング対象の要素について
let(:included) { Item.new(price: 250) } let(:not_included) { Item.new(price: 300) } let(:array) { [included, not_included] } subject(array.filtering) it '250円以下の品物が絞り込まれる' do expect(subject).to contain_exactly(included) expect(subject).not_to contain_exactly(not_included) end
のように、フィルターの境界値の値を持つ要素をフィルタリング前のコレクションに含めておいて、その要素が含まれる/含まれない、といった形でspecを書いていくこともできますが、allマッチャを使うことで
let(:sample1) { Item.new(price: 250) } let(:sample2) { Item.new(price: 300) } let(:array) { [sample1, sample2] } subject(array.filtering) it '250円以下の品物が絞り込まれる' do expect(subject.map(&:price)).to all(be <= 250) expect(subject.count).to eq 1 end
元の要素ではなく、絞り込まれた結果に注目してspecを書くことができます。