Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

ドライモナドを使用した鉄道における鉄道指向プログラミング

エラー処理は、すべてのプログラムの重要な部分です。コードの実装中にどのようなエラーが発生する可能性があるかについて積極的に取り組むことが重要です。これらのエラーは、各エラーとそれが発生したアプリケーションの段階を適切に説明する出力が生成されるように処理する必要があります。それでも、コードが機能し、読みやすい状態を維持できるようにこれを実現することも重要です。あなたがすでに持っているかもしれない質問に答えることから始めましょう:鉄道指向プログラミングとは何ですか?

鉄道指向プログラミング

特定の目的を達成する機能は、より小さな機能の組み合わせである可能性があります。これらの機能は、最終的な目標の達成につながるさまざまなステップを実行します。たとえば、データベース内のユーザーのアドレスを更新し、その後この変更をユーザーに通知する関数は、次の手順で構成されます。

validate user -> update address -> send mail upon successful update

これらの各ステップは失敗することも成功することもあり、いずれかのステップが失敗すると、機能の目的が達成されないため、プロセス全体が失敗します。

鉄道指向プログラミング(ROP)は、 Scott Wlaschinによって発明された用語です。 これは、このような機能のエラー処理に鉄道スイッチのアナロジーを適用します。鉄道の分岐器(英国では「ポイント」と呼ばれます)は、列車をある線路から別の線路に案内します。スコットは、各ステップの成功/失敗の出力が、成功トラックまたは失敗トラックに移動できるため、鉄道の分岐器のように機能するという意味で、このアナロジーを採用しています。

ドライモナドを使用した鉄道における鉄道指向プログラミング 鉄道の分岐器として機能する成功/失敗の出力

いずれかのステップでエラーが発生すると、障害出力によって障害トラックに移動し、残りのステップをバイパスします。ただし、成功した出力がある場合は、次のステップの入力に接続され、下の画像に示すように、最終的な目的地に到達するのに役立ちます。

ドライモナドを使用した鉄道における鉄道指向プログラミング いくつかのステップの成功/失敗の出力

この2トラックのアナロジーは、鉄道指向プログラミングの背後にある考え方です。あるステップでの失敗がプロセス全体の失敗であることを保証するために、方法のすべてのステップ(つまり、プロセスの一部であるすべてのメソッド)でこれらの成功/失敗の出力を返すように努めます。各ステップを正常に完了するだけで、全体的な成功につながります。

日常生活の例

The Milk Shakersという名前の店から直接ミルクのカートンを購入するという目標があると想像してみてください。 。関係する可能性のある手順は次のとおりです。

Leave your house -> Arrive at The Milk Shakers -> Pick up a carton of milk -> Pay for the carton of milk

家から出られない場合、最初のステップは失敗であるため、プロセス全体が失敗になります。家を出てウォルマートに行ったらどうしますか?指定された店舗に行かなかったため、プロセスはまだ失敗しています。ウォルマートからミルクを入手できるという事実は、プロセスが継続することを意味するものではありません。 ROPはウォルマートでプロセスを停止し、ストアがミルクシェーカーではなかったためにプロセスが失敗したことを通知する失敗出力を返します。ただし、正しい店舗に行った場合、プロセスは続行され、出力が確認され、プロセスが終了するか、次のステップに進みます。これにより、より読みやすく洗練されたエラー処理が保証され、if/elseなしでこれを効率的に実現できます。 およびreturn 個々のステップをリンクするステートメント。

Railsでは、 Dry Monads と呼ばれる宝石を使用して、この2トラックの鉄道出力を実現できます。 。

ドライモナドの紹介とその仕組み

モナドはもともと数学的な概念でした。基本的に、これらはいくつかの特殊関数の合成または抽象化であり、コードで使用すると、ステートフル値の明示的な処理を排除できます。また、プログラムロジックに必要な計算ボイラープレートコードを抽象化したものにすることもできます。ステートフル値は特定の関数に対してローカルではありません。いくつかの例には、入力、グローバル変数、および出力が含まれます。モナドには、これらの値を1つのモナドから別のモナドに渡すことを可能にするバインド関数が含まれています。したがって、明示的に処理されることはありません。これらは、例外、ロールバックコミット、再試行ロジックなどを処理するように構築できます。モナドの詳細については、こちらをご覧ください。

ドキュメントに記載されているように、ドライモナドはRubyの一般的なモナドのセットです。モナドは、エラー、例外、および連鎖関数を処理するための洗練された方法を提供します。これにより、コードがはるかに理解しやすくなり、すべてのifs and elsesなしで必要なすべてのエラー処理が可能になります。 。 結果モナドに焦点を当てます 先ほどお話しした成功/失敗の成果を達成するために必要なのはまさにそれだからです。

次のコマンドを使用して、Railway-appという名前の新しいRailsアプリを起動してみましょう。

rails new railway-app -T

コマンドの-Tは、テストにRSpecを使用するため、テストフォルダーをスキップすることを意味します。

次に、必要なgemをGemfileに追加します:gem dry-monads 成功/失敗の結果とgem rspec-rails テストフレームワークとしてのテストおよび開発グループで。これで、bundle installを実行できます 追加された宝石をインストールするために私たちのアプリで。ただし、テストファイルとヘルパーを生成するには、次のコマンドを実行する必要があります。

rails generate rspec:install
関数をいくつかのステップに分割する

関数を、最終的な目標を達成するために連携して機能する小さなメソッドに分割することを常にお勧めします。これらのメソッドのエラーがある場合は、プロセスが失敗した場所を正確に特定し、コードをクリーンで読みやすくするのに役立ちます。これをできるだけ簡単にするために、ユーザーに車を届けるトヨタの自動車販売店のクラスを構築します。要求されたモデルと色が利用可能である場合、製造年が2000年より前でない場合、および配送先の都市が近隣の都市のリストに含まれている場合。これは面白いはずです。 :)

配信プロセスをいくつかのステップに分割することから始めましょう:

  • 製造年が2000年より前ではないことを確認してください。
  • モデルが利用可能であることを確認します。
  • 色が使用可能であることを確認します。
  • 配送先の都市が近くの都市であることを確認します。
  • 車が配達されることを示すメッセージを送信します。

さまざまな手順が決まったので、コードを詳しく見ていきましょう。

成功/失敗の出力結果の入力

app / modelフォルダーに、car_dealership.rbというファイルを作成しましょう。 このクラスを重要な詳細で初期化します。ファイルの先頭で、dry/monadsを要求する必要があります 、クラス名の直後に、DryMonads[:result, :do]を含める必要があります 。これにより、結果のモナドとdo表記(yieldワードを使用したいくつかのモナド演算の組み合わせが可能になります)が利用可能になります。

require 'dry/monads'

class CarDealership

include Dry::Monads[:result, :do]

  def initialize
    @available_models = %w[Avalon Camry Corolla Venza]
    @available_colors = %w[red black blue white]
    @nearby_cities = %w[Austin Chicago Seattle]
  end
end

次に、deliver_carを追加します メソッド。これは、関連する他のすべてのステップで構成され、すべてのステップが成功した場合に成功メッセージを返します。これらのステップを相互に結合またはバインドするために、yieldワードを追加します。これは、これらのステップのいずれかの失敗メッセージがdeliver_carの失敗メッセージになることを意味します メソッド、およびそれらのいずれかでの成功出力は、リストの次のステップの呼び出しになります。

def deliver_car(year,model,color,city)
  yield check_year(year)
  yield check_model(model)
  yield check_city(city)
  yield check_color(color)

  Success("A #{color} #{year} Toyota #{model} will be delivered to #{city}")
end

それでは、他のすべてのメソッドを追加し、チェックの結果に基づいて成功/失敗の結果をそれらに添付しましょう。

def check_year(year)
  year < 2000 ? Failure("We have no cars manufactured in year #{year}") : Success('Cars of this year are available')
end

def check_model(model)
  @available_models.include?(model) ? Success('Model available') : Failure('The model requested is unavailable')
end
def check_color(color)
  @available_colors.include?(color) ? Success('This color is available') : Failure("Color #{color} is unavailable")
end

def check_city(city)
  @nearby_cities.include?(city) ? Success("Car deliverable to #{city}") : Failure('Apologies, we cannot deliver to this city')
end

現在、クラスと必要なすべてのメソッドがあります。これはどのように機能しますか?このクラスの新しいインスタンスを作成し、deliver_carを呼び出して調べてみましょう。 引数が異なるメソッド。

good_dealer = CarDealership.new

good_dealer.deliver_car(1990, 'Venza', 'red', 'Austin')
#Failure("We have no cars manufactured in year 1990")

good_dealer.deliver_car(2005, 'Rav4', 'red', 'Austin')
#Failure("The model requested is unavailable")

good_dealer.deliver_car(2005, 'Venza', 'yellow', 'Austin')
#Failure("Color yellow is unavailable")

good_dealer.deliver_car(2000, 'Venza', 'red', 'Surrey')
#Failure("Apologies, we cannot deliver to this city")

good_dealer.deliver_car(2000, 'Avalon', 'blue', 'Austin')
#Success("A blue 2000 Toyota Avalon will be delivered to Austin")

上に示したように、deliver_carメソッドの失敗の結果は、失敗したメソッドによって異なります。そのメソッドの失敗はその失敗になり、すべてのメソッドが成功すると、それ自体の成功結果を返します。また、これらのステップは、deliver_carとは独立して呼び出すこともできる個別のメソッドであることを忘れないでください。 方法。例を以下に示します:

good_dealer.check_color('wine')
#Failure("Color wine is unavailable")

good_dealer.check_model('Camry')
#Success('Model available')

RSpecを使用したテスト

上記のコードをテストするには、specフォルダーに移動し、ファイルcar_dealership_spec.rbを作成します。 パス内spec/models 。最初の行には、「rails_helper」が必要です。最初に失敗、次に成功のコンテキストのテストを作成します。

require 'rails_helper'

describe CarDealership do
  describe "#deliver_car" don
    let(:toyota_dealer) { CarDealership.new }
    context "failure" do
      it "does not deliver a car with the year less than 2000" do
        delivery = toyota_dealer.deliver_car(1990, 'Venza', 'red', 'Austin')
        expect(delivery.success).to eq nil
        expect(delivery.failure).to eq 'We have no cars manufactured in  year 1990'
      end

       it "does not deliver a car with the year less than 2000" do
        delivery = toyota_dealer.deliver_car(2005, 'Venza', 'yellow', 'Austin')
        expect(delivery.success).to eq nil
        expect(delivery.failure).to eq 'Color yellow is unavailable'
      end
   end
 end
end

上記のように、result.failureを使用して失敗または成功の結果にアクセスできます。 またはresult.success 。成功のコンテキストでは、テストは次のようになります。

context "success" do
  it "delivers a car when all conditions are met" do
    delivery = toyota_dealer.deliver_car(2000, 'Avalon', 'blue', 'Austin')
    expect(delivery.success).to eq 'A blue 2000 Toyota Avalon will be delivered to Austin'
    expect(delivery.failure).to eq nil
  end
end

これで、提供された引数をdeliver_carに微調整することで、失敗のコンテキストに他のテストを追加できます。 方法。無効な引数が指定されている状況については、コードに他のチェックを追加することもできます(たとえば、年変数などの値として文字列が提供されます)。 bundle exec rspecを実行しています ターミナルでテストを実行し、すべてのテストに合格したことを示します。メソッドの出力として両方を持つことはできないため、基本的に、失敗と成功の結果のチェックを同時にテストに追加する必要はありません。失敗した結果があった場合、またはその逆の場合に成功した結果がどのように見えるかを理解するために追加しただけです。

結論

これは、ドライモナドの紹介であり、アプリで鉄道指向プログラミングを実現するためにどのように使用できるかを示しています。これの基本的な理解は、より複雑な操作やトランザクションにさらに適用できます。これまで見てきたように、ROPを使用すると、よりクリーンで読みやすいコードを実現できるだけでなく、エラー処理が詳細になり、ストレスが少なくなります。このアプローチはエラーが発生した場所と理由を特定するのに役立つため、プロセスを構成するさまざまなメソッドに簡潔な失敗/成功メッセージを添付することを常に忘れないでください。 ROPの詳細については、ScottWlaschinによるこのプレゼンテーションをご覧になることをお勧めします。


  1. RailsでのTailwindCSSの使用

    CSSは魔法のようですが、時間がかかります。美しく、機能的で、アクセスしやすいサイトを使用するのは楽しいことですが、独自のCSSを作成するのは大変です。 Bootstrapなどの多くのCSSライブラリは近年爆発的に増加しており、Tailwindは2021年にパックをリードしています。 RailsにはTailwindが付属していませんが、この記事では、TailwindCSSを新しいRubyon Railsプロジェクトに追加する方法を説明します。これにより、設計の実装にかかる時間を節約できます。また、Tailwindのユーティリティクラスを使用した設計のウォークスルーも行います。このチュートリア

  2. Rails5でのAngularの使用

    あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた