Class: GemOverrideMarker::SourceExtractor
- Inherits:
-
Object
- Object
- GemOverrideMarker::SourceExtractor
- Defined in:
- lib/gem_override_marker/source_extractor.rb
Overview
Ruby ソースから対象メソッドの実装を抽出し、インデントを正規化する。
def から end までを正規表現で切り出すことはネストした end の判別が
できず不可能なため、Ruby 3.3 標準の prism で AST 解析してメソッド単位で
slice する。本家ソース・override の双方を同じ方式で抽出することで、
クラス直下 (本家) と module 多階層 (decorator) のネスト差を吸収できる。
Defined Under Namespace
Classes: MethodNotFoundError
Class Method Summary collapse
-
.dedent(code) ⇒ Object
prism の slice は 1行目 (def) が左寄せ・body 行が元のインデントのまま 返るため、body の最小インデントを def の位置まで詰めてネスト差を吸収する。.
-
.extract(file_path, method_name) ⇒ Object
指定ファイルから対象メソッドの実装を抽出して dedent する。 method_name が nil の場合はファイル全体を返す (view / partial 等)。.
-
.indent_width(line) ⇒ Object
行頭の半角スペース数を返す。.
-
.slice_method(source, method_name) ⇒ Object
AST を走査して対象メソッドの DefNode を探し、その範囲を slice する。 最初に見つかった定義を返す。見つからなければ nil。.
Class Method Details
.dedent(code) ⇒ Object
prism の slice は 1行目 (def) が左寄せ・body 行が元のインデントのまま 返るため、body の最小インデントを def の位置まで詰めてネスト差を吸収する。
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/gem_override_marker/source_extractor.rb', line 48 def self.dedent(code) lines = code.lines return code if lines.empty? base_indent = indent_width(lines.first) body = lines[1..] || [] body_indents = body.reject { |line| line.strip.empty? }.map { |line| indent_width(line) } min_body_indent = body_indents.min || base_indent remove = [ min_body_indent - base_indent, 0 ].max result = [ lines.first ] result += body.map { |line| line.strip.empty? ? line : line[remove..] } result.join end |
.extract(file_path, method_name) ⇒ Object
指定ファイルから対象メソッドの実装を抽出して dedent する。 method_name が nil の場合はファイル全体を返す (view / partial 等)。
15 16 17 18 19 20 21 22 23 |
# File 'lib/gem_override_marker/source_extractor.rb', line 15 def self.extract(file_path, method_name) source = File.read(file_path) return dedent(source) if method_name.nil? sliced = slice_method(source, method_name.to_sym) raise MethodNotFoundError, "method #{method_name} not found in #{file_path}" if sliced.nil? dedent(sliced) end |
.indent_width(line) ⇒ Object
行頭の半角スペース数を返す。
65 66 67 |
# File 'lib/gem_override_marker/source_extractor.rb', line 65 def self.indent_width(line) line[/\A */].size end |
.slice_method(source, method_name) ⇒ Object
AST を走査して対象メソッドの DefNode を探し、その範囲を slice する。 最初に見つかった定義を返す。見つからなければ nil。
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/gem_override_marker/source_extractor.rb', line 27 def self.slice_method(source, method_name) result = Prism.parse(source) found = nil visit = lambda do |node| return if node.nil? || found if node.is_a?(Prism::DefNode) && node.name == method_name found = node.location.slice return end node.compact_child_nodes.each { |child| visit.call(child) } end visit.call(result.value) found end |