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.
-
.remove_gem_dependency(content, gem_name) ⇒ String
Remove gem calls that reference the given gem name (to prevent self-dependency).
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 |
.remove_gem_dependency(content, gem_name) ⇒ String
Remove gem calls that reference the given gem name (to prevent self-dependency).
Works by locating gem() call nodes where the first argument matches gem_name.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/kettle/dev/prism_gemfile.rb', line 140 def remove_gem_dependency(content, gem_name) return content if gem_name.to_s.strip.empty? result = PrismUtils.parse_with_comments(content) stmts = PrismUtils.extract_statements(result.value.statements) # Find gem call nodes where first argument matches gem_name gem_nodes = stmts.select do |n| next false unless n.is_a?(Prism::CallNode) && n.name == :gem first_arg = n.arguments&.arguments&.first arg_val = begin PrismUtils.extract_literal_value(first_arg) rescue StandardError nil end arg_val && arg_val.to_s == gem_name.to_s end # Remove each matching gem call from content out = content.dup gem_nodes.each do |gn| # Remove the entire line(s) containing this node out = out.sub(gn.slice, "") end out rescue StandardError => e if defined?(Kettle::Dev) && Kettle::Dev.respond_to?(:debug_error) Kettle::Dev.debug_error(e, __method__) else Kernel.warn("[#{__method__}] #{e.class}: #{e.}") end content end |