2026年2月のふりかえり

そういえば書いていなかったことを思い出したので書いておく。

前回はこれ:

kozy4324.hatenablog.jp

OSS活動

rbs-siggenというのを開発している。READMEはまだちゃんと書いていない。このネタで記事1本書こうと思っている。

付随してデバッグ用途でgemを2つほど開発した。

基本的にREADMEの記述はAIに丸投げしている。traceologistについては100%バイブコーディングで作られている。ユーザーが自分だけの小規模gemぐらいならバイブコーディングも悪くないなって思った。

英語学習

Speak & NHKゴガクの2本立てで継続している。

読書

2月はコードばっか書いていてマジで読書していない。呪術廻戦モジュロ②は読んだ。

ランニング

復活!というほど距離は稼げていない。花粉つらいよ花粉。しっかり距離を走っていこう。

ブログ

アウトプット少ないね。意識していこう。

勉強会

3ヶ月ぶりにKashiwa.rbに参加した。3月もやるぞ!

KPT

  • Keep
    • OSS活動
    • 英語学習
    • ランニング
    • 勉強会
  • Problem
    • 読書
    • ブログ
  • Try
    • アウトプットするぞ

objenealogistというgemを作った

github.com

発音と命名の由来

Object(オブジェクト) + Genealogist(ジーニーアーロジスト) -> オブジーニーアーロジスト

という想定なのだけど、本当にそう発音していいかは知らない。造語です。

Genealogistは「家系、血縁関係、家系図を専門的に調査・研究する専門家」ということで、家系図=クラス継承ツリーを出力するということでこういう命名をしました。

使い方

例えばMyClassクラス があったとしてClassクラスに #to_tree メソッドが生えているのでそれを呼びます。

puts MyClass.to_tree

以下のような出力結果が得られます。

C MyClass (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:39)
│ ├ c (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:43)
│ └ singleton_c (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:44)
│
├── M M2 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:7)
│ └ m2 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:8)
│
├── M M1 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:3)
│ └ m1 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:4)
│
└── C NS::C2 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:32)
    │ └ c2 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:35)
    │
    ├── M M4 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:15)
    │ └ m4 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:18)
    │
    ├── M M3 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:11)
    │ └ m3 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:12)
    │
    └── C C1 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:25)
        │ └ c1 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:28)
        │
        ├── M M5 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:21)
        │ └ m5 (location: /Users/kozy4324/work/objenealogist/test/my_class.rb:22)
        │
        └── C Object (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:700)
            ├── M PP::ObjectMixin (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:348)
            │ ├ pretty_print (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:362)
            │ ├ pretty_print_cycle (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:379)
            │ ├ pretty_print_inspect (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:402)
            │ └ pretty_print_instance_variables (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:390)
            │
            ├── M Kernel (location: /Users/kozy4324/.rbenv/versions/4.0.0/lib/ruby/gems/4.0.0/gems/pp-0.6.3/lib/pp.rb:720)
            └── C BasicObject

クラス定義とメソッド定義のsource locationも出力されるのでVSCodeであれば command + click でコードジャンプできるので便利ですね。

ブログ記事上だとlocationあると見づらいのでシンプルなバージョンも貼っておきます。

> puts MyClass.to_tree(show_locations: false)
C MyClass
│ ├ c
│ └ singleton_c
│
├── M M2
│ └ m2
│
├── M M1
│ └ m1
│
└── C NS::C2
    │ └ c2
    │
    ├── M M4
    │ └ m4
    │
    ├── M M3
    │ └ m3
    │
    └── C C1
        │ └ c1
        │
        ├── M M5
        │ └ m5
        │
        └── C Object
            ├── M PP::ObjectMixin
            │ ├ pretty_print
            │ ├ pretty_print_cycle
            │ ├ pretty_print_inspect
            │ └ pretty_print_instance_variables
            │
            ├── M Kernel
            └── C BasicObject

作ったモチベーション

RailsActiveRecordモデルのメタプログラミング事情を調べたくて作りました。例えばDBカラムに title というカラムがあった場合に #title, #title=, #title? といったメソッドがメタプログラミングによって生えるのですが、それらがクラス継承ツリー上はどこで定義されることになるメソッドなのか、また同様にメタプログラミングで生えるメソッドは他に何があるのかあたりを確認したいなとなりました。

Rubyは動的定義されたメソッドも動的に取得できる Object#methodsModule#instance_methods あたりが使えるので便利ですよね。

実際に出力した内容はgistに置きました。動的に定義されるDBカラムメソッド群は Article::GeneratedAttributeMethods に定義されているのが分かると思います(このmoduleもまた動的に定義されたmoduleです)。

https://gist.github.com/kozy4324/2e1e3c565f12329007922f2ec4092da8

実装を一工夫したところ

メソッド定義位置は Object#method で取得できるMethodオブジェクトの#source_locationから取得可能ですが、moduleやclassの定義位置の取得はAPIには見当たりませんでした。なのでメソッド定義位置から取得できるファイルパスでソースファイルを読み込み→Prismパース→ASTレベルで走査という順番でやってみました。ASTをシュッと利用できるRubyistってカッコいいですよね。私もそんなRubyistになりたい。

  class ClassVisitor < Prism::Visitor
    attr_reader :found

    def initialize(target_class_names)
      @target_class_names = target_class_names.map(&:to_s).map(&:to_sym)
      @found = []
      @stack = []
      super()
    end

    def visit_class_node(node)
      @stack << node.name
      name = @stack.join("::").to_sym
      @found << [name, node.location] if @target_class_names.include?(name)
      super
      @stack.pop
    end

    alias visit_module_node visit_class_node
  end
        source = File.read(path)
        visitor = ClassVisitor.new(clazz.ancestors)
        Prism.parse(source).value.accept(visitor)
        visitor.found.each do |name, def_location|
          (location_map[name] ||= []) << [path, def_location]
        end

1歩進んだ使い方: 出力結果をLLMに渡す

irbRails console上で使うことを想定して作りました。ActiveRecordモデルの継承やメソッド数になるとコピペもままならないので、お手軽にファイル出力できるようにもしておきました。

Article.to_tree >> "article_tree.txt"

この出力結果をLLMにぶん投げればいいわけですね。

$ cat article_tree.txt | claude "Articleモデルのクラスツリーで、これをベースに要点だけを絞ってクラス図を書いて"

みたいな感じで出力してもらった内容が以下の通り。悪くない。

Article Model Class Diagram
===========================

┌─────────────────────────────────────────────────────────────┐
│                         Article                              │
├─────────────────────────────────────────────────────────────┤
│ Attributes:                                                  │
│   id, title, body, user_id, created_at, updated_at          │
├─────────────────────────────────────────────────────────────┤
│ + GeneratedAssociationMethods                                │
│ + GeneratedAttributeMethods                                  │
└───────────────────────────┬─────────────────────────────────┘
                            │ extends
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    ApplicationRecord                         │
└───────────────────────────┬─────────────────────────────────┘
                            │ extends
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    ActiveRecord::Base                        │
├─────────────────────────────────────────────────────────────┤
│ 主要な include モジュール:                                    │
│                                                              │
│ [永続化・CRUD]                                               │
│   ├─ Persistence      (save, destroy, update, reload...)    │
│   ├─ Callbacks        (before_save, after_create...)        │
│   └─ Transactions     (transaction, with_lock...)           │
│                                                              │
│ [バリデーション]                                              │
│   ├─ Validations      (valid?, validate!, errors)           │
│   └─ ActiveModel::Validations                                │
│                                                              │
│ [属性・アクセス]                                              │
│   ├─ AttributeMethods ([], []=, attributes, has_attribute?) │
│   │   ├─ Read/Write   (read_attribute, write_attribute)     │
│   │   ├─ Dirty        (changed?, saved_changes?)            │
│   │   └─ PrimaryKey   (id, id=, to_key)                     │
│   └─ Attributes                                              │
│                                                              │
│ [関連付け]                                                    │
│   ├─ Associations     (association, belongs_to, has_many)   │
│   ├─ AutosaveAssociation                                     │
│   └─ NestedAttributes                                        │
│                                                              │
│ [クエリ・スコープ]                                            │
│   ├─ Scoping          (default_scope, scope)                │
│   └─ Inheritance      (STI support)                          │
│                                                              │
│ [ロック]                                                      │
│   ├─ Locking::Optimistic  (楽観的ロック)                     │
│   └─ Locking::Pessimistic (lock!, with_lock)                │
│                                                              │
│ [その他]                                                      │
│   ├─ Turbo::Broadcastable (broadcast_append, refresh...)   │
│   ├─ ActiveStorage::Attached::Model (attachment_changes)    │
│   ├─ GlobalID::Identification (to_gid, to_sgid)             │
│   ├─ Integration      (cache_key, to_param)                 │
│   └─ Serialization    (to_json, as_json)                    │
└───────────────────────────┬─────────────────────────────────┘
                            │ extends
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                         Object                               │
│  (ActiveSupport拡張: blank?, present?, try, in?, etc.)      │
└─────────────────────────────────────────────────────────────┘


主要なメソッドの由来まとめ
==========================

| 機能                       | モジュール                    |
|----------------------------|------------------------------|
| save, destroy, update      | Persistence                  |
| valid?, errors             | Validations                  |
| title, body, user_id       | GeneratedAttributeMethods    |
| changed?, saved_changes?   | AttributeMethods::Dirty      |
| before_save, after_create  | Callbacks                    |
| transaction, with_lock     | Transactions                 |
| broadcast_append           | Turbo::Broadcastable         |
| to_gid, to_sgid            | GlobalID::Identification     |

まとめ

Railsソースコードリーディングや内部実装を探るお供にobjenealogistというgemを作ってみました。LLMと連携する前提でこういったツール作るのも悪くないです。

2026年1月のふりかえり

1月〜2月頭まで家庭のいろいろ(子1の受験本番、子2の習い事発表会、etc...)があり多忙だった。ゆえにいつものこの「ふりかえり」も遅れての更新。

前回はこれ:

kozy4324.hatenablog.jp

OSS活動

忙しいなりにも続けている。

前回はherb/reactionviewを触っていたけど、投げたPRがスルーされると「まぁいいか」という感じで別のことをやっていた。

Rubyの型関連、RBSあたりを触っていた。gem_rbs_collection に趣味でrbs追加してて、差分管理するアイデアが思いついたのでgemを一つ作成した。

GitHub - kozy4324/rbs-patch

おそらく需要がないのでアイデアを形にしたところで一定満足したのだけど、rbsのASTとか理解できたのでそれはそれで良かった。

rbs関連で別アイデアがあって、Rubyの動的なメソッド定義に対して、sigも動的に生成できればいいのではって思ってrbs-siggenというgemを作り始めた。まだREADMEもちゃんと書いていないのでここで詳細を書いてみる。

class A
  def self.scope(name)
    define_method("generated_#{name}") do
      puts "hello generated #{name}"
    end
  end

  scope :hoge
end

というクラスがあれば、

class A
  def self.scope: (Symbol name) -> void
end

というsigがベースにあって、動的に生成されたメソッドに対しては以下のsigがあれば良い。

class A
  def generated_hoge: () -> void
end

rbsのannotationsにそういうsigのテンプレートがあればいけるんちゃうか?というのを思いついた。

class A
  %a{siggen:
    def generated_<%= name %>: () -> void
  }
  def self.scope: (Symbol name) -> void
end

これぐらいならASTを走査すればできる。この方針でどんなことができるのかを探ってみたいと思って手を動かしている。

英語学習

Speakばっかりやっている。あとNHKゴガク。ここは継続。

読書

久しぶりに技術書じゃない書籍を読了した。

企業変革のジレンマ 「構造的無能化」はなぜ起きるのか https://www.amazon.co.jp/dp/4296115928

チームの能力は単なる個人の能力の総和にはならないって話はよくあって、総和以上にもなれば以下にもなる。この本では企業組織においてなぜ総和以下になるのかという話がなされており、過去に自分が所属した組織とも重なる部分が多々あって面白く(?)読ませてもらった。チームとか組織に関する話は割と好きな方なので、これ系の書籍はまた興味あるやつを発見すれば読んでいきたい。

ランニング

いろいろがあり全然走れなかった。体調に気をつけながら巻き返していきたい。

ブログ

全然更新してなかった。小ネタは日々あるのでちゃんとアウトプットしていこう。

勉強会

12月〜1月は自粛してたので参加はゼロ。2月からやっていきです。

というわけで今月末2/27(金)に主催するKashiwa.rbをやります!参加するのは3ヶ月ぶりです。

kashiwarb.connpass.com

KPT

  • Keep
    • OSS活動
    • 英語学習
    • 読書
  • Problem
    • ブログ
  • Try
    • ランニング
      • まずはペースを戻すところから!
    • ブログ
      • ちゃんと書く!
    • 勉強会
      • やっていくぞ!

OSS活動記 #6 - rbs_rails - prismパーサーを利用するようにする

対象リポジトリ

github.com

作成したPR

github.com

経緯

rbs_railsRailsアプリケーションでのrbsRubyの型定義ファイル)作成を支援してくれるもの。ActiveRecordモデルはDBカラムから動的にメソッド群が生える。そういった動的に生えるメソッドをケアしてくれるのがrbs_rails

RailsアプリでのSteep利用を改めて整理しようと思ってrbs_railsを実行したら以下のwarningが出ていた。

warning: parser/current is loading parser/ruby33, which recognizes 3.3.x-compliant syntax, but you are running 4.0.0.
Please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.

これrubocop-astで見たことあるやつだ!となったのでrbs_railsでもparser使っていることがすぐ検知できた。Ruby 4.0.0を使っているのだが、Ruby 3.4でもなくRuby 3.3系でパースされるのは心象良くないなと思ったのでコードを覗いてみた。

rbs_railsでparser使っている実装

以下でASTを取得しているのに利用している。

https://github.com/pocke/rbs_rails/blob/f1a7764617f57aac96e73d81fba1fedf5929bf0d/lib/rbs_rails/active_record.rb#L474

ASTからいくつかのメソッド呼び出しパターンを検知してrbsを追加する実装になっているようだ。そこらへんの詳細は主題から外れるのでこんなところでよし。

Parser::CurrentRuby とは

「parser」と書くと曖昧さがあるので「whitequark/parser」とする。そのgemの中に、実行時のRubyバージョンをチェックしてバージョン毎のパーサークラスを振り分けてくれるものが parser::CurrentRuby

https://github.com/whitequark/parser/blob/9520c3ac88f808595eea8f517c2eb271867f9a61/lib/parser/current.rb

冒頭のwarningsは warn_syntax_deviation メソッドで定義されているものだということもこの実装から確認できる。

PrismのTranslationレイヤー

Prismパーサーは既存パーサーとの互換性を持っておりI/FはそのままでPrismパーサーへと移行可能になっている。ここら辺の実装を把握&参考にさせてもらったのはrubocop-astでの実装なのでそのリンクを貼っておく。

https://github.com/rubocop/rubocop-ast/blob/69036498c11ca944c6099d1b672ba408f34a3eb4/lib/rubocop/ast/processed_source.rb#L260-L325

whitequark/parserの Parser::RubyXX クラスと Prism::Translation::ParserXX クラスとで互換性があることがこの実装からも確認できる。

Prismにも Prism::Translation::ParserCurrent があるけど使えるのは v1.5.0 から

以下で追加されている。

Add `Prism::Translation::ParserCurrent` · ruby/prism@77177f9 · GitHub

残念ながらrbs_railsのprismはv1.2.0でpinされてしまっている(rbs-inlineの依存がそうなっているため)

実装したバージョン分岐ロジック

rbs_railsRuby 3.2 未満はサポートしていないので気にしないくていいよ、とレビューコメントもらったので以下のようにした。

      private def parser_class #: untyped
        case RUBY_VERSION
        when /^3\.2\./
          # backward campatibility
          require 'parser/current'
          Parser::CurrentRuby
        when /^3\.3\./
          Prism::Translation::Parser33 # steep:ignore
        when /^3\.4\./
          Prism::Translation::Parser34 # steep:ignore
        else
          # For Prism v1.5.0+, Prism::Translation::ParserCurrent should be used instead.
          Prism::Translation::Parser34 # steep:ignore
        end
      end

Prismのバージョン上げたら Prism::Translation::ParserCurrent を利用するようにしたい。

まとめ

rubocop-astのソースコードリーディングしてparser周りの事情を少し把握できていたのでPR作成までスムーズにいけた。色々興味を持ってソースコードリーディングしておくものですね。

OSS活動記 #5 - herb - HTML Spec にある Content Model に準拠しているかチェックする Linter ルールを作る

対象リポジトリ

github.com

背景課題

herb のツールセットの中に HTML + ERB ファイルのチェックをしてくれる Linter が含まれる。

Herb Linter | Herb

そのルールの1つに html-no-block-inside-inline というものがあるのだけど、

Linter Rule: No block elements inside inline elements | Herb

偽陽性になっている!というIssueが目についていた。

ということでデフォルトでは無効化され「後でなんとかする」というIssueが鎮座している。

github.com

HTML要素の配置を block or inline で決定するということへの違和感

block or inline というのは CSSdisplay だったり、フローレイアウトで出てくる概念。

CSS フローレイアウト - CSS | MDN

たしかに inline 要素の子要素に block 要素は持って来れないという認識はあるのだけど、 block or inline って特定要素に一意に決まるんだっけ?ということを思った。思ったので HTML Spec を確認する。

解釈としては Content Model で決定するということになる。例えば span 要素の下に div が置けないという規則もそれぞれに記載されている Content Model と Categories を照らし合わせれば確認可能。

既存 html-no-block-inside-inline ルールのそもそもの方針が間違っていそうなので、これは直したくなった。

Issue にコメントを残して開発に着手

いちおう作者はどう考えているかを確認したかったのでコメントを投げておいた。

https://github.com/marcoroth/herb/issues/267#issuecomment-3690764419

レスから「Content Model ベースで作ったらええがな」という気配を感じたので開発を始める。

地域コミュニティで HTML Linter あたりの現状について質問してみる

東葛.devという地域コミュニティに参加させてもらっているのだけど、フロントエンド方面に明るい方が何人かいらっしゃる気配を感じていたので「最近の HTML Linter 事情ってどないでっか?」という質問を投げてみた。Content Model 関連の Lint ルールがあれば参考にしてみようという魂胆でした。

markuplint はいいぞ〜という情報を得る。markuplintはいいぞ〜

markuplint.dev

あとこういう時に気軽に質問できる地域コミュニティもいいぞ〜

markuplint も参考にしつつ、実装方針を練る

さて、Content Model をベースにする場合、HTML Spec に記載されている全ての要素の Categories と Content Model を網羅しておく必要があるなと考えていた。markuplint を参考にしてもそこらへんの情報をデータとして保持している。ルックアップテーブル的な何かを作る必要がある。ただ HTML Spec に書いてある内容を手で書き写していくのもダルいのでまずはスクレイピングして雛形データを作ることにした。

書き捨てのスクリプトRuby で書くに限る。

https://gist.github.com/koji-nakamura-classi/aac4ea9ba6ed5acdd8819bd6fc5ba11f#file-parser-rb

以下のような JSON データの集合をゲットできた。

  :
  "p": {
    "categories": [
      "flow"
    ],
    "contentModel": [
      "phrasing"
    ],
    "link": "https://html.spec.whatwg.org/multipage/grouping-content.html#the-p-element"
  },
  :

Content Model のパターンごとの実装

JSON データを単純にルックアップすれば終わりか、と思いきやそんなに話はシンプルではなかった。

1. 単純ルックアップパターン

子要素の Categories が親要素の Content Model に適合していれば OK になる。これは最もシンプルなパターン。

2. Transparentで祖先要素に移譲されるパターン

例えば <a> タグ。この Content Model は Transparent

https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element

これは何かというと <a> タグの親タグの Content Model がみるべきものになるやつ。

<div><a>...</a></div> の場合は <div> の Content Model が適用されるので Flow<span><a>...</a></span> の場合は <span> の Content Model が適用されるので Phrasing ということになる。

3. 特定タグが許可されるパターン

例えば <ul> タグと <li> タグ。 <li> タグは Categories が None. となっており、 <ul> タグといった特定タグでのみ利用可能な扱いとなっている。

4. 親要素・祖先要素によって Content Model が変わるパターン

<div><span> がそう。

ただ祖先が <option> or <optgroup> or <select> というやつは「カスタマイズ可能な select 要素」という文脈っぽく、

カスタマイズ可能な select 要素 - ウェブ開発の学習 | MDN

ポイントとしては <div> 要素が <dl> 要素内で利用可能で、その場合に限り <dt> <dd> あと script-supporting elements を受け入れるという話があるということ。

最終的に作成した PR

色々試行錯誤を重ねてこのような PR が仕上がった。

github.com

Content Model の全てを実装しようとすると実装が巨大で複雑になりそうだったので html-no-block-inside-inline の上位互換にはなる程度に留めて PR を出してみた。コメントにも書いたのだけど、この段階でもIssueを6個ほどまとめて解決できそうなのがとても良い。

作者から年明けにフィードバックが貰えると嬉しいな。

今回のまとめ

  • オープンスタンダードな仕様(今回の話であれば HTML Spec)をちゃんと押さえておくの大事だなぁと改めて思った
  • 久しぶりに TypeScript をちゃんと書いたのだけど、仕様の語彙をいい感じに型やコードへ落として込めた気がする
  • 地域コミュニティはいいぞ〜

2025年12月のふりかえり

今年最後のふりかえり。1年のふりかえりは別で記事を書こうかな。書くかもしれないし書かないかもしれません。

前回はこれ:

kozy4324.hatenablog.jp

OSS活動

herb/reactionviewを触るのが楽しくなってきている。12月に作成したPRとIssueを並べてみる。

あとPRは作っていないのだけどreactionviewのDraft PRにコメントしたやつもある。

モチベーションは何か?というところなんだけど、お仕事でRailsアプリケーションの開発に携わっており、herb/reactionviewあたりが良くなれば自身の開発体験も良くなるだろうということで取り組んでいる。来年も取り組んでいきたい。

英語学習

今年の2月からの継続がキープしている。あとNHKゴガクのラジオ英会話もちゃんと1週間分かかさず聴くように戻した。NHKゴガク本当におすすめ。

www.nhk.jp

読書

コード書いてばかりで今月は何も読んでいなかった。正月休み中に何か読み始めよう。

ランニング

忙しさに忙殺された。後半まったく距離を稼げず...。来年に期待!

体重

たぶんキープしている。走ったら記録していたのでランニングしないと計測しなくなるということが分かった。

ブログ

OSS活動したら些細なことでも書くようにした。まだ書いてないものがひとネタあるのでそれも書こう。 書いた。

勉強会

来年の2月頭まで休止期間なので「無」です。

KPT

  • Keep
    • OSS活動
    • 英語学習
    • ブログ
  • Problem
    • 読書
    • ランニング
  • Try
    • 1冊/月の読書ペースを取り戻そう
    • ランニング、1月も距離は稼げないかもしれないが走れる時は走ろう

OSS活動記 #4 - herb - デバッグモードのoverlay label表示を良しなにする

対象リポジトリ

github.com

作成したPR(2つ)

キリ番 999 と 1000 をゲットした。やったぜ

github.com

github.com

起きていた問題

reactionviewのデバッグモードで出力するoverlay labelというものがある。

DOMツリーに動的に要素を追加してくれるのだけど少しイケていない感じだったので修正した。

まずoverlay labelを position: absolute で追加するために親要素を位置基準要素にする必要がある。既存の実装では position: relative を設定しているが、

parent.style.position = 'relative';

これだと元々のposition値として absolute , fixed , sticky が設定されているとレイアウト崩れが発生してしまう。 relative であればそもそも設定する必要がないので getComputedStyle() で取得した値が static の時だけ relative 設定すればいいという判断をした。

Window: getComputedStyle() メソッド - Web API | MDN

おそらくこれは実装選択肢の余地がないので気に入らなかったらrejectしてもらうしかないかなって感じでPRをまとめた。

次に、reactionviewのデバッグモードなんだけど実はちゃんと layouts/application.html.erb にもoverlay labelを出力しているがviewportからはみ出していることに気づいた。

Chrome DevToolsでスタイルを弄ったものが右

overlay labelを追加しているならちゃんとviewportに納めたいよねってことでもう一つPRを作成した。ただしこちらに関しては「そもそもCSSでスタイル定義しているのにスクリプトで条件分岐してスタイル上書きするのが実装として散らかってるな〜」と感じている。既存実装にならって条件分岐したがあまり好ましい実装とは思えないのでその部分を質問しつつPRを作成した。

余談: ローカルでの動作検証が面倒くさい

さて、このherbにあるJavaScript部分はテストが用意されていない。Rubyが出力するdebug情報とも密結合しているのでなかなかテストも書きづらいのだろうと勝手に納得しつつ、ローカルでの動作検証が最初どうやればいいか分からなかった。合っているかどうかは分からないけどとりあえず出来た方法を書き残しておく。

$ pwd
/Users/kozy4324/work/reactionview/javascript/packages/dev-tools/src

$ ls -l
total 8
lrwxr-xr-x@ 1 kozy4324  staff    60 12 21 10:27 dev-tools -> /Users/kozy4324/work/herb/javascript/packages/dev-tools/dist
-rw-r--r--@ 1 kozy4324  staff  3493 12 21 10:28 index.ts
  • reactionviewにあるTypeScriptでモジュール読み込み先を変更
$ git diff -- index.ts
diff --git a/javascript/packages/dev-tools/src/index.ts b/javascript/packages/dev-tools/src/index.ts
index 7adf7b7..1f4f558 100644
--- a/javascript/packages/dev-tools/src/index.ts
+++ b/javascript/packages/dev-tools/src/index.ts
@@ -1,4 +1,4 @@
-import { initHerbDevTools, HerbOverlay, type HerbDevToolsOptions } from "@herb-tools/dev-tools"
+import { initHerbDevTools, HerbOverlay, type HerbDevToolsOptions } from "./dev-tools/herb-dev-tools.esm"
 
 export interface ReActionViewDevToolsOptions extends HerbDevToolsOptions {
   projectPath?: string
  • reactionviewで yarn build
  • RailsアプリケーションのGemfile修正して bundle install
$ git diff -- Gemfile
diff --git a/Gemfile b/Gemfile
index 6052a88..02334ee 100644
--- a/Gemfile
+++ b/Gemfile
@@ -66,7 +66,7 @@ group :test do
 end
 
 
-gem "reactionview", "~> 0.2.0"
+gem "reactionview", path: "/Users/kozy4324/work/reactionview"
 
 gem "tailwindcss-rails", "~> 4.3"

とやることで解決した。どう考えても面倒くさすぎる。ここら辺の正しいやり方を確認しておきたい。