rails 6.0.3 で api mode にした時の diff
主な違い
- frontend 関連のファイルがごっそり消えている
- sass や cabipara など、rails での view 出力に関わるライブラリが消えている
- view に関わるファイルも消えている
- config/application.rb での設定が変わっている
- rack-cors がデフォルトで入っている
以下から全ての diff が見れる。 https://github.com/nekootoko3/rails-diff/pull/1/files
rack-cors から CORS を理解する
web の仕様を理解するにはドキュメントを読み、その仕様の実装を見てみるのが良い方法だと思う。
ruby の CORS の制御を実装している rack-cors から CORS を理解する。
- CORS について
- rack-cors
CORS について
- Cross Origin Resource Sharing とは、異なるオリジン(protocol + port + domain)間で、リソースを認可するための仕様
- 主に XMLHttpRequest または Fetch API を呼び出し時に利用されることが多い(つまり JavaScript を利用したサーバーとの通信全般)
- この仕様の全体についてはまとめないので下のような記事を読むと良さそう
- Simple Request と Preflight Request に関しては、自分もあまり覚えていなかったのでまとめる
- Simple Request
- サイト間リクエストがユーザーデータに影響を与えないようなリクエスト
- CORS プリフライトを伴わないリクエストで、 MDN のドキュメントによると以下 5 条件が満たされているリクエストのこと。
- 許可されているメソッドのうちの一つであること
- GET
- HEAD
- POST ユーザーエージェントによって自動的に設定されたヘッダーを除いて、手動で設定できるヘッダーが以下だけであること
- Accept
- Accept-Language
- Content-Language
- Content-Type (但し、下記の要件を満たすもの)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- Content-Type ヘッダーが以下のいずれかであること
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- リクエストに使用されるどの XMLHttpRequestUpload にもイベントリスナーが登録されていないこと。
- リクエストに ReadableStream オブジェクトが使用されていないこと。
- 許可されているメソッドのうちの一つであること
- Preflight Request
rack-cors とは
rack アプリケーションに CORS の制御機能を追加する rack middleware。 独自の DSL で CORS を制御できるようにする。 rack-cors の README にあるサンプル。 どのオリジンが、どのリソースに、何の操作をしていいかを定義している。
use Rack::Cors do allow do origins 'localhost:3000', '127.0.0.1:3000', /\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/ # regular expressions can be used here resource '/file/list_all/', :headers => 'x-domain-token' resource '/file/at/*', methods: [:get, :post, :delete, :put, :patch, :options, :head], headers: 'x-domain-token', expose: ['Some-Custom-Response-Header'], max_age: 600 # headers to expose end allow do origins '*' resource '/public/*', headers: :any, methods: :get # Only allow a request for a specific host resource '/api/v1/*', headers: :any, methods: :get, if: proc { |env| env['HTTP_HOST'] == 'api.example.com' } end end
実際に見ていく
- まずはアプリケーション起動時にオリジン・リソース・許可する操作を記述した DSL を解釈して実行しやすい状態として持っておく
- DSL の解釈開始 https://github.com/nekootoko3/rack-cors/blob/6fbc109450f872bc12ab987f4b027f250b79d295/lib/rack/cors.rb#L45
Rack::Cors#allow
の内部にある DSL はResources
のコンテキストで解釈される https://github.com/nekootoko3/rack-cors/blob/6fbc109450f872bc12ab987f4b027f250b79d295/lib/rack/cors.rb#L54-L62- origins, resource を事前処理しておく
実際にリクエストが来た時、Simple Request
とPreflight Request
で制御が異なるのでそれぞれのケースを見ていく。
Simple Request のケース
- Simple Request を扱うケース
- レスポンス時に付加するヘッダーを取得する
- Rack::Cors#process_cors
- 定義した DSL の origin・resource にリクエストが発生している origin・resource の組み合わせがあるか確認する
- 存在していればレスポンス時に付加するヘッダーを取得する
- Rack::Cors::Resource
- 定義した DSL に基づいて
Access-Control-*
のハッシュを返す
- 定義した DSL に基づいて
- Rack::Cors::Resource
- レスポンス時に
Access-Control-*
のヘッダーを付加する- リクエストヘッダーの
Origin
と レスポンスヘッダーのAccess-Control-Allow-Origin
が一致していなければリソースは利用されない。 - Simple Request の時って、
Access-Control-Allow-Origin
とAccess-Control-Allow-Credentials
以外のヘッダーはいらないのでは?
- リクエストヘッダーの
Preflight Request のケース
- Preflight Request
- Method が
Options
でヘッダーにHTTP_ACCESS_CONTROL_REQUEST_METHOD
がある時は、Preflight Request としてリクエストを処理する - Rack::Cors#process_preflight
- 定義した DSL の origin・resource にリクエストが発生している origin・resource の組み合わせがあるか確認する
- なければ Preflight Request の処理を終了する。
Access-Control-*
がレスポンスヘッダーに付加されないので後続のリクエストは送られない - リクエストヘッダーの
Access-Control-Request-Method
とAccess-Control-Request-Headers
が事前に許可するとしたメソッドとヘッダーに含まれていなければ Preflight Request の処理を終了する。 - Rack::Cors#to_preflight_headers
- レスポンスヘッダーに追加する
Access-Control-*
のハッシュを返す
- レスポンスヘッダーに追加する
- 内側の rack アプリケーションにはリクエストを伝搬せず、ヘッダーを付加したレスポンスを返す
- Method が
Ruby によるデザインパターン
この本は絶版になっており非常に高い。
中古を高いお金を出して買わずに https://github.com/davidgf/design-patterns-in-ruby や日本語でのまとめを探して読むので十分だと思う。
このまとめはコードは載せていないが、コードを載せているまとめも多くあるので多くあるので探してみるとよい。
1. よいプログラムとパターン
GoF 本に対する筆者の要約は次の 5 つのポイントになる。
- 変わるものを変わらないものから分離する
- インターフェイスに対してプログラムし、実装に対して行わない
- 継承より集約
- 委譲、委譲、さらに委譲
- 必要になるまで作るな(YAGNI = You Ain't Gonna Need It)
Ruby によるデザインパターンでも同様にポイントになっているので常に意識して読むと良さそう
2. Ruby をはじめよう
Ruby の文法について
3. アルゴリズムを変更する: Template Method
- 概略
- 骨格となるメソッド(テンプレートメソッド)を持った抽象基底クラスを構築し、サブクラスが実際のテンプレートメソッドの実際の処理(フックメソッド)を提供する。
- テンプレートメソッドは NotImplementedError を raise するメソッドかもしれないし何かしらの標準実装かもしれない。
- 大枠の処理は抽象基底クラスに定義してあるので、サブクラスは断片的に見えるかもしれない。
- 骨格となるメソッド(テンプレートメソッド)を持った抽象基底クラスを構築し、サブクラスが実際のテンプレートメソッドの実際の処理(フックメソッド)を提供する。
- メリット
- 継承を使ってアルゴリズムに多様性をもたせることができる
- 抽象基底クラスはテンプレートメソッドを通じて高レベルの処理の制御に集中できる。サブクラスはその詳細を埋める
- デメリット・注意点
- 継承を使っていることは問題になりうる。継承より集約
- 抽象基底クラスとサブクラス間で将来的にも is-a という関係を保ち続けられるか?(リスコフの原則)
- 継承階層が深くなってしまい可読性が落ちてしまわないか?
- YAGNI
- 最初からテンプレートメソッドで多くの場合に対処できるようにはしない。最初はシンプルな実装で必要になってからこのパターンを適用する。
- 大量のフックメソッドの実装を強いるテンプレートクラスを作らない。
- 継承を使っていることは問題になりうる。継承より集約
4. アルゴリズムを交換する: Strategy
- 概略
- Template Method では継承を使って
変わるものを変わらないものから分離する
を実現したが Strategy では委譲
によってこれを実現する- 変わる部分を別のオブジェクトに切り出し、利用したいオブジェクトは切り出したオブジェクトを取替可能なパーツとして扱う。
- 切り出された同じ目的を持ったオブジェクト群を
Strategy
と呼び、Strategy の利用者をContext
と呼ぶ。 - コンテキストからストラテジに渡す引数はコンテキスト自身にするケースもある(もともとはストラテジはコンテキストの一部なので合理的に思える)
- 簡易的なストラテジであれば Proc オブジェクトを使って実装することもできる
- 切り出された同じ目的を持ったオブジェクト群を
- 変わる部分を別のオブジェクトに切り出し、利用したいオブジェクトは切り出したオブジェクトを取替可能なパーツとして扱う。
- Template Method では継承を使って
- メリット
- 委譲を使ってアルゴリズムに多様性をもたせることができる
- ストラテジを切り出すことで関心を分離できる
- 移譲と集約に基づいている(継承ではない)ので、実行時にストラテジの切り替えが簡単にできる
- デメリット・注意点
- コンテキストとストラテジのインターフェイスに一貫性をもたらさないと、ストラテジはあくまでコンテキストから切り出したものである、という関係性が崩れる
- コンテキストと特定のストラテジの依存関係を高めると複数のストラテジへの対応が難しくなるのでメリットが小さくなる
5. 変更に追従する: Observer
- 概略
- 変更が発生したオブジェクト(Subject, Observable)と変更の発生に関心のあるオブジェクト(Observer)間で、上手く変更を伝搬する
- Observer パターン用の Ruby 標準ライブラリ: https://github.com/ruby/ruby/blob/master/lib/observer.rb
- オブザーバーは変更の発生時に呼び出すための共通のインターフェイスを持っており、Subject はイベント発生時に一連のオブザーバーのインターフェイスを呼び出す。
- 変更が発生したオブジェクト(Subject, Observable)と変更の発生に関心のあるオブジェクト(Observer)間で、上手く変更を伝搬する
- メリット
- デメリット・注意点
- 特にイベント通知が多い場合、不必要な通知を送ることで全体のパフォーマンスが落ちる可能性がある。
- その他
6. 部分から全体を組み立てる: Composite
- 概略
- メリット
- 任意の深さの木構造を構築でき、どのノードも同じように扱うことができる
- デメリット・注意点
- Leaf と Composite を実装上どう扱うかに注意
- ツリーの深さが1段しかないと仮定してしまうとおかしくなってしまう
7. コレクションを操作する: Iterator
- 概略
- メリット
- 実装の詳細を知ることなく、集約オブジェクトの子オブジェクトを走査し任意の走査を行うことができる
- #each と #<=> を実装すれば Enumerable モジュールを include することで便利メソッドがたくさん手に入る
- デメリット・注意点
- コレクションの走査中にコレクションに変更を加えると網羅的に走査できないかも
8. 命令を実行する: Command
- 概略
- オブジェクトからある特定の動作を切り出したオブジェクトにする(Strategy に近い?)
- Composite パターンと合わせて、1 つのタスク実行で一連の Command を実行するパターンも形成できる
- Obsever パターンと多くの共通点を持つ
- Command: 動作の方法を知っているだけで呼び出し元の状態には関心がない
- Observer: 呼び出される対象の状態に応じて動作する
- メリット
- デメリット・注意点
9. ギャップを埋める: Adapter
- 概略
- メリット
- 新たにインターフェイスを満たすクラスを 1 から作ることなく既存のクラスを再利用できる
- デメリット・注意点
- 実装方針の選定基準
- オブジェクトの変更: 対象をよく理解していて、インターフェイスの変更が比較的少ない場合
- アダプタクラスの実装: オブジェクトが複雑な場合やオブジェクトへの理解が不十分な場合
- 実装方針の選定基準
10. オブジェクトに代理を立てる: Proxy
- 概略
- クライアントと対象オブジェクトの間に対象オブジェクトと同じインターフェイスを持つオブジェクトを挟みいくつかの機能を提供する
- Protection Proxy: 対象オブジェクトへのアクセス制御を行う
- Remote Proxy: 実際にはネットワークを介した別サーバに存在する対象オブジェクトが実際に存在しているかのように振る舞う(RPC)
- Virtual Proxy: 対象オブジェクトの生成を必要になるまで遅らせる(||= 演算子を使う)
- https://github.com/wantedly/computed_model などは Proxy の例
- クライアントと対象オブジェクトの間に対象オブジェクトと同じインターフェイスを持つオブジェクトを挟みいくつかの機能を提供する
- メリット
- Protection Proxy: 対象オブジェクトからアクセス制御の責務を分離できる
- Remote Proxy: クライアントは対象オブジェクトがどこにあるかを意識しなくて良くなる
- Virtual Proxy: オブジェクト生成コストを遅らせる、削減できる
- デメリット・注意点
11. オブジェクトを改良する: Decorator
- 概略
- 基本となる機能をを実装する ConcreteComponent を副次的な機能を持つ Decorator でラップしチェーンして呼び出す
- rack middleware は Decorator の例。middleware は #call のインターフェイスを持ち、次々に次の rack app を呼び出していく
- メリット
- 必要な機能を実行時に組み立てることができる
- 基本機能と Decorator、Decorator 毎の関心を分離できる
- デメリット・注意点
- 必要な機能を実行毎に組み立てるのが煩雑になりうる
- デコレータの連鎖によるパフォーマンスの低下。一つの大きなオブジェクトでいいかもしれない
Adapter と Proxy と Decorator
- これら 3 パターンは別のオブジェクトの代理オブジェクトの役割を果たしている
12. 唯一を保証する: Singleton
- 概略
- インスタンスが 1 つしかなく、そのオブジェクトをグローバルにす
- singleton モジュールを include する方法、モジュールで実装する、クラスメソッドを使って実装するなどいくつかの方法がある
- メリット
- 設定ファイルやロガーなど、1 つだけ存在すればいいオブジェクトを引き回さなくて良くなる
- デメリット・注意点
- 本当にグルーバル変数にする必要があるのか?
- 1 つだけ存在しているというのは確かなのか?
13. 正しいクラスを選び出す: Factory
- 概略
- クラス選択をサブクラスに任せる
- 親クラスが振る舞いを持ち、サブクラスがどのクラスを選択するかを持つ
- Abstract Factory は、有効なクラス選択の組み合わせを決めておく
- クラス選択をサブクラスに任せる
- メリット
- 共通の振る舞いを持つ別のクラスを簡単に作成できる
- 振る舞いとクラス選択の責務を分離できる
- デメリット・注意点
14. オブジェクトを組み立てやすくする: Builer
- 概略
- メリット
- 複雑なオブジェクトの生成・生成時のパラメータのバリデーションを別のクラスに切り出すことができる
- デメリット・注意点
- YAGNI: 単純なインスタンス化でいいところに Builer を適用して不必要にコードを複雑にすること
- Builder クラスを用意するより
self.build
というメソッドを定義し、そのメソッドにインスタンス化を任せるケースも多い- Builder クラスを用意する例
- https://github.com/rack/rack/blob/f9ad97fd69a6b3616d0a99e6bedcfb9de2f81f6c/lib/rack/builder.rb
- config.ru から rack app を組み立てる
- 別のクラスとして返すので Factory でもある?
- https://github.com/rack/rack/blob/f9ad97fd69a6b3616d0a99e6bedcfb9de2f81f6c/lib/rack/builder.rb
- self.build でインスタンス化をする例
- Builder クラスを用意する例
15. 専用の言語で組み立てる: Interpreter
- 概略
- 課題を解決するための専用の言語を作る
- 専用の言語をパースして個別のノードが実行可能な抽象構文木(AST)にする
- 課題を解決するための専用の言語を作る
- メリット
- 専用の言語によってシンプルな記述によって課題を解決することができる
- デメリット・注意点
- パーサーを作ることが複雑になりうる
- Interpreter パターンを利用することで課題をシンプルに解決できるにも関わらず適用しないことで複雑なまま課題に向かうことになる
Ruby のためのパターン
- DSL(Domain Specific Language)
- class_eval・instance_eval を活用して DSL を作ろう。
- rack DSLやrails の routesなどが有名?
- class_eval・instance_eval を活用して DSL を作ろう。
- メタプログラミング
- 動的にオブジェクトやメソッドを作る。
- メタプログラミングRubyは本当にいい本。
- Convention over Configuration
まとめ
冒頭の筆者の GoF 本に対するまとめの再掲
- 変わるものを変わらないものから分離する
- インターフェイスに対してプログラムし、実装に対して行わない
- 継承より集約
- 委譲、委譲、さらに委譲
- 必要になるまで作るな(YAGNI = You Ain't Gonna Need It)
この辺りはオブジェクト指向実践設計ガイドでも大体同じことを言っていた。
原則としては TRUE
・SOLID
などがこのポイントに当てはまってくるのではないだろうか。
- TRUE
- Transparent
- Reasonable
- Usable
- Exemplary
- SOLID
- Single Responsibility Principal
- Open Closed Principal
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
プロジェクトマネジメントのトリセツを読んだ
図解 これ以上やさしく書けない プロジェクトマネジメントのトリセツ
モチベーション
エンジニアの仕事はコードを書くだけではない。
事前調査したり、他チームの人と協力したり、作業見積もりを出したりと色んなことをやる必要がある。
この色んなことにはプロジェクトマネジメントの要素が多分に含まれているのでこの本を読んだ。
自分は自社サービスを持つ web 企業で働くエンジニアなので、自分に役立ちそうなことをまとめていく。
書いてある内容を省いたり置き換えたり変えてまとめている部分もある。
まえがき
プロジェクトマネジメントとは、先読み技術
。
いかに先読みして、手戻りが発生しない最前の手を打っていくか。
WBS・ガントチャート・PERT・リスクマネジメントなどを駆使して手戻りの少ないプロジェクト進行を実現していく。
プロジェクトの基本
- プロジェクトとは、
'特定の目的'を達成するための'臨時組織'による活動
で現状維持の業務とは一線を画すもの - プロジェクトマネジメントの 5 原則
- プロジェクトフェーズとライフサイクルの原則
不確実性を回避する
ためにプロジェクトをフェーズに分割
し、そのフェーズごとにライフサイクル
を明確にする- 企画・設計・実施(実装)に分けるとよい
- 基本方針の明確化や全体の方向付けをする企画フェーズを入れるとよい。しないと手戻りが多くなりがち
- ライフサイクルは、立ち上げ・
計画
・実行・コントロール
・終結のプロセスで構成される- 計画: スコープを明確にし、計画を立案する
- コントロール: 進捗をモニタリングし、必要に応じて修正する
- プロジェクト・ステークホルダー明確化の原則
- プロジェクトの影響を受ける人に事前に承認を取り、協力してもらえるように進める
- 組織影響の原則
- ステークホルダーにも関連するが、影響範囲を明確にしておかなければ既存組織から反発もありうる
- プロジェクト運営知識・能力確保の原則
- 社会経済的影響の原則
- あらかじめ作業計画・スケジュール、オーナー、ステークホルダーを明確化する
- リスクマネジメント
- 損失を発生するかもしれない不確実な度合い
- 対策が必要か看過できるものか事前に決めておく
- 損失を発生するかもしれない不確実な度合い
プロジェクトのテーマ設定とフェーズ分割
- 事業目標とプロジェクト目標の方向性を一致させる
プロジェクトの立ち上げと計画立案
- プロジェクト計画は実行段階での航海図
- 実行段階では PDCA を回して計画差異を埋める
- プロジェクト計画の基本は 5W2H
- Why(背景)・What(目的)・Where(スコープ)・When(納期)・Who・How・How Much
- テーマ設定
- 背景 + 目的 + 達成目標 + スコープ を明確にする
- 背景: プロジェクトが必要な理由を説明する
- 何か課題を解決したい、新たな挑戦をしたい
- 目的: プロジェクトが目指すゴール
- 00のために00する、など分かりやすいものにする
- 迷った時に立ち返るプロジェクトの拠り所
- 達成目標: 目的を数値化して詳細に定義したもの。QCD(Quality・Cost・Delivery)を含めるとよい
- スコープ
- 明確にしておかないとプロジェクトが進むにつれてやることが増えていく
- 背景: プロジェクトが必要な理由を説明する
- 背景 + 目的 + 達成目標 + スコープ を明確にする
- 前提条件・制約条件の明確化
- メンバー間の認識齟齬をなくす
- 後から制約条件が分かってだめだったと分かると辛い
- プレリサーチによって現状分析を行いメンバー間の認識を合わせる
- 主要成果物を明確にする
- 全体像から個別の詳細へと解像度をあげていく
プロジェクトの実行・運用
- あらかじめプロジェクトリーダーに情報を統合する仕組みをつくる
- ルールの明確化
- 会議体の明確化
- どのような会議をどのようなメンバーでどのような頻度で行うか
- 情報共有と情報伝達ルート
- キックオフミーティングでステークホルダーへの周知と参加者の参画意識を高める
- トレードオフが発生した場合の優先順位を決めておく
- プロジェクト完了後の導入・継続もプロジェクト計画にまで含める
チェックリスト
- 事業目的とプロジェクト目的の整合性が明確になっている
- プロジェクトオーナーを明確化する
- オーナーやステークホルダーのプロジェクトへの理解
- プロジェクトマネジメントの知識を身につける
- 目的指向の風土をめざす
- 計画を立てて PDCA を回す
- 5W2H を明確にする
- 計画不足・予算不足・準備不足の解消
- マトリックス組織では、関与する割合を明確化する
- 情報を共有し、フラットでオープンな組織に
- コミュニケーションチャンネルの明確化
- フォーマットや会議体の明確化
- マイルストーン(中間報告、フィーズの完了報告)をおく
- 集中討議の活用で結束力を高める
- 全員が目標達成に向かう状態を作り上げる
- プロジェクトでの活動を、人事考課へ正しく反映させる
- トレードオフ、コンフリクトへの早期対応
JavaScript Primer を読んだ
jsprimer
最近 js をちょくちょく書くようになったけど、js についてちゃんと勉強したことなかった。
そこで https://jsprimer.net/ を読んだ。とても分かりやすかった。
非同期処理は分かった気がしているだけなので、自分で書いてみて Promise 本 も読みたい。
第一部では Javascript の言語自体についての説明。
第二部は実際に使ってみよう、という趣旨。
第二部の Todo アプリでの、リファクタ、Model・View の流れの方向を決めるのは勉強になった。
以下個人的なポイント列挙。
var
は使っちゃダメ。- for 文でのスコープを作らなかったり、巻き上げをするのでびっくりする。
- データ型は
プリミティブ型
とオブジェクトがある。- プリミティブ型は本当にデータだけを持つんじゃなくて、いくつかメソッドを呼び出すことができる。
- その時には暗黙的に型変換する。
- プリミティブ型は本当にデータだけを持つんじゃなくて、いくつかメソッドを呼び出すことができる。
- 比較する時には
==
じゃなくて===
を使おう。==
は暗黙的な型変換後の値比較。===
は型の同一性と値を比較する。
- 分割代入便利。
- 真偽値の判定をする時、暗黙的な型変換はされないようにしよう。
- 関数
- ファーストクラスなので、関数の引数に渡せる。
- 同一名称の関数は引数の数が違っても上書きされる。
- 関数定義と呼び出し時の引数は違っても良くて、少ない分は undefined になる(よくなさそう)
- js の関数はクロージャー。
- switch 文は break しないと該当 case 全て実行されちゃう。
- Object の merge は
Object.assign({}, objA, objB)
が典型的な方法 - javascript の clone は shallow copy。
- 全てのオブジェクトの祖先は
Object
。 - プロパティの存在確認で祖先まで見に行ってほしくない場合には、
Object#hasOwnProperty
- 配列
- Array かどうかの判定は
Array.isArray
を使おう。typeof arrayInstance
は "object" を返す。
- for 文は基本使わず
forEach
・map
・filter
・reduce
で処理しよう。 - 分割代入できる。
- Array かどうかの判定は
- 文字列
- JavaScript は文字コードは
Unicode
、エンコード方式はUTF-16
を採用している。 - Unicode で定義されている文字列の中には 16 ビットに収まらないものがあるので、絵文字を扱ったり文字数を数える場合には注意。
- 文字列を配列に変化して処理するなどの工夫が必要なケースも
- JavaScript は文字コードは
- this
- this の参照先は条件によって変わる
実行コンテキスト
・コンストラクタ
・関数とメソッド
・ArrowFunction
- いくつか使い方はあるけど、基本的なユースケースは関数内で使うこと
- ArrowFunction 以外の関数では実行時に this が決定される。
- 関数内の this はベースオブジェクトになる。
- ベースオブジェクトとは、
メソッドを呼ぶ際に、そのメソッドのドット演算子またはブラケット演算子のひとつ左にあるオブジェクト
(書籍から引用)- 関数単体で呼び出した時ベースオブジェクトは存在しないので this は undefined を返す
- ベースオブジェクトとは、
- これはいくつかのケースで問題になる
- this を含むメソッドが変数に代入された場合 => call, apply, bind によって、this を指定してメソッドを実行する。
- コールバック関数 => this を一時変数に代入する or
Arrow Function
- 関数内の this はベースオブジェクトになる。
- Arrow Function における this は実行時に決定される
Arrow Function自身の外側のスコープに定義されたもっとも近い関数のthisの値
(書籍から引用)
- コンストラクタにおける this はインスタンスオブジェクトになるので、プロパティ定義などできる
- this はメソッド以外では使うべきではない
- this の参照先は条件によって変わる
- クラス
- ES2015 から新しくできた。それ以前は同等のものが関数で表現されていた。
- コンストラクタ内では return しない。インスタンス以外が返ってしまう
- クラス内に定義されたメソッドは
プロトタイプメソッド
と呼ばれ、インスタンス間で共有される。 - コンストラクタ内で定義されたメソッドはインスタンス固有のメソッドとなり、プロトタイプメソッドより優先して呼び出される(ruby だと 特異メソッドとインスタンスメソッド)
- 同名のプロトタイプメソッドとインスタンスオブジェクトのメソッドがあっても上書きされない
- アクセッサプロパティがある
static
キーワードをプロパティの前につけることで、静的メソッド(クラスメソッド)になる- 静的メソッド内の this はクラス自身を参照するので、factory やクラス自身の処理に使われる
- インスタンス(オブジェクト)は、インスタンスの生成元や継承元への参照を
prototype
に持っている- Javascript のクラスは
プロトタイプベース
と言われるのはこれか - メソッドやプロパティの探索は prototype が undefined になるまで行われる
- Javascript のクラスは
extends
でクラスを実行できる
try
...catch
...finally
が ruby でいうところの begin...rescue...ensure- catch は全ての例外を補足するので error の種類によって処理を変える場合は catch 内でやる
- TypeError などのビルトインエラーがいくつかある
- throw で自分で例外を発生させられる
- 非同期処理
- Javascript の非同期処理は基本的には並行処理。Web Worker API など一部は並列で実行される
- 非同期処理の外からは非同期処理の中で発生した例外を知ることはできない。
- 非同期処理を上手く扱うための主要な 3 つのパターン
エラーファーストコールバック
・Promise
・Async Function
エラーファーストコールバック
- 非同期処理の中で例外が起きた場合に実行するコールバック関数を非同期処理の引数の最初に渡すというルール
- 仕様ではなくルール
- 非同期処理の中で例外が起きた場合に実行するコールバック関数を非同期処理の引数の最初に渡すというルール
Promise
- 非同期処理を扱うためのオブジェクトでありインターフェイス
- Promise はインスタンス作成時に
resolve
とreject
の 2 つの引数を持つ関数を引数に取る - Promise の処理内で
resolve
が呼び出されるとPrmise#then
でチェーンされた関数が呼び出される - Promise の処理内で
reject
が呼び出される、または例外が発生するとPromise#catch
でチェーンされた関数が呼び出される - Promise は内部的に 3 つの状態を持っている
- Promise の処理内では一度だけ resolve が呼べる。
- Fullfilled の状態になった Promise インスタンスに対して、then メソッドでコールバック関数を登録できる
- Promise#resolve、Promise#reject で Fullfilled、Rejected な状態の Promise オブジェクトを作れる
- テストでよく使われる
Promise#then
やPromise#catch
は Fullfilled な状態の Promise オブジェクトを作成して返すのでチェーンすることができるPromise.all
には Promise インスタンスの配列を受け取って、新しい Promise オブジェクトを返す。- 引数で受け取った全ての Promise インスタンスの状態が Fullfilled になったら返り値も Fullfilled になる。
Promise.race
は 1 つでも Settled になったらその Promise オブジェクトの状態と同じ状態の新しい Promise オブジェクトを返す
Async Function
- 関数の前に
async
キーワードをつけることで常に Promise インスタンスを返すようになる(以下引用)- Async Function が値を return した場合、その返り値を持つ Fulfilled な Promise を返す
- Async Function が Promise を return した場合、その返り値の Promise をそのまま返す
- Async Function 内で例外が発生した場合は、そのエラーを持つ Rejected な Promise を返す
- Async Function 内では
await
式を利用できる- await 式は右辺の Promise インスタンスが Settled になるまで待つ
- Fullfilled なら await 式は resolve された値を返す
- Rejcted ならその場でエラーを throw するので、同じ try...catch 構文で例外処理をできる
- await 式は右辺の Promise インスタンスが Settled になるまで待つ
- Async Function は Promise API(Promise.all など)と組み合わせて使うと効果的
- 関数の前に
- Map や Set があって、どちらも Iterable。Iterate ようの Map#keys などもメソッドもある
- オブジェクトは JSON を使って文字列とオブジェクトを行き来させられる
- Date オブジェクトは使いにくいので既存のライブラリと組み合わせて使ったほうがよさそう
次は MDN の Javascript を読むかな。(順序が逆かもしれない) https://developer.mozilla.org/ja/docs/Web/JavaScript
Ruby の throw と catch の実例
Ruby では大体のケースでは例外を begin-raise-rescue で実行箇所を移しているのでは。
なので throw
と catch
を使う機会はあんまりないように思う。
そんな中、実際に使われているところを見つけたので紹介。
throw と catch を上手く利用しているライブラリ
使われていたのは Rack ベースのアプリケーションで認証機能を提供している warden というライブラリ。
このライブラリはdevise の内部でも利用されている。
利用箇所
throw と catch が使われているのはそれぞれ下記のコード。
- throw 周辺のコード
def authenticate!(*args) user, opts = _perform_authentication(*args) throw(:warden, opts) unless user user end
- catch 周辺のコード
def call(env) # :nodoc: return @app.call(env) if env['warden'] && env['warden'].manager != self env['warden'] = Proxy.new(env, self) result = catch(:warden) do env['warden'].on_request @app.call(env) end result ||= {} case result when Array handle_chain_result(result.first, result, env) when Hash process_unauthenticated(env, result) when Rack::Response handle_chain_result(result.status, result, env) end end
コードの説明
throw は認証を行うコード中にあって、認証が失敗した場合に throw(:warden)
が実行されている。
catch は rack ミドルウェアの内部で利用されているので、この warden よりも内側のミドルウェア・アプリケーションコード中で throw(:warden) が実行されたらここまでジャンプする。
なので、認証が失敗したら rack ミドルウェアまで戻ってきて認証失敗時の処理を行う。
認証が成功した場合には アプリケーションのコードが rack response を返すはずなので、そのレスポンスをそのまま外側のミドルウェアに渡す。
まとめ
ジャンプしたいコードが大きく離れている場合には非常に有効な手段だと思った。
特に今回の例のように、Rack ミドルウェアとアプリケーションコード間でのジャンプに使うとよさそう。
Rails で 有効なエンドポイントを出したい
Rails で有効なエンドポイントを列挙したい
叩かれなくなっているエンドポイントを見つけたい。 そのためにアクセスログとアプリケーションの有効なエンドポイントとを突き合わせるとよさそう。 そこで有効なエンドポイントの列挙方法を調べた。
実行するコードと出力
異なるアクセスログの形式に対応できるように 2 通りで出す。
verb path の配列を取得するケース
Rails.application.routes.routes.reject(&:internal).collect do |route| next unless route.requirements[:controller] && route.requirements[:action] "#{route.verb} #{route.path.spec.to_s.sub(/\(.*\)/, "")}" end.compact
これを実行すると下記のような配列が取得できる。
path 中の :
から始まっているところは正規表現に置き換えてあげればよい。
=> ["GET /users/sign_in", "POST /users/sign_in", "DELETE /users/sign_out", "GET /users/password/new", "GET /users/password/edit", ... "PATCH /rails/conductor/action_mailbox/inbound_emails/:id", ... "POST /rails/active_storage/direct_uploads"]
controller#action の配列を取得するケース
Rails.application.routes.routes.reject(&:internal).collect do |route| next unless route.requirements[:controller] && route.requirements[:action] "#{route.requirements[:controller]}##{route.requirements[:action]}" end.compact
これを実行すると下記のような配列が取得できる
=> ["devise/sessions#new", "devise/sessions#create", "devise/sessions#destroy", "devise/passwords#new", "devise/passwords#edit", ... "active_storage/direct_uploads#create"]