コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

Railsでログに残したくないパラメータなどはfilter_parametersに指定する

他の人がやってくれていたアプリのみ触っていたので今後のためのメモ

環境

$ bin/rails -v
Rails 5.2.2

やりたいこと

ユーザログインを作る場合などに受け取ったパラメータをそのままパラメータとしてRailsログに書き出してしまうが、場合によってはRailsのログにそのような個人情報を書き出したくないということがあるので、それらを出さないようにしたい

やり方

フィルタリングしたいパラメータ名を filter_parameters に指定する。

記述方法はいろいろあるが、最近の記述で見かけるのは config/application.rb 内で下記のように書く方法

module {{アプリ名}}
  class Application < Rails::Application
    # ... 中略 ...

    # フィルタリング対象を追加、emailとpasswordは伏せる
    config.filter_parameters += ["email", "password"]
  end
end

というようにすると、 emailpassword はログには [FILTERD] と出て具体的な値は記録されない、

Sentryなどのツールも、この設定を踏襲してくれるので気になるものは一律設定しておくと便利。

参考リンク

Railsでのテストにおいて、リクエストホスト名はデフォルトだと www.example.com

どこで指定してんだろ、と思って調べてみたメモ

環境

$ bin/rails -v
Rails 5.2.2

概要

RSpecなどのテストにおいて、xxx_urlで生成されるurlのホスト名は www.example.com になる

たとえば自分のURLを生成してJSONとして返すコントローラーを作るとする

class UsersController < ApplicationController
  def index
    render json: {url: users_url}
  end
end

routes.rbはこんな感じ

Rails.application.routes.draw do
  resources :users, only: :index
end

これに対してrspecを書くと、以下のように書くとコケる

require 'rails_helper'

RSpec.describe "/users/", type: :request do
  subject { get users_path}
  context "正しくアクセスしたとき" do

    before do
      subject
    end

    it "期待した結果が返ってくる" do
      url = JSON.parse(response.body)["url"]
      expect(url).to eq("http://hoge.com/users")
    end
  end
end

このrspecを実行すると、以下のように結果が返ってきてドメイン名が固定で割り振られていることがわかる。

$ bundle exec rspec spec/request/user_spec.rb 
F

Failures:

  1) /users/ 正しくアクセスしたとき 期待した結果が返ってくる
     Failure/Error: expect(url).to eq("http://hoge.com/users")
     
       expected: "http://hoge.com/users"
            got: "http://www.example.com/users"
     
       (compared using ==)
     # ./spec/request/user_spec.rb:13:in `block (3 levels) in <top (required)>'

Finished in 0.07468 seconds (files took 2.93 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/request/user_spec.rb:11 # /users/ 正しくアクセスしたとき 期待した結果が返ってくる

結果にもあるように何も指定しないと www.example.com になる

どこでやっているのか

rails/actionpack/lib/action_dispatch/testing/integration.rb でテストのセッションの設定をしておりここらへんでやっている模様

    class Session
      DEFAULT_HOST = "www.example.com"

参考リンク

RailsにおいてModelのcreate!はブロックで囲めるし、それで関連レコードの作成ができる

こんな書き方あるんだ的なメモ

環境

$ bin/rails -v
Rails 5.2.2

やり方

今回使うモデル

# == Schema Information
#
# Table name: users
#
#  id         :bigint(8)        not null, primary key
#  name       :string(255)
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class User < ApplicationRecord
  has_many :message
end
# == Schema Information
#
# Table name: messages
#
#  id         :bigint(8)        not null, primary key
#  name       :string(255)
#  user_id    :integer
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class Message < ApplicationRecord
  belongs_to :user
end

書き方

createした自分自身をブロックに渡すことができる

User.create!(name: "Taro") { |user| Message.create(user: user) }
#   (0.3ms)  BEGIN
#  User Create (0.3ms)  INSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES ('Taro', '2018-12-24 17:12:42', '2018-12-24 17:12:42')
#  Message Create (0.3ms)  INSERT INTO `messages` (`user_id`, `created_at`, `updated_at`) VALUES (8, '2018-12-24 17:12:42', '2018-12-24 17:12:42')
#   (0.4ms)  COMMIT
#   (0.1ms)  BEGIN
#   (0.1ms)  COMMIT

同時にコミットしてくれるのでいろいろ整合性を取るときなどに便利。

RailsのActiveRecordのcreate_***は元からrelationがあるレコードがあっても新規で追加されるので気をつける

当たり前といえば当たり前なんだけど、冪等性保つ動きを期待しちゃう側面もあるのでメモ

環境

$ bin/rails -v
Rails 5.2.2

概要

relationを行っているモデルに対して create_{{モデル名}} というメソッドを実行すると連動したレコードが作れるが実行するたびに作られてしまうので注意

今回使うモデル

# == Schema Information
#
# Table name: users
#
#  id         :bigint(8)        not null, primary key
#  name       :string(255)
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class User < ApplicationRecord
  has_many :message
end
# == Schema Information
#
# Table name: messages
#
#  id         :bigint(8)        not null, primary key
#  name       :string(255)
#  user_id    :integer
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class Message < ApplicationRecord
  belongs_to :user
end

実際のコード

Message と一緒に User を作りたい場合にbuild_xxxを使うと以下のように作ることができる

message = Message.new
#=> #<Message id: nil, name: nil, user_id: nil, created_at: nil, updated_at: nil>
message.build_user
#=> #<User id: nil, name: nil, created_at: nil, updated_at: nil>
message.save!
# (0.2ms)  BEGIN
#            User Create (0.3ms)  INSERT INTO `users` (`created_at`, `updated_at`) VALUES ('2018-12-23 16:58:57', '2018-12-23 16:58:57')
#            Message Create (0.2ms)  INSERT INTO `messages` (`user_id`, `created_at`, `updated_at`) VALUES (3, '2018-12-23 16:58:57', '2018-12-23 16:58:57')
#            (1.9ms)  COMMIT
#            => true

create_xxxも使うと同様に作ることができるが、冪等性があるわけではないので呼ぶたびに新しくレコードがつくられてしまう。

message.create_user
#   (0.1ms)  BEGIN
#  User Create (0.9ms)  INSERT INTO `users` (`created_at`, `updated_at`) VALUES ('2018-12-23 17:02:05', '2018-12-23 17:02:05')
#   (0.5ms)  COMMIT
#=> #<User id: 4, name: nil, created_at: "2018-12-23 17:02:05", updated_at: "2018-12-23 17:02:05">
message.create_user
#   (0.1ms)  BEGIN
#  User Create (2.0ms)  INSERT INTO `users` (`created_at`, `updated_at`) VALUES ('2018-12-23 17:02:06', '2018-12-23 17:02:06')
#   (0.4ms)  COMMIT
#=> #<User id: 5, name: nil, created_at: "2018-12-23 17:02:06", updated_at: "2018-12-23 17:02:06">
message.create_user
#   (0.1ms)  BEGIN
#  User Create (0.2ms)  INSERT INTO `users` (`created_at`, `updated_at`) VALUES ('2018-12-23 17:02:11', '2018-12-23 17:02:11')
#   (1.5ms)  COMMIT
#=> #<User id: 6, name: nil, created_at: "2018-12-23 17:02:11", updated_at: "2018-12-23 17:02:11">

既にある場合には作られたくない、などをするときは

message.create_user if message.user.nil?

のようにしてあげないといけないので注意。

RSpecのitはブロックにしないことにより、箇条書きリストとして使うことができる

先日のt_wadaさんのライブコーディングで得た知見

環境

rspec (3.7.0)

概要

itはブロックを設けないことにより、pendingになるので、箇条書きとして機能する

例えば以下のようなUserモデルがあったとき

# == Schema Information
#
# Table name: users
#
#  id         :bigint(8)        not null, primary key
#  name       :string(255)
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class User < ApplicationRecord
end

こんなモデルへのテストを以下のように考える

  • 名前の長さは10文字以下
  • 名前には英語しか使えない
  • 名前には数字は使えない

そしてテストを書くときに以下のように書くと良い

require 'rails_helper'

RSpec.describe User, type: :model do

  it "名前の長さは10文字以下"
  it "名前には英語しか使えない"
  it "名前には数字は使えない"
end

こんな感じに書くと

$ bundle exec rspec spec/models/user_spec.rb 
***

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) User 名前の長さは10文字以下
     # Not yet implemented
     # ./spec/models/user_spec.rb:15

  2) User 名前には英語しか使えない
     # Not yet implemented
     # ./spec/models/user_spec.rb:16

  3) User 名前には数字は使えない
     # Not yet implemented
     # ./spec/models/user_spec.rb:17

このようにpendingになるので、足りてないものを書くときに気づくことができるので、TDDに最適。

関連リンク

『銀座Rails#4』に行ってきたよメモ

銀座Rails#4に行ってきたよメモです

各発表の感想


Rails アプリを個人開発からチーム開発に成長させる

スライドは見つけたら貼ります、なおQiitaのアカウントはこちら

感想

  • 一人で取り回していたサービスからチーム開発へ至るまでの経験談の話
  • before_action の乱用は避けるとか、accept_nested_attributes_for は怖いよねみたいなのはすごい理解できた
  • AtomicDesignをどうやって入れていくかをエンジニア発信で入れている事例だったのでスライドが公開したらサンプルコード含めてちゃんと読みたい。
  • SketchとAtomicDesignのべき論みたいなところは、ガイドラインをうまく整備できれば、デザイナー(NotCSSコーダ)とCSS的な概念を挟まずとも、粒度感をちゃんと合わせればいい感じに回るような気がした

関連リンク


Microservice is dead. Long live microservice (with Rails)

感想

  • MicroServiceというだけで多方面からマサカリが飛んできかねない現状とRailsで作ることに関しての実情をオムニバス形式で紹介された発表
  • 管理ツールだけで切り出すのはドメインの整理上おかしいという話があり、確かに、と思ってしまった。
  • 管理ツールにおいて中央においてSSOにするというのは、オーバースペック感があるけど、ドメインごとにサービスと管理ツールが同じなのであれば、大正解って感じを受けた
  • 秋刀魚の脂の乗り方と同じように、安易にViewを作れるところが売りなのに、SPAとAPIとかやってしまったら本来やりたかったことと違くない?というのを「パッサパサのRails」という表現したのはすごい刺さった
  • WebComponetsも楽しい思想だけど、一定プロダクトに持ち出すには説明責任が問われるのでなかなかえいやが踏み出せないなという感じ
  • 「標準への関心」というのも個人的には刺さった、多分基本の型を覚えるに通じる話だなと思った

関連リンク


実録テスト駆動開発

スライドはありません!

発表メモ

  • Rails + rspecを使ったTDDのライブコーディング
  • テーマは「地獄の軽減税率シュミレータライブコーディング」
    • 聞けば聞くほどテーマとしては秀逸で、すごい複雑な業務ロジック事例としては認知度含めて最高だった
  • rspecの内容を書いて、順次実装を書いていくスタイル
  • 途中でアクシデントはあったが、rspecを盛り盛り書いて、それをコードに落とし込んでいっていた

感想

  • 学びが多かった
    • it をブロックにしない(do endをつけない)と箇条書きのリストとして機能するというのは目から鱗
    • 最初に使いたい要素を it の中にべた書きして、そのあと必要なものを let に抜き出していくのもなるほどなぁという感じ
    • デバッグに関しても、一定letが信用できなくなったらbeforeで純粋にcerateするという方針をしていたのですごい納得感があった。
    • テーブルの定義も秀逸で、ルール的なものを xxx_methods みたいなテーブル名にしてたので、なるほどなー!!という学びがった
  • 軽減税率という複雑なビジネスロジックに対して、SQLアンチパターンにも通じるt_wadaさんならこうするみたいのが垣間見れてすごい勉強になった
  • これのリベンジ再演があたら見てみたい
  • 一定PHPカンファレンスの話もRubyに置き換えてほしかったりするがそれはまた別の話

関連リンク


全体を通しての感想

  • 一人から複数人へという観点の事例、マイクロサービスの加熱っぷりと現状に一隻投じる話、RalisにおけるTDDの進め方の一例、と三者三様の話が聞けて面白かった
  • 懇親会も初めて参加したのですがみなさん面白い話をされていたので学びがあった、次回も積極的に出たい

Rubyにおいてnilでも使えるメソッドを眺める

他の言語だとだいたいExceptionが起きるNULLだけど、rubyの場合は結構メソッドが用意されているので割と動いてくれる。そのメソッドたちを調べた

環境

$ ruby -v
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin18]

検証

使えるものを調べる

nil.public_methods
=> [:to_c, :&, :nil?, :===, :to_s, :inspect, :to_r, :to_i, :to_a, :to_h, :rationalize, :|, :to_f, :^, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :public_methods, :instance_variables, :method, :public_method, :define_singleton_method, :public_send, :singleton_method, :extend, :pp, :to_enum, :enum_for, :<=>, :=~, :!~, :eql?, :respond_to?, :freeze, :object_id, :send, :display, :hash, :class, :singleton_class, :clone, :itself, :dup, :taint, :yield_self, :untaint, :tainted?, :untrusted?, :untrust, :frozen?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :!, :equal?, :instance_eval, :==, :instance_exec, :!=, :__id__, :__send__]

結構ある。

使いがちなものダイジェスト

.to系

だいたい空とか0に準じるものになる。

nil.to_i
#=> 0
nil.to_f
#=> 0.0
nil.to_a
#=> []
nil.to_h
#=> {}
nil.to_s
#=> ""

?系

王道のnil?

nil.nil?
=> true

比較

nil.eql?(nil)
=> true

クラス判断系

nil.is_a?(NilClass)
=> true

nil.kind_of?(Integer)
=> false

参考リンク