Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: RailsでApplication Layer Encryption、rubocop-factory_bot登場ほか(20230614後編)

こんにちは、hachi8833です。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Rails

🔗 has_oneの関連付けでレコードが1つになるとは限らない


つっつきボイス:「thoughtbotの記事です」「has_one関連付けでレコードがいつも1つだと思ってはいけない?」「レコードが入ってしまえば複数になることはありえますね」

「そうならないためにはこうやってデータベース側に制約を追加する↓」「これならデータベース側で追加を阻止できますね」

# 同記事より
class CreateAccounts < ActiveRecord::Migration[7.0]
  def change
    create_table :accounts do |t|
      t.string :name
-     t.belongs_to :supplier, null: false, foreign_key: true
+     t.belongs_to :supplier, null: false, index: { unique: true }, foreign_key: true

      t.timestamps
    end
  end
end

参考: §2.2 has_one関連付け -- Active Record の関連付け - Railsガイド

🔗 Active Recordのメソッドで式展開を使ってはいけない(Ruby Weeklyより)


つっつきボイス:「これはもう定番の話ですね」「これは場合によってはえらいことになる書き方↓」

# 同記事より: やってはいけない書き方
User.delete_by("id = #{params[:id]}")
User.where("email = #{params[:email]}")

「記事にもあるように、サニタイズされない形でデータをクエリに渡すとSQLインジェクションにやられる可能性がありますね」

# 同記事より: SQLインジェクションの例
# user-provided parameter
params[:id] = "1) OR 1=1--"
User.delete_by("id = #{params[:id]}")
#=> User Delete All (4.2ms)  DELETE FROM "users" WHERE (id = 1) OR 1=1--)

保存版: Railsアプリケーションのセキュリティベストプラクティス(翻訳)

「ところでこういう記事を見るとArelを勉強しようという気持ちになってきますね」

Arelのススメ — Arelを使ってみよう

🔗 AsherahでRailsのApplication Layer Encryptionを実装する(Ruby Weeklyより)


つっつきボイス:「Application Layer EncryptionをRailsで実装する記事か、面白そう」「asherahというライブラリをasherah-ruby経由で使っているんですね」「asherahを開発したのはドメインレジストラで有名なGoDaddyか: よく見たらこの記事もGoDaddyのサイトだった」

godaddy/asherah - GitHub
godaddy/asherah-ruby - GitHub

Asherahは、Go版のAsherah Application Layer Encryption SDKをRuby FFI(外部関数インターフェイス)でラップしたものです。Asherahは、高度な暗号化機能や、侵害に対する深層防御を提供します。「エンベロープ暗号化」と呼ばれる技術を利用して、クラウドに依存しないデータストレージと鍵の管理をサポートします。
github.com/godaddy/asherah-ruby READMEより

参考: AWS KMS の概要とエンベロープ暗号化について説明してみる - 私の戦闘力は53万です

「この図がわかりやすい↓: 記事によるとマスターキー(MK)としてKMSの他にハードウェアセキュリティモジュール(HSM)も使えるとあるので、本格的な機能のようですね」「図の最上位の赤いキーがマスターキーで、その下のメタストア層の中間キーがこのライブラリで管理されるんですね」「通常の暗号化は1つのキーを使うけど、この方法だと最下層の永続化層にあるデータ行はその上のすべてのキーが揃って初めてアクセスできるんでしょうね」「メモリダンプでデータを取り出せないようにメモリ上のデータも保護されるのか」


同記事より

「asherahの設定ファイル↓を見ると、config.metastoreにrdbmsを指定したりできるらしい: 記事をざっと眺めただけでも興味を惹かれる技術ですね👍」

# 同記事より
Asherah.configure do |config|
  config.service_name = 'marketing'
  config.product_id = 'email'
  config.metastore = 'rdbms'
  config.enable_session_caching = true # default: false

  c = ActiveRecord::Base.connection_db_config.configuration_hash
  config.connection_string = "#{c[:username]}:#{c[:password]}@tcp(#{c[:host]}:#{c[:port]})/#{c[:database]}"

  if ENV['ASHERAH_KMS_ENABLED'] == 'true'
    config.kms = 'aws'
    config.preferred_region = ENV.fetch('AWS_REGION')
    config.region_map = { ENV.fetch('AWS_REGION') => ENV.fetch('KMS_KEY_ARN') }
  elsif Rails.env.development? || Rails.env.test?
    config.kms = 'static' # The static key used for encryption is `thisIsAStaticMasterKeyForTesting` (defined in Asherah Go)
  else
    raise "Asherah client not configured for: #{Rails.env}"
  end
end

🔗 rubocop-factory_bot(Ruby Weeklyより)

rubocop/rubocop-factory_bot - GitHub


つっつきボイス:「factoryの作り方は人によってかなり癖があったりするので、factory_bot向けのRuboCopがあってもよさそう👍」「ドキュメントを見ると、既にいろいろなcopがあるんですね」

参考: FactoryBot :: RuboCop Docs

# docs.rubocop.orgより: FactoryBot/CreateList

# bad
3.times { create :user }
3.times.map { create :user }
[create(:user), create(:user), create(:user)]
Array.new(3) { create :user }

# good
create_list :user, 3

# bad
3.times { create :user, age: 18 }

# good - index is used to alter the created models attributes
3.times { |n| create :user, age: n }

# good - contains a method call, may return different values
3.times { create :user, age: rand }

🔗Ruby

🔗 contracts.ruby: Rubyで契約プログラミング

egonSchiele/contracts.ruby - GitHub

参考: Contracts.ruby by egonSchiele -- チュートリアル


つっつきボイス:「以下のツイートで探してみてcontracts.rubyを見つけました」

Contract Num => Numみたいな感じで型を定義すると、違う型の値を渡したときにエラーを出すもののようですね: コード内に書くところはSorbet的」「★が1400超えていて、"メンテナー募集"とありますね」「コミットはそこそこ動いているけどissuesはあまり動いてないっぽい: 本番でヘビーに使うというより、参考になったという意味での★なのかもしれませんね」

# 同リポジトリより
require 'contracts'

class Example
  include Contracts::Core
  include Contracts::Builtin

  Contract Num => Num
  def double(x)
    x * 2
  end
end

puts Example.new.double("oops")

「このgemは知らなかったけど、contractはいわゆる契約プログラミングのようですね: とりあえずWikipediaを見ると、クライアントの事前条件遵守やサプライヤの事後条件遵守のあたりが契約に相当するんでしょうね↓」「動作としては実行時チェックっぽいですね」

参考: 契約プログラミング - Wikipedia

契約はまたクライアントとサプライヤに生じる義務と利益によって特徴付けられる。 クライアントの設計者にはクラスのインタフェースとして示された事前条件を遵守する義務が生じる一方、事後条件が満たされること期待してよく、反対にサプライヤの設計者はクライアントが事前条件を満たしてクラスを利用することを期待してよい一方、事後条件を遵守する義務が生じる。 このクライアントとサプライヤの双方に生じる義務と、義務を守った際に保証された状態を得られる利益とが現れる点で、プログラミングにおける「契約」と一般的な意味での契約の類似を見出せる。
契約プログラミング - Wikipediaより

🔗 Rubyで列挙型(enum)っぽく連番を定義できるgemを作ってみた

JunichiIto/seq_as_enum - GitHub

つっつきボイス:「jnchitoさんの記事です」「C言語のenum型みたいなものをRubyでやってみたくなる気持ちわかる: 普通のRubyだとこういうことをするためにハッシュを作ったりしますね」

# 同記事より
require 'seq_as_enum'

class Sample
  extend SeqAsEnum

  # seq_as_enumメソッドで連番を定義
  seq_as_enum :RED, :YELLOW, :GREEN
  # すると、以下のような定数が定義される
  # RED = 0
  # YELLOW = 1
  # GREEN = 2

  # 上で定義した連番を使用する
  def self.main
    puts "RED = #{RED}"
    puts "YELLOW = #{YELLOW}"
    puts "GREEN = #{GREEN}"
  end
end

Sample.main

「こういうふうに連番を割り当てる方法はメタプログラミングと相性が悪いんですよ: メタプロによるパッチ当てでenum値の種類を増やしたり減らしたりするようなコードがあると期待通りに動かないといったケースもあり得るので、Ruby本体の機能としてenum連番が必要かどうかについては微妙なところかもしれません」

🔗 RubyKaigi 2023で興味を持った技術

つっつきボイス:「RubyKaigi 2023の報告記事もだいぶ増えてきましたね」「Scrapboxのまとめを見るとかなりの数になってます↓」

参考: RubyKaigi 2023 - ruby-jp

🔗 設計

🔗 日本の住所


つっつきボイス:「上の記事がバズってますね」「経済産業省の公式APIがあるといっても住所の正規化はまだまだ大変そう(ウォッチ20200601)」

「関連する記事をいくつか見繕いました: 京都の通り名が厄介なのは有名ですけど、北海道の住所も手強い」

「住所という概念がない国...」

なお、番地と住所については以下の本がおすすめです。著者の今尾恵介さんは他にも地図や住所に関する本をたくさん出しています。

🔗クラウド/コンテナ/インフラ/Serverless

🔗 AWS LambdaがRuby 3.2をサポート

つっつきボイス:「LambdaでRuby 3.2がサポートされた🎉」「最新なのがありがたい」「Lambdaランタイムのサポートリストを見ると前のバージョンはRuby 2.7なのか」「だいぶジャンプしましたね」「LambdaでRubyを積極的に使っている人って少ないんだろうか🤔」「ところでツイートで"ルビー"になってるあたり、いかにも機械翻訳っぽいですね」


後編は以上です。

バックナンバー(2023年度第2四半期)

週刊Railsウォッチ: Arel::Nodes::Cteが追加、html_escape_onceの修正ほか(20230613前編)

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Ruby Weekly


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。