Rails CHANGELOG "2019-10-07".."2019-10-13"
この期間の CHANGELOG.md へのコミットは3件。
ActionPack, ActiveModel, ActiveStorageに関してそれぞれ1件。
ActionPack
ActionDispatch::Request クラスのインスタンス変数 @remote_ip を更新できない不具合を修正した
コミット: Updated `ActionDispatch::Request.remote_ip=` · rails/rails@bf14a8e
修正は remote_ip= メソッドの @remote_ip = nil の一行だけ。
def remote_ip @remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s end def remote_ip=(remote_ip) @remote_ip = nil set_header "action_dispatch.remote_ip", remote_ip end
引用: rails/request.rb at bf14a8e23526447b9893ed66090f898da68eb7f1 · rails/rails
@remote_ip = nil をせずに remote_ip メソッドを呼んでいたため、set済みの @remote_ip のまま書き変わらない不具合があった。
ActiveModel
freezeされた ActiveModel のオブジェクトに対して attribute の書き変えをできないようにした
コミット: Raise FrozenError for frozen objects when trying to write to a · rails/rails@54b1574
概要はCHANGELOG.mdにある例の通り。
class Animal include ActiveModel::Attributes attribute :age end animal = Animal.new animal.freeze animal.age = 25 # => FrozenError, "can't modify a frozen Animal"
引用: rails/CHANGELOG.md at 54b157442171d28c7e9553537aef7a6ae571aad7 · rails/rails
修正は ActiveModel:: Attributes::ClassMethods モジュールの define_method_attribute= メソッド。
def define_method_attribute=(name) ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method( generated_attribute_methods, name, writer: true, ) do |temp_method_name, attr_name_expr| generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{temp_method_name}(value) raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? name = #{attr_name_expr} write_attribute(name, value) end RUBY end end
なるほどこういう実装になっていたのか〜。
ActiveStorage
ActiveStorage::Blob#url でパーマネントリンクを作れるようになった
コミット: Permanent URLs for public storage blobs · rails/rails@feab703
config/storage.yml に新しいキー public: true | false を設定することで、 true の場合はパーマネントリンクを作ることができる。未設定の場合は false で、今まで通り期限付きのリンクになる。
ActiveStorage::Blob#url を見てみる。ここでは service.url(ActiveStorage::Service#url) を呼んでる。
def url(expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline, filename: nil, **options) filename = ActiveStorage::Filename.wrap(filename || self.filename) service.url key, expires_in: expires_in, filename: filename, content_type: content_type_for_service_url, disposition: forced_disposition_for_service_url || disposition, **options end
引用: rails/blob.rb at feab7031b57040afa2b2f5f78f9251dbad6cbdf8 · rails/rails
ActiveStorage::Service#url を見てみる。if public? で public_url or private_url に振り分けてる。
def url(key, **options) instrument :url, key: key do |payload| generated_url = if public? public_url(key, **options) else private_url(key, **options) end payload[:url] = generated_url generated_url end end
引用: rails/service.rb at feab7031b57040afa2b2f5f78f9251dbad6cbdf8 · rails/rails
ActiveStorage::Service クラスの public_url と private_url は未実装(raise NotImplementedError)となっており、このクラスを継承している各ストレージサービスのクラスで public_url と private_url を実装している。具体的な実装は各クラスを参照。
ActiveStorage::Service::AzureStorageServiceActiveStorage::Service::DiskServiceActiveStorage::Service::GCSServiceActiveStorage::Service::S3Service
子クラスで実装を分けたい(かつ未実装だったらエラー吐きたい)ときに、親クラスで raise NotImplementedError にしとくの便利。よくある(定石的な)実装なのかな。これまでまともにOSSのコードリーディングしてなかった&設計関連の本もほぼ読んでないからこの辺の設計論?みたいなもの、自論として確立できてないんだよなぁ。ともかく勉強になった。