Module: Kettle::Dev::PrismGemfile
- Defined in:
- lib/kettle/dev/prism_gemfile.rb
Overview
Prism helpers for Gemfile-like merging.
Class Method Summary collapse
-
.merge_gem_calls(src_content, dest_content) ⇒ Object
Merge gem calls from src_content into dest_content.
Class Method Details
.merge_gem_calls(src_content, dest_content) ⇒ Object
Merge gem calls from src_content into dest_content.
- Replaces dest
sourcecall with src’s if present. - Replaces or inserts non-comment
git_sourcedefinitions. - Appends missing
gemcalls (by name) from src to dest preserving dest content and newlines.
This is a conservative, comment-preserving approach using Prism to detect call nodes.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/kettle/dev/prism_gemfile.rb', line 14 def merge_gem_calls(src_content, dest_content) src_res = PrismUtils.parse_with_comments(src_content) dest_res = PrismUtils.parse_with_comments(dest_content) src_stmts = PrismUtils.extract_statements(src_res.value.statements) dest_stmts = PrismUtils.extract_statements(dest_res.value.statements) # Find source nodes src_source_node = src_stmts.find { |n| PrismUtils.call_to?(n, :source) } dest_source_node = dest_stmts.find { |n| PrismUtils.call_to?(n, :source) } out = dest_content.dup dest_lines = out.lines # Replace or insert source line if src_source_node src_src = src_source_node.slice if dest_source_node out = out.sub(dest_source_node.slice, src_src) dest_lines = out.lines else # insert after any leading comment/blank block insert_idx = 0 while insert_idx < dest_lines.length && (dest_lines[insert_idx].strip.empty? || dest_lines[insert_idx].lstrip.start_with?("#")) insert_idx += 1 end dest_lines.insert(insert_idx, src_src.rstrip + "\n") out = dest_lines.join dest_lines = out.lines end end # --- Handle git_source replacement/insertion --- src_git_nodes = src_stmts.select { |n| PrismUtils.call_to?(n, :git_source) } if src_git_nodes.any? # We'll operate on dest_lines for insertion; recompute dest_stmts if we changed out dest_res = PrismUtils.parse_with_comments(out) dest_stmts = PrismUtils.extract_statements(dest_res.value.statements) # Iterate in reverse when inserting so that inserting at the same index # preserves the original order from the source (we insert at a fixed index). src_git_nodes.reverse_each do |gnode| key = PrismUtils.statement_key(gnode) # => [:git_source, name] name = key && key[1] replaced = false if name dest_same_idx = dest_stmts.index { |d| PrismUtils.statement_key(d) && PrismUtils.statement_key(d)[0] == :git_source && PrismUtils.statement_key(d)[1] == name } if dest_same_idx # Replace the matching dest node slice out = out.sub(dest_stmts[dest_same_idx].slice, gnode.slice) replaced = true end end # If not replaced, prefer to replace an existing github entry in destination # (this mirrors previous behavior in template_helpers which favored replacing # a github git_source when inserting others). unless replaced dest_github_idx = dest_stmts.index { |d| PrismUtils.statement_key(d) && PrismUtils.statement_key(d)[0] == :git_source && PrismUtils.statement_key(d)[1] == "github" } if dest_github_idx out = out.sub(dest_stmts[dest_github_idx].slice, gnode.slice) replaced = true end end unless replaced # Insert below source line if present, else at top after comments dest_lines = out.lines insert_idx = dest_lines.index { |ln| !ln.strip.start_with?("#") && ln =~ /^\s*source\s+/ } || 0 insert_idx += 1 if insert_idx dest_lines.insert(insert_idx, gnode.slice.rstrip + "\n") out = dest_lines.join end # Recompute dest_stmts for subsequent iterations dest_res = PrismUtils.parse_with_comments(out) dest_stmts = PrismUtils.extract_statements(dest_res.value.statements) end end # Collect gem names present in dest (top-level only) dest_res = PrismUtils.parse_with_comments(out) dest_stmts = PrismUtils.extract_statements(dest_res.value.statements) dest_gem_names = dest_stmts.map { |n| PrismUtils.statement_key(n) }.compact.select { |k| k[0] == :gem }.map { |k| k[1] }.to_set # Find gem call nodes in src and append missing ones (top-level only) missing_nodes = src_stmts.select do |n| k = PrismUtils.statement_key(n) k && k.first == :gem && !dest_gem_names.include?(k[1]) end if missing_nodes.any? out << "\n" unless out.end_with?("\n") || out.empty? missing_nodes.each do |n| # Preserve inline comments for the source node when appending inline = begin PrismUtils.inline_comments_for_node(src_res, n) rescue [] end line = n.slice.rstrip if inline && inline.any? inline_text = inline.map { |c| c.slice.strip }.join(" ") # Only append the inline text if it's not already part of the slice line = line + " " + inline_text unless line.include?(inline_text) end out << line + "\n" end end out rescue StandardError => e # Use debug_log if available, otherwise Kettle::Dev.debug_error if defined?(Kettle::Dev) && Kettle::Dev.respond_to?(:debug_error) Kettle::Dev.debug_error(e, __method__) else Kernel.warn("[#{__method__}] #{e.class}: #{e.}") end dest_content end |