最新Crystalらしい書き方へのリファクタリング#85
Conversation
- shard.yml: bump crystal requirement to >= 1.20.0 - lambda.cr: replace echo-subshell logging with stdlib Log, explicit block param - github/repository.cr: use HTTP::Status predicate methods, fix typos - github/models.cr: property -> getter, constant arrays + in?, presence - usecase/error: idiomatic || and backtrace? safe navigation - slack/models.cr: property -> getter, formatter refs #84 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- rename Github::Notifications -> Github::Notification (singular) - inject NotificationRepository into Github::Usecase to remove per-attachment HTTP::Client creation (N+1) - centralize ENV access in main.cr (composition root); usecases receive dependencies via constructor - Slack::PostRepository keeps only @uri; send_attachment delegates to send_attachments refs #84 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- add ameba 1.6 as a development dependency and satisfy its lints - fix broken spec_helper (required a non-existent file) and replace the placeholder spec with model unit tests (Subject#color/#update?/#comment_url, Notification#mention?, JSON parsing, Slack serialization) - add GitHub Actions CI running format check / ameba / spec - remove obsolete .travis.yml; gitignore built bin/ refs #84 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request refactors the codebase to use dependency injection, updates the Crystal version requirement, adds Ameba for linting, and introduces comprehensive unit tests for GitHub and Slack models. It also cleans up model properties to use getters, adopts more idiomatic Crystal methods (like .in? and .presence), and updates logging to use the standard Log library. Feedback is provided to ensure that errors in the alert handler do not swallow the original exception, to avoid passing full URLs directly to the HTTP client, and to optimize log chunking to reduce memory allocations.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| rescue error | ||
| alert_repo = Slack::PostRepository.new ALERT_WEBHOOK_URL | ||
| Error::Usecase.new(alert_repo, SLACK_ID, APP_ENV).alert error | ||
| raise error |
There was a problem hiding this comment.
エラーハンドリングの rescue error ブロック内で Error::Usecase#alert を呼び出していますが、もし Slack への通知(ネットワークエラーなど)が失敗して例外が発生した場合、元の error が再スロー(raise error)されず、Slack 通知の例外に上書きされてしまいます。
これにより、本来発生していたエラーのデバッグが困難になります。alert の呼び出しを begin ... rescue で囲み、Slack 通知の失敗をログ出力にとどめることで、元のエラーが確実に再スローされるように保護することを推奨します。
rescue error
begin
alert_repo = Slack::PostRepository.new ALERT_WEBHOOK_URL
Error::Usecase.new(alert_repo, SLACK_ID, APP_ENV).alert error
rescue alert_error
Serverless::Lambda.print_log "Failed to send error alert to Slack: #{alert_error.message}"
end
raise error
| @@ -43,21 +43,21 @@ module Github | |||
| end | |||
|
|
|||
| res = @github.get url | |||
There was a problem hiding this comment.
| def print_log(log : String) | ||
| log.split(//).each_slice(50000) do |line| | ||
| puts `echo '#{line.join.gsub(/(\r\n|\r|\n|\f)/, "")}'` | ||
| STDOUT.flush | ||
| log.gsub(/(\r\n|\r|\n|\f)/, "").each_char.each_slice(50000) do |chunk| | ||
| Log.info { chunk.join } | ||
| end | ||
| end |
There was a problem hiding this comment.
.each_char.each_slice(50000) を使用すると、50,000要素の Array(Char) がメモリ上に確保され、それを join して文字列を再構築するため、メモリ割り当て(アロケーション)とCPUのオーバーヘッドが非常に大きくなります。
文字列のインデックススライス(clean_log[offset, 50000])をループで回すことで、中間配列の生成を避け、大幅に効率化できます。
def print_log(log : String)
clean_log = log.gsub(/(\r\n|\r|\n|\f)/, "")
offset = 0
while offset < clean_log.size
Log.info { clean_log[offset, 50000] }
offset += 50000
end
end
Closes #84
最新の Crystal(1.20系)らしい書き方・設計・ツーリングへ全面リファクタリング。外部から見た挙動は不変。
Phase 1: イディオム・スタイル最新化 (87bf0a8)
shard.yml:crystal要件を>= 1.20.0にlambda.cr:puts `echo '...'`のサブシェルログ(インジェクション/クォート破壊リスク)を stdlibLogに置換。handlerのブロックパラメータを明示github/repository.cr: 数値比較をHTTP::Status述語(server_error?/unauthorized?/client_error?)へ。typo (retrun/faild) 修正github/models.cr:property→getter、冗長なemit_null: false削除、[...].includes?→定数配列+in?、comment_urlをpresence ||化error/usecase.cr:err.backtraceをbacktrace?.tryで安全にnotify/usecase.cr:size != 0→empty?否定Phase 2: 設計改善 (f86ca5f)
Github::Notifications→Github::Notification(1件を表すため単数形)Github::UsecaseにNotificationRepositoryをコンストラクタ注入し、attachment 1件ごとにHTTP::Clientを生成していた N+1 を解消main.cr(composition root)に集約し起動時に解決。各 usecase は依存をコンストラクタで受け取るSlack::PostRepositoryは@uriのみ保持、send_attachmentはsend_attachmentsへ委譲Phase 3: ツーリング整備 (45ca47f)
false.should eq(true))をモデルのユニットテストに置換.travis.ymlを削除、ビルド成果物bin/を gitignore検証
crystal tool format --check差分なしcrystal build src/main.cr成功(Crystal 1.20.2 / ローカル)crystal spec… 11 examples, 0 failures./bin/ameba… 13 inspected, 0 failures🤖 Generated with Claude Code