じぶんメモ

プログラミングのメモ、日常のメモとか。

rspec内でCSFR対策を有効にする

railsでpost送信を行う際に、画面を経由しないリクエストには422が返却される。 これはrailsCSRF対策である、ActionController::Base.allow_forgery_protectionがtrueになり、ApplicationControllerの protect_from_forgery with: :exceptionで、CSRFチェックでNGだった場合に例外を発生させる設定になっているため。

rspecで使われるtest環境ではallow_forgery_protectionがfalseになっているが、rspec時にCSFR対策を有効にするには ActionController::Base.allow_forgery_protection = trueとする。

describe 'createの確認' do
  let(:params) { FactoryGirl.build(:post).attributes }

  before { ActionController::Base.allow_forgery_protection = true }
  after  { ActionController::Base.allow_forgery_protection = false }

  it 'レスポンスに422が返されること', autodoc: true do
    post '/api/v1/posts/create', params
    expect(response.status).to eq(422)
    expect(response).not_to be_success
  end
end

direnvを使ってディレクトリごとの環境変数設定を行う

direnvを使って、そのディレクトリ下でのみ有効な環境変数を設定する。 前提mac

$ brew install direnv

インストール後、.bashrcまたは.bash_profileに以下の1行を追加

# bashの場合
eval "$(direnv hook bash)"

あとは環境変数を設定したいディレクトリに以下のコマンドで設定ファイルを作成する。

$ direnv edit .

そうすると、.envrcファイルが出来上がるので、中身を編集する。

# 一例
export HOST_URL='http://localhost:3000'

エラーが発生する場合はdirenv allowコマンドを実行する。

注意点として.envrc内にgit管理されるとまずい情報が載っている場合は.gitignoreに忘れずに含めること。

railsのモデルに、特定の条件下で動くvalidationを追加

ifを使用した条件分岐

例えば、bという項目がtrueの場合のみaの値は必須にしたい、とかあると思う。
with_optionsメソッドを使用して以下のように実装することができる。

class Post < ActiveRecord::Base
  # ifオプションで条件に合致する場合のみvalidatesを実行(published?はメソッド名またはmodelのboolean項目)
  validates :name, presence: true if: published?

  # 複数valitatesをまとめたい場合はwith_optionsを使用
  with_options if: :published? do
    validates :name,  presence: true
    validates :category,  presence: true
  end

  # unlessで条件に合致しない場合のみvalidatesを実行
  # lambdaを使うことで複数条件を指定できる
  with_options unless: -> { :hoge? || :foo? } do
    validates :name,  presence: true
  end
end

ただし、この場合、with_optionsブロック内部で更にif: を使用してしまうと、
内部のif条件の場合にしかvalidatesが実行されなくなる。

  # hogeの時しかwith_options内部が実行されない
  with_options if: :published? do
    validates :name,  presence: true
    validates :category,  presence: true, if: hoge
  end

  # with_optionsで定義したものと逆のもの(この場合はunless)を使用すれば問題ない
  with_options if: :published? do
    validates :name,  presence: true
    validates :category,  presence: true, unless: 'hoge.blank?'
  end

ネストしたvalidationを記載するなら、メソッドを一つ作って指定した方が良さそう。

validates :name,  presence: true, if: :require_validation?

def require_validation?
  return true if aaa? && bbb?
  false
end

onを使用した条件分岐

例えば新規登録(create)の場合のみバリデーションをかけたい時は、onオプションを使用する。

class Post < ActiveRecord::Base
  # createの時のみ実行
  validates :name, presence: true on: :create

  # if同様with_optionsを使用できる
  with_options on: :create? do
    validates :name,  presence: true
    validates :category,  presence: true
  end

  # こうすることで、pattern_hogeコンテキストが渡された時のみvalidatesが実行される
  validates :hoge, presence: true, length: { maximum: 3000 }, on: :pattern_hoge
end
class PostsController < ApplicationController
  def hoge
    # pattern_hogeコンテキストを指定
    post.save(context: :hoge.save(context: :pattern_validation))
  end
end

railsでenumを使う

modelに記述する。

class Post < ActiveRecord::Base
  enum status: { created: 0, drafted: 1, canceled: 2 }
end

こうすることで、Post.statusesとして、各Enumにアクセスすることができる。

pry(main)> Post.statuses            # => {"created"=>0, "drafted"=>1, "canceled"=>2}
pry(main)> Post.statuses[:drafted]  # => 1
pry(main)> @post = Post.new
pry(main)> @post.status = Post.statuses[:drafted]

また、インスタンスに対して、enumの各要素をメソッドとして使用することができる。

pry(main)> @post = Post.new
pry(main)> @post.status = Post.statuses[:drafted]
pry(main)> @post.drafted?     # => true
pry(main)> @post.canceled!
pry(main)> @post.canceled?    # => true
pry(main)> @post.drafted?     # => false

modelとの関連付けはせず、Enumとしてだけ使用したい場合、
locales下のymlファイルに記述する方法もあり。

ja:
  Enum:
    status:
      created: 0
      drafted: 1
      canceled: 2
pry(main)> Enum.codes(:status)
=> ["created", "drafted", "canceled"]

選択肢が限られているセレクトボックスの検証とかで、inclusionと合わせて使えそう。

シェルでディレクトリ内のファイルの数を調べる

例えばシェルでディレクトリ内に何ファイル存在するか調べるには以下の方法でチェックをする。

ls -FU1 '対象のパス' | grep -v / | wc -l 

ざっと解説すると、lsの結果をgrepに渡し、grep -vで不要な情報を削除し、
wcコマンドにわたし、数をカウントしている。lsオプションは以下の通り。

  • -F:ディレクトリが存在する場合は/を付与して表示する。
  • -1:lsの結果を1行ずつ表示
  • -U:lsの結果をソートしない(速度向上)

上記の例だとディレクトリを省いてファイルの数を調べている。
また、wcコマンドは、渡されたファイルのバイト数も表示するため、
-lオプションは、渡されたファイルの個数のみを表示させている。

ブックマークレットでフォームに自動的に入力するスクリプトを作りたい

テストとかで画面に値を入力する場合、 繰り返しテストをしていると、
毎回フォームに値を入力しないといけないのがダルい。
ブックマークレットで1クリックだけでフォームに値をセットできるスクリプトを作りたい。
とりあえずフォームの要素に値をセットする方法。

javascript:(function(){ document.getElementById("srchtxt").value = "てすと";}())

ついでに画面内のID全て取得するブックマークレット

javascript:(function() {var elements = document.getElementsByTagName("input");var i = 0; var ids = "";for (i = 0 ; i < elements.length ; i++){ids += elements[i].id + "\r\n";}alert(ids)}());

ブックマークレットスクリプトで改行すると美味く解釈されないので、基本的には改行なし。

コマンドプロンプトでgrep

コマンドプロンプトgrepするならfindstr

Windowsコマンドプロンプトgrepと同じことをしたい。
findstrコマンドが良さげ。

findstr [オプション]  "検索対象文字列" "検索対象のファイル"

以下よく使いそうなオプション

デフォルトでは正規表現での検索をしてくれないので、/Rオプションを使用する。

/I 大文字小文字を区別しない
/S ディレクトリを再帰的に検索する
/R 正規表現を使用する
/C 空白を含む文字を検索する場合に指定する
/N 一致した行数を表示
/V 指定した文字を含まない行をすべて表示

使用例

カレントディレクトリ以下で、”This is〜"という文字が含まれている箇所をresult.txtに出力する。

findstr /R /S  /C "This is.*" * > result.txt