diff --git a/cherry.txt b/cherry.txt index be2200c..2c89892 100644 --- a/cherry.txt +++ b/cherry.txt @@ -384,4 +384,77 @@ variableパターン:in節のパターンに変数を書いてローカル変 451ページから ## 3/31 +arrayパターン:in節に[]を使って配列の構造パターンを指定する利用パターン。[]の中に書いた変数には対応する要素の値が代入される +case [1,2,3] +in[a,b,c] +end +変数ではなく数値や文字列をそのままin節に指定すると「その値と等しいこと」がマッチの条件になる +アンダースコアで始まる変数:これは「任意の要素」を表現する目的で例外的にin節で2回以上使うことができる +in節で*を使うと任意の長さの要素を指定したことになる +hashパターン:in節に{}を使ってハッシュの構造パターンを指定する利用パターン。値に変数を指定すると、その変数に対応する値が格納される +case {name: 'Alice', age: 20} +in {name: name, age: age} + "name=#{name}, age=#{age}" +end +値の変数を省略するとキーと同じ名前の変数に値が代入される +hashパターンではハッシュの各要素がin節で指定したパターン(キーと値、またはキーのみ)に部分一致すればマッチしたと判定される。ただし、in節に{}を書いた場合は例外的に「空のハッシュに完全一致」することがマッチの条件になる +hashパターンのin節はkey:value形式のパターンしか許容されていない。key => value形式を使おうとすると構文エラーが発生する +in節に書くのはあくまでパターン:パターンは一見、配列やハッシュのように見える場合があるが、これらはあくまで記法を似せただけの「パターン」である。 +asパターン:パターンマッチでマッチしたオブジェクトを変数に代入する利用パターン +case {name: 'Alice', age: 20, gender :female} +in {name: String => name, age: 18.. => age} + "name=#{name}, age=#{age}" +end +一番外側に=>変数名と書くとマッチしたオブジェクト全体を取得できる +alternativeパターン:2つ以上のパターンを指定し、どれかに1つマッチすればマッチしたと見なす利用パターン +case 2 +in 0 | 1 | 2 + 'matched' +end +arrayパターン、hashパターン、asパターンとは組み合わせられるがvariableパターンとはできない +findパターン:アスタリスクを2回使って「前と後ろにある任意の要素」をパターンとして表現できるRuby3.0以降の機能を活かしたパターン。これにより、配列野中から特定のパターンに合致する部分を抜き出すことができる。 +case [13, 11, 9, 6, 12, 10, 15, 5, 7, 14] +in[*, 10.. => a, 10.. => b, 10.. => c, *] + "a=#{a}, b=#{b}, c=#{c}" +end +ガード式: +case 式 +in パターン if 条件式 + パターンにマッチし、なおかつ条件式が真になった場合に実行する処理 +end +このような形式でin節に条件式を追加できる。case節の式がin節のパターンにマッチすることに加え、この条件式も真になった場合にin節に対応する処理が実行される。以上の条件式をガード式と言う +1行パターンマッチ:Rubyのパターンマッチではcase節を省略して"評価したい式 in パターン"を一行で書くこともできる。マッチすればtrue、しなければfalseが返る +[1,2,3] in [Integer, Integer, Integer] # => true +[1,2, 'x'] in [Integer, Integer, Integer] # => false +また、1行パターンマッチは"式 => パターン"という記法も用意されている。主にパターンマッチを使った変数代入を利用するために使う +変数のスコープに関する注意点:Rubyのパターンマッチは独自の変数スコープを作らないので、すでに同名のローカル変数が存在していると意図せず上書きされる可能性がある。パターンマッチ内で新たに定義された変数は外部でも使用できる +ここから第12章 +よく発生する例外クラスとその原因 +NameError:単純なタイプミスであることが多いが、外部ファイルやライブラリのrequireを忘れている場合にも発生することがある +NoMethodError:存在しないメソッドや可視性が制限されているため呼び出せないメソッドを呼び出そうとした場合に発生する。特にレシーバがnilになっているケースは非常に多いとされる +TypeError:期待しない型がメソッドの引数に渡された時に発生する +ArgumentError:引数の数が違ったり、期待する値ではなかったりした場合に発生する +ZeroDivisionError:整数を0で除算しようとした時に発生する +SystemStackError:スタックが溢れた時に発生する。特に間違ってメソッドを再帰呼び出しした場合に発生する +LoadError:requireやrequire_relativeの実行に失敗した時に発生する +SyntaxError:構文エラー。endやカンマの数に過不足がある、カッコがきちんと閉じられていないなどの原因で起こる +Segmentation fault:略してセグフォ。SEGVと呼ばれることもある。Ruby本体や拡張ライブラリに不具合がある時に発生する極めて稀なエラー。 +tap:ブロックパラメータにレシーバをそのまま渡すメソッド。ブロックの戻り値は無視され、tapメソッド全体の戻り値はレシーバ自身になる +debug.gemの代表的なコマンド: +step/s 実行を1行進めて停止する。その行にメソッド呼び出しがあればそのメソッドの中に入って停止する(ステップイン) +next/n 実行を1行進めて停止する。その行にメソッド呼び出しがあればそのメソッドを実行してから次の行で停止する(ステップオーバー) +finish/fin 現在実行中のメソッドを最後まで実行し、呼び出し元に戻ってきたところで停止する(ステップアウト) +continue/c プログラムを再開する。停止すべきポイント(ブレークポイント)がなければ、そのプログラムの最後までプログラムを実行する +p/pp Rubyのpやppのように指定された式の値を表示する +リターン 直前に実行したコマンドを繰り返す +help/h 使用可能なコマンドとその説明を表示する。help+コマンド名で特定のコマンドのヘルプを表示することもできる +quit/q デバッガを途中で終了する +パソコンの前から離れる:実際これで解決することがプログラミングに限らずよくある +504ページから + +## 4/1 + + + + diff --git a/main.rb b/main.rb index 644a9ad..8065296 100644 --- a/main.rb +++ b/main.rb @@ -1,37 +1,22 @@ -module Effects - def self.reverse - lambda do |words| - words.split(' ').map(&:reverse).join(' ') - end - end +require 'net/http' +require 'uri' +require 'json' - def self.echo(rate) - lambda do |words| - words.each_char.map { |c| c == ' ' ? c : c * rate }.join - end - end +module LogFormatter + def self.format_log + uri = URI.parse('https://samples.jnito.com/access-log.json') + json = Net::HTTP.get(uri) + log_data = JSON.parse(json, symbolize_names: true) - def self.loud(level) - lambda do |words| - words.split(' ').map { |word| word.upcase + '!' * level }.join(' ') - end - end -end - -class WordSynth - def initialize - @effects = [] - end - - def add_effect(effect) - @effects << effect - end - - def play(original_words) - words = original_words - @effects.each do |effect| - words = effect.call(words) - end - words + log_data.map do |log| + case log + in {request_id:, path:, status: 404 | 500 => status, error:} + "[ERROR] request_id=#{request_id}, path=#{path}, status=#{status}, error=#{error}" + in {request_id:, path:, duration: 1000.. => duration} + "[WARN] request_id=#{request_id}, path=#{path}, duration=#{duration}" + in {request_id:, path:} + "[OK] request_id=#{request_id}, path=#{path}" + end + end.join("\n") end end diff --git a/main_test.rb b/main_test.rb index d6da660..5da2593 100644 --- a/main_test.rb +++ b/main_test.rb @@ -1,46 +1,13 @@ require 'minitest/autorun' require_relative 'main' -class EffectTest < Minitest::Test - def test_reverse - effect = Effects.reverse - assert_equal 'ybuR si !nuf', effect.call('Ruby is fun!') - end - - def test_echo - effect = Effects.echo(2) - assert_equal 'RRuubbyy iiss ffuunn!!', effect.call('Ruby is fun!') - - effect = Effects.echo(3) - assert_equal 'RRRuuubbbyyy iiisss fffuuunnn!!!', effect.call('Ruby is fun!') - end - - def test_loud - effect = Effects.loud(2) - assert_equal 'RUBY!! IS!! FUN!!!', effect.call('Ruby is fun!') - - effect = Effects.loud(3) - assert_equal 'RUBY!!! IS!!! FUN!!!!', effect.call('Ruby is fun!') - end -end - -class WordSynthTest < Minitest::Test - def test_play_without_effects - synth = WordSynth.new - assert_equal 'Ruby is fun!', synth.play('Ruby is fun!') - end - - def test_play_with_reverse - synth = WordSynth.new - synth.add_effect(Effects.reverse) - assert_equal 'ybuR si !nuf', synth.play('Ruby is fun!') - end - - def test_play_with_many_effects - synth = WordSynth.new - synth.add_effect(Effects.echo(2)) - synth.add_effect(Effects.loud(3)) - synth.add_effect(Effects.reverse) - assert_equal '!!!YYBBUURR !!!SSII !!!!!NNUUFF', synth.play('Ruby is fun!') +class LogFormatterTest < Minitest::Test + def test_format_log + text = LogFormatter.format_log + lines = text.lines(chomp: true) + assert_equal '[OK] request_id=1, path=/products/1', lines[0] + assert_equal '[ERROR] request_id=2, path=/wp-login.php, status=404, error=Not found', lines[1] + assert_equal '[WARN] request_id=3, path=/products, duration=1023.8', lines[2] + assert_equal '[ERROR] request_id=4, path=/dangerous, status=500, error=Internal server error', lines[3] end end