rubyn 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b1ca55e10bd9ae4ec17fbdbfa196577fda468788c083905679a2c1e7e782181
4
- data.tar.gz: f82e928da5975b754dca641f00488bb6360ffbd4cd45d538909348a4b1ff6b05
3
+ metadata.gz: da809f272fcbe6c989b0a09ff5917f4aa91db8b345dd29b85d2229bfe2227421
4
+ data.tar.gz: 78a8fb3249fb87a9696d73f8d4622e9421dd0d4254732eb292cb4e9f11c07f59
5
5
  SHA512:
6
- metadata.gz: da671165752719684baaf371f4c1a47d7a9f3e8604af37360b106827d15c08a1508219b7fc47ae859a1c8f54f9e756a7e7a459c66fff4cbb718e70610eb4684a
7
- data.tar.gz: 7f6f19c30b1ba42e74aeb0f3b0ec936d8f14ac63b6d52949d87c0714fce3b76c041c3cc8bf67a8eb07c36453b6d7602ae35736c46b1771646ce71bcebac748b4
6
+ metadata.gz: 1ca7d13fd823a0089510cb255ed082ee1213beb066c435374013848305102ecb95bf14b5b31f285da9029a1cc84a0071a8925a772348c7626254f38317f78980
7
+ data.tar.gz: b9a529dea633b3e2ab4ee0a92b6cb7dec54c8466a4458de65365498a14a13e06b795c7c1b6a0896d85ab135ce8f9700ebb7c1b11d23d58b4670774d4c89c4695
@@ -59,19 +59,26 @@ module Rubyn
59
59
  def extract_file_blocks(response)
60
60
  blocks = []
61
61
 
62
- # Match path header lines directly above ```ruby blocks
63
- # Handles: `[NEW] path.rb`, `path.rb`, backtick-wrapped or plain
64
- response.scan(/(?:\[NEW\]\s*)?`?([a-zA-Z0-9_\/\.\-]+\.rb)`?\s*\n```ruby\n(.*?)```/m) do
65
- path = $1
66
- code = $2
67
- is_new = $~[0].include?("[NEW]")
68
- blocks << { tag: is_new ? "NEW" : nil, path: path, code: code }
69
- end
62
+ # Split response around code blocks, keeping surrounding text for path detection
63
+ parts = response.split(/(```ruby\n.*?```)/m)
64
+
65
+ parts.each_with_index do |part, i|
66
+ next unless part.start_with?("```ruby\n")
67
+
68
+ code = part.sub(/\A```ruby\n/, "").sub(/```\z/, "")
69
+ preceding_text = i > 0 ? parts[i - 1] : ""
70
+
71
+ # Strategy 1: Path in bold header above code block
72
+ # **New file: app/services/foo.rb** or **app/services/foo.rb**
73
+ path = preceding_text.match(/\*\*(?:New file:\s*|Modified:\s*)?([a-zA-Z0-9_\/\.\-]+\.rb)\*\*/i)&.dig(1)
74
+
75
+ # Strategy 2: Path as backtick-wrapped line above code block
76
+ path ||= preceding_text.match(/`([a-zA-Z0-9_\/\.\-]+\.rb)`\s*\z/)&.dig(1)
77
+
78
+ # Strategy 3: Path as comment on first line inside code block
79
+ path ||= code.lines.first&.strip&.match(/^#\s*([a-zA-Z0-9_\/\.\-]+\.rb)/)&.dig(1)
70
80
 
71
- # Fallback: no path headers found, grab first code block
72
- if blocks.empty?
73
- code_match = response.match(/```ruby\n(.*?)```/m)
74
- blocks << { tag: nil, path: nil, code: code_match[1] } if code_match
81
+ blocks << { path: path, code: code }
75
82
  end
76
83
 
77
84
  blocks
@@ -103,8 +110,9 @@ module Rubyn
103
110
  formatter.newline
104
111
  formatter.info("This refactor produces #{file_blocks.length} file(s):")
105
112
  file_blocks.each do |block|
106
- label = block[:tag] == "NEW" ? "NEW" : "MODIFIED"
107
113
  path = block[:path] || file
114
+ is_new = path != file && !path.end_with?("/#{file}") && !file.end_with?("/#{path}")
115
+ label = is_new ? "NEW" : "MODIFIED"
108
116
  formatter.info(" [#{label}] #{path}")
109
117
  end
110
118
 
@@ -129,8 +137,9 @@ module Rubyn
129
137
 
130
138
  def apply_selected_blocks(file, original, file_blocks, formatter)
131
139
  file_blocks.each do |block|
132
- label = block[:tag] == "NEW" ? "NEW" : "MODIFIED"
133
140
  path = block[:path] || file
141
+ is_new = path != file && !path.end_with?("/#{file}") && !file.end_with?("/#{path}")
142
+ label = is_new ? "NEW" : "MODIFIED"
134
143
  print " Apply [#{label}] #{path}? (y/n/diff) "
135
144
  choice = $stdin.gets&.strip&.downcase
136
145
 
@@ -138,7 +147,7 @@ module Rubyn
138
147
  when "y"
139
148
  write_block(file, block, formatter)
140
149
  when "diff"
141
- existing = block[:tag] == "NEW" ? "" : File.read(resolve_path(file, block))
150
+ existing = is_new ? "" : File.read(resolve_path(file, block))
142
151
  Rubyn::Output::DiffRenderer.render(original: existing, modified: block[:code])
143
152
  print " Apply? (y/n) "
144
153
  if $stdin.gets&.strip&.downcase == "y"
@@ -293,22 +293,24 @@
293
293
 
294
294
  // Build the file-to-code mapping
295
295
  // Headers and code blocks are in the same order — zip them together
296
+ // Determine NEW vs MODIFIED by comparing path against the original file
296
297
  var fileChanges = codeBlocks.map(function(code, i) {
297
298
  var header = fileHeaders[i];
299
+ var path = header ? header.path : file;
300
+ var isNew = path !== file && !path.endsWith("/" + file) && !file.endsWith("/" + path);
298
301
  return {
299
- path: header ? header.path : file,
300
- tag: header ? header.tag : null,
302
+ path: path,
303
+ isNew: isNew,
301
304
  code: code
302
305
  };
303
306
  });
304
307
 
305
- // Show each code block with its file path, tag, and individual apply button
308
+ // Show each code block with its file path and NEW badge if applicable
306
309
  fileChanges.forEach(function(change, i) {
307
310
  html += '<div class="rubyn-code-block">';
308
311
  html += '<div class="rubyn-code-block-header">';
309
- if (change.tag) {
310
- var tagClass = change.tag === "NEW" ? "rubyn-tag-new" : "rubyn-tag-modified";
311
- html += '<span class="rubyn-file-tag ' + tagClass + '">' + change.tag + '</span>';
312
+ if (change.isNew) {
313
+ html += '<span class="rubyn-file-tag rubyn-tag-new">NEW</span>';
312
314
  }
313
315
  html += '<span class="rubyn-tool-filepath">' + escapeHtml(change.path) + '</span>';
314
316
  html += '<div class="rubyn-code-block-actions">';
@@ -478,14 +480,34 @@
478
480
 
479
481
  function extractFileHeaders(text) {
480
482
  var headers = [];
483
+ var parts = text.split(/(```ruby\n[\s\S]*?```)/g);
481
484
 
482
- // Match path headers directly above ```ruby blocks
483
- // Handles: `[NEW] path.rb`, `path.rb`, #### path.rb, etc.
484
- var regex = /(?:\[NEW\]\s*)?`?([a-zA-Z0-9_\/\.\-]+\.rb)`?\s*\n```ruby/g;
485
- var match;
486
- while ((match = regex.exec(text)) !== null) {
487
- var isNew = match[0].indexOf("[NEW]") !== -1;
488
- headers.push({ tag: isNew ? "NEW" : null, path: match[1] });
485
+ for (var i = 0; i < parts.length; i++) {
486
+ if (parts[i].indexOf("```ruby\n") !== 0) continue;
487
+
488
+ var code = parts[i].replace(/^```ruby\n/, "").replace(/```$/, "");
489
+ var preceding = i > 0 ? parts[i - 1] : "";
490
+ var path = null;
491
+
492
+ // Strategy 1: Bold header above code block
493
+ // **New file: path.rb** or **path.rb**
494
+ var boldMatch = preceding.match(/\*\*(?:New file:\s*|Modified:\s*)?([a-zA-Z0-9_\/\.\-]+\.rb)\*\*/i);
495
+ if (boldMatch) path = boldMatch[1];
496
+
497
+ // Strategy 2: Backtick-wrapped path above code block
498
+ if (!path) {
499
+ var tickMatch = preceding.match(/`([a-zA-Z0-9_\/\.\-]+\.rb)`\s*$/);
500
+ if (tickMatch) path = tickMatch[1];
501
+ }
502
+
503
+ // Strategy 3: Path as comment on first line inside code block
504
+ if (!path) {
505
+ var firstLine = code.split("\n")[0].trim();
506
+ var commentMatch = firstLine.match(/^#\s*([a-zA-Z0-9_\/\.\-]+\.rb)/);
507
+ if (commentMatch) path = commentMatch[1];
508
+ }
509
+
510
+ headers.push({ path: path });
489
511
  }
490
512
 
491
513
  return headers;
@@ -735,6 +735,7 @@ body {
735
735
  color: var(--rubyn-success);
736
736
  }
737
737
 
738
+
738
739
  .rubyn-code-block .rubyn-tool-output {
739
740
  margin: 0;
740
741
  padding: 1rem;
data/lib/rubyn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubyn
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - matthewsuttles