of conventions Make Rails building blocks a part of your toolbox active_model/active_support/zeitwerk 43 RAILS を習得する RAILS の設計パターンを学ぶ 規約の原則を受け⼊れる RAILSの構成要素をあなたのツールボックスに⼊れる
where(tags_table[:value].eq(tag)) relation.where(tags_subquery.exists) end private def tags_table @tags_arel ||= Arel::Nodes::NamedFunction.new( "json_each", [arel_table[:tags]] ).then do name = Arel.sql(_1.to_sql) Arel::Table.new(name, as: :json_tags) end end def arel_table = self.class.query_model.arel_table end Separated complexity 分離された複雑さ
@cable.valid? if @cable.last_step? @cable.save session.delete(:cable_params) redirect_to @cable, notice: "Success!" else session[:cable_params] = cable_params.to_h redirect_to new_cable_path end else render :new, status: :unprocessable_entity end end def back @cable = Cable.new(session[:cable_params]) @cable.current_step = @cable.previous_step render :new end end 67 What we got 私たちが得たもの Written by senior AI developer # Ad hoc persistence (Infrastructure layer?) カスタムな永続化の実装 (インフラ層) Not a Rails-like action Railsらしくないアクション AI先⽣で作られたコード
to fill the gap doesn't look like Rails Crossing the architecture layer boundaries leads to high coupling and poor maintainability 68 RAILSは複数ステップ機能を標準では提供してくれない その隙間を埋めるために書いたコードは、RAILSらしくない アーキテクチャ層の越境は、密結合と低いメンテナンス性に⾄る
ApplicationWorkflow # ... end attribute :wizard_state, default: -> { "name" } attribute :wizard_action def submit! if wizard_action == "back" wizard.back! else wizard.submit! end return false unless wizard.complete? cable.save! end def wizard = @wizard ||= Wizard.new(self) end end Multi-step form logic What we should have done 私たちがすべきだったこと Written by senior Martian developer ベテランの⽕星⼈開発者が書いたもの
ApplicationWorkflow workflow do state :name do event :submit, transitions_to: :framework end state :framework do event :submit, transitions_to: :rpc, if: :needs_rpc? event :submit, transitions_to: :secrets event :back, transitions_to: :name end state :rpc do event :submit, transitions_to: :secrets event :back, transitions_to: :framework end state :secrets do event :submit, transitions_to: :region event :back, transitions_to: :rpc, if: :needs_rpc? event :back, transitions_to: :framework end state :complete end end end end Another abstraction — workflow! What we should have done 私たちがすべきだったこと Written by senior Martian developer ベテランの⽕星⼈開発者が書いたもの
ApplicationWorkflow workflow do state :name do event :submit, transitions_to: :framework end state :framework do event :submit, transitions_to: :rpc, if: :needs_rpc? event :submit, transitions_to: :secrets event :back, transitions_to: :name end state :rpc do event :submit, transitions_to: :secrets event :back, transitions_to: :framework end state :secrets do event :submit, transitions_to: :region event :back, transitions_to: :rpc, if: :needs_rpc? event :back, transitions_to: :framework end state :complete end end end end What we should have done 私たちがすべきだったこと Written by senior Martian developer ベテランの⽕星⼈開発者が書いたもの
:after define_callbacks :commit, only: :after def save return false unless valid? with_transaction do AfterCommitEverywhere.after_commit { run_callbacks(:commit) } run_callbacks(:save) { submit! } end end def model_name ActiveModel::Name.new(nil, nil, self.class.name.sub(/Form$/, "")) end private def with_transaction(&) = ApplicationRecord.transaction(&) def submit! raise NotImplementedError end end This is how we leverage Rails building blocks これはRailsの構成要素 を活⽤する⽅法だ
end def create @form = Cable::CreateForm.from(params.require(:cable)) if @form.save redirect_to cable_path(@form.cable), notice: "Success!" else status = @form.valid? ? :created : :unprocessable_entity render :new, status: end end end Controller code resembles the scaffolded code コントローラーのコードは、 「rails g scaffold」 で⾃動⽣成されたコードに似ている