immosquare-cleaner 0.1.91 → 0.1.93

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: 14f63e23fce2b146112aa531dfccf10660d1d8120bc3ed07cbf3a71a16279804
4
- data.tar.gz: 5bcb6669c4647b15cdda3413c0443ff73e73c5de5c14408cb1285280c165eb57
3
+ metadata.gz: 386d987876b864e4d445826193b67f8ea4bfdf30a292fc08f4271b2ab5eb1c06
4
+ data.tar.gz: 410548afe07047e7f7cef616f8d766a299f151ea95ee1f072b6d32ac147d3048
5
5
  SHA512:
6
- metadata.gz: dc1c03e5b3d66151c858c96ff143af1999676718294745e38e0bd1fbe16e8916c9a0fec4e61f88128015cad234cd1afc15e5208a3e13fdb427bf57f7f43bc3be
7
- data.tar.gz: 7bc5ad3ca942f8a2a3092815516354c9bebc60cc6a4fc9c3f239142613e4c01037999748222b76f1a75f84cde083f04b8be8f670c5977301741ec3ec87b0cb97
6
+ metadata.gz: 219571e479c00ae7575c079b2a504877ea4e3f85176bf0e7d44526684ef43dd68ae12e2b3b78c049a267ffa12dcf03c7925ce1786eba549c835f31cd484d1daf
7
+ data.tar.gz: 6aed55c5b70fa5116a34c4d49cac06f7aa7639b18d01806026ea0bcc2582db0b9f203fb733cd1ec431b58bc40705d3f51a14b13faa44468e7563997cd5279a0e
@@ -1,3 +1,3 @@
1
1
  module ImmosquareCleaner
2
- VERSION = "0.1.91".freeze
2
+ VERSION = "0.1.93".freeze
3
3
  end
@@ -14,7 +14,8 @@ namespace :immosquare_cleaner do
14
14
  "public",
15
15
  "test",
16
16
  "tmp",
17
- "vendor"
17
+ "vendor",
18
+ "db"
18
19
  ]
19
20
  extensions_to_exclude = [
20
21
  ".lock",
@@ -345,23 +345,170 @@ module ERBLint
345
345
 
346
346
  ##============================================================##
347
347
  ## Build content_tag call
348
+ ## Handles if/unless modifiers by moving them outside content_tag
348
349
  ##============================================================##
349
350
  def build_content_tag(tag_name, content, attributes)
351
+ ##============================================================##
352
+ ## Extract modifier (if/unless) from content if present
353
+ ##============================================================##
354
+ content_part, modifier_part = extract_modifier(content)
355
+
356
+ ##============================================================##
357
+ ## Wrap content in parentheses if it's an ambiguous method call
358
+ ## (method call without parentheses that has arguments)
359
+ ##============================================================##
360
+ wrapped_content = needs_parentheses?(content_part) ? "(#{content_part})" : content_part
361
+
362
+ ##============================================================##
363
+ ## Format tag name as symbol or string depending on characters
364
+ ##============================================================##
365
+ formatted_tag = format_tag_name(tag_name)
366
+
350
367
  if attributes.empty?
351
- "<%= content_tag(:#{tag_name}, #{content}) %>"
368
+ base = "content_tag(#{formatted_tag}, #{wrapped_content})"
352
369
  else
353
370
  attrs_str = attributes.map do |name, value|
354
371
  key = normalize_attribute_name(name)
355
- ##============================================================##
356
- ## Check if value contains interpolation
357
- ##============================================================##
358
- if value&.include?('#{')
359
- end
360
- "#{key} => \"#{value}\""
372
+ quoted_value = quote_attribute_value(value)
373
+ "#{key} => #{quoted_value}"
361
374
  end.join(", ")
362
375
 
363
- "<%= content_tag(:#{tag_name}, #{content}, #{attrs_str}) %>"
376
+ base = "content_tag(#{formatted_tag}, #{wrapped_content}, #{attrs_str})"
364
377
  end
378
+
379
+ if modifier_part
380
+ "<%= #{base} #{modifier_part} %>"
381
+ else
382
+ "<%= #{base} %>"
383
+ end
384
+ end
385
+
386
+ ##============================================================##
387
+ ## Format tag name for content_tag
388
+ ## - Simple tags (div, span) -> :div, :span
389
+ ## - Tags with special chars (x-card) -> "x-card"
390
+ ##============================================================##
391
+ def format_tag_name(tag_name)
392
+ if tag_name.match?(/\A[a-z_][a-z0-9_]*\z/i)
393
+ ":#{tag_name}"
394
+ else
395
+ "\"#{tag_name}\""
396
+ end
397
+ end
398
+
399
+ ##============================================================##
400
+ ## Check if Ruby code needs parentheses when used as argument
401
+ ## Returns true for method calls without parentheses that have
402
+ ## arguments (which would be ambiguous in content_tag context)
403
+ ## Example: "tag t('x'), path, :class => 'y'" needs parentheses
404
+ ##============================================================##
405
+ def needs_parentheses?(erb_code)
406
+ return false unless erb_code
407
+
408
+ result = Prism.parse(erb_code)
409
+ return false unless result.success?
410
+
411
+ node = result.value.statements.body.first
412
+ return false unless node.is_a?(Prism::CallNode)
413
+
414
+ ##============================================================##
415
+ ## Check if it's a method call with arguments but no parentheses
416
+ ##============================================================##
417
+ has_arguments = node.arguments&.arguments&.any?
418
+ return false unless has_arguments
419
+
420
+ ##============================================================##
421
+ ## Check if parentheses are missing by looking at the source
422
+ ## If the method name is not immediately followed by '(', it
423
+ ## needs wrapping
424
+ ##============================================================##
425
+ method_name = node.name.to_s
426
+ source = erb_code.strip
427
+
428
+ ##============================================================##
429
+ ## Find position after method name (accounting for receiver)
430
+ ##============================================================##
431
+ if node.receiver
432
+ ##============================================================##
433
+ ## For calls like "tag.div" or "f.input", find the method call
434
+ ##============================================================##
435
+ call_loc = node.call_operator_loc || node.message_loc
436
+ return false unless call_loc
437
+
438
+ after_method = call_loc.end_offset + method_name.length
439
+ else
440
+ after_method = method_name.length
441
+ end
442
+
443
+ ##============================================================##
444
+ ## Check if character after method name is '('
445
+ ##============================================================##
446
+ return false if after_method >= source.length
447
+
448
+ source[after_method] != "("
449
+ rescue StandardError
450
+ false
451
+ end
452
+
453
+ ##============================================================##
454
+ ## Quote attribute value for Ruby output
455
+ ## - Uses double quotes by default
456
+ ## - Switches to single quotes if value contains double quotes
457
+ ## - Handles interpolation by keeping double quotes and escaping
458
+ ##============================================================##
459
+ def quote_attribute_value(value)
460
+ return '""' if value.nil? || value.empty?
461
+
462
+ has_interpolation = value.include?('#{')
463
+ has_double_quotes = value.include?('"')
464
+
465
+ if has_interpolation
466
+ ##============================================================##
467
+ ## Must use double quotes for interpolation, escape inner quotes
468
+ ##============================================================##
469
+ escaped = value.gsub('"', '\\"')
470
+ "\"#{escaped}\""
471
+ elsif has_double_quotes
472
+ ##============================================================##
473
+ ## Use single quotes to avoid escaping
474
+ ##============================================================##
475
+ "'#{value}'"
476
+ else
477
+ "\"#{value}\""
478
+ end
479
+ end
480
+
481
+ ##============================================================##
482
+ ## Extract if/unless modifier from Ruby code
483
+ ## Returns [content, modifier] or [content, nil]
484
+ ## Example: "foo if bar" => ["foo", "if bar"]
485
+ ##============================================================##
486
+ def extract_modifier(erb_code)
487
+ return [erb_code, nil] unless erb_code
488
+
489
+ result = Prism.parse(erb_code)
490
+ return [erb_code, nil] unless result.success?
491
+
492
+ node = result.value.statements.body.first
493
+
494
+ ##============================================================##
495
+ ## Check for if/unless modifier
496
+ ##============================================================##
497
+ if node.is_a?(Prism::IfNode) || node.is_a?(Prism::UnlessNode)
498
+ ##============================================================##
499
+ ## Only handle modifier form (no else, single statement body)
500
+ ##============================================================##
501
+ if node.consequent.nil? && node.statements&.body&.size == 1
502
+ keyword = node.is_a?(Prism::IfNode) ? "if" : "unless"
503
+ condition = node.predicate.slice
504
+ content = node.statements.body.first.slice
505
+ return [content, "#{keyword} #{condition}"]
506
+ end
507
+ end
508
+
509
+ [erb_code, nil]
510
+ rescue StandardError
511
+ [erb_code, nil]
365
512
  end
366
513
 
367
514
  ##============================================================##
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immosquare-cleaner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.91
4
+ version: 0.1.93
5
5
  platform: ruby
6
6
  authors:
7
7
  - immosquare