view_component_devtools 0.1.1 → 0.1.3

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: 133887ab12a90a7f356d8ab4c77475c53ac65e9280769060c1a02f6e9df6043e
4
- data.tar.gz: fcf6a866579b6a27b71d40ac3c57d8453b76907e9520fb5b74cc03260985820b
3
+ metadata.gz: bf5b2619905340a6dd2b5e8bc564172c9a49441a6353d48e36619585abb190b3
4
+ data.tar.gz: c5fc0d7882588e18739448c3cc147d3a10a4e0c93fdad4c0a8a5406fe65958b4
5
5
  SHA512:
6
- metadata.gz: 9e79c04dd30cdcadb4c80a1dbf4f53410d17a95f2fdcffe4c27e03e98cd718705e89ee56de1f8c82a8a65e784cae21b86b7a0dacdb07dc4e8f95d99a2cdbae09
7
- data.tar.gz: cbb037a59de31c4d41fb7017d2231f1e58660031f1a8f6c422eda105cf7e00afb5174d5dd7d7df0810510875d704b64db28d925f001c9ce56ee8c3f238e44c97
6
+ metadata.gz: cd02547ecaca8df7483794ee934f9b7374821fd10df047028e9739d43277207c7b1aa3cb10b92f5ae5da91f43497b84f232d61669042a30d6d2fe1bdb08288a0
7
+ data.tar.gz: d3f7f7a4000c3fe7527bdd87de585806d4e8fb559d1e3970e9900d884495e817a4d537fe98b3a740d9387effcf9d1ac558a93c25182bf1cb70728009a1e64a8a
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/output_safety"
4
+ require "base64"
5
+
3
6
  module ViewComponentDevtools
4
7
  module Component
8
+ MARKER_PREFIX = "view-component-devtools"
9
+
5
10
  def self.included(base)
6
11
  base.extend(ClassMethods)
7
12
  end
@@ -13,54 +18,66 @@ module ViewComponentDevtools
13
18
  end
14
19
 
15
20
  def view_component_devtools_prop_names
16
- classes = component_class_chain
17
- declared = classes.flat_map do |component_class|
18
- component_class.instance_variable_get(:@view_component_devtools_props) || []
19
- end
20
- inferred = classes.flat_map do |component_class|
21
- component_initializer(component_class)&.parameters&.filter_map do |kind, name|
22
- name if %i[req opt keyreq key].include?(kind)
23
- end || []
21
+ declared = (@view_component_devtools_props || []).select do |name|
22
+ directly_defined_zero_argument_method?(name)
24
23
  end
24
+ inferred = component_initializer&.parameters&.filter_map do |kind, name|
25
+ name if %i[req opt keyreq key].include?(kind) && directly_defined_attribute_reader?(name)
26
+ end || []
25
27
 
26
- (declared + inferred).uniq.select do |name|
27
- prop_reader?(name, classes:, explicitly_declared: declared.include?(name))
28
- end
28
+ (declared + inferred).uniq
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def component_class_chain
34
- boundary = defined?(ViewComponent::Base) && self < ViewComponent::Base ?
35
- ViewComponent::Base : Object
36
- classes = []
37
- component_class = self
33
+ def attr(*names)
34
+ result = super
35
+ reader_names = names.last == true || names.last == false ? names[0...-1] : names
36
+ track_attribute_readers(reader_names)
37
+ result
38
+ end
38
39
 
39
- while component_class.is_a?(Class) && component_class != boundary
40
- classes << component_class
41
- component_class = component_class.superclass
42
- end
40
+ def attr_accessor(*names)
41
+ result = super
42
+ track_attribute_readers(names)
43
+ result
44
+ end
43
45
 
44
- classes
46
+ def attr_reader(*names)
47
+ result = super
48
+ track_attribute_readers(names)
49
+ result
45
50
  end
46
51
 
47
- def component_initializer(component_class)
48
- return unless component_class.private_instance_methods(false).include?(:initialize) ||
49
- component_class.protected_instance_methods(false).include?(:initialize) ||
50
- component_class.public_instance_methods(false).include?(:initialize)
52
+ def component_initializer
53
+ return unless private_instance_methods(false).include?(:initialize) ||
54
+ protected_instance_methods(false).include?(:initialize) ||
55
+ public_instance_methods(false).include?(:initialize)
51
56
 
52
- initializer = component_class.instance_method(:initialize)
53
- initializer if initializer.owner == component_class
57
+ instance_method(:initialize)
54
58
  end
55
59
 
56
- def prop_reader?(name, classes:, explicitly_declared:)
57
- return false unless public_method_defined?(name)
60
+ def directly_defined_attribute_reader?(name)
61
+ reader = (@view_component_devtools_attribute_readers || {})[name]
62
+ return false unless reader
63
+ return false unless directly_defined_zero_argument_method?(name)
64
+
65
+ instance_method(name) == reader
66
+ end
67
+
68
+ def directly_defined_zero_argument_method?(name)
69
+ return false unless public_instance_methods(false).include?(name)
58
70
 
59
71
  method = instance_method(name)
60
- callable_without_arguments = method.parameters.none? do |kind, _|
61
- %i[req keyreq].include?(kind)
72
+ method.owner == self && method.arity.zero?
73
+ end
74
+
75
+ def track_attribute_readers(names)
76
+ @view_component_devtools_attribute_readers ||= {}
77
+ names.each do |name|
78
+ name = name.to_sym
79
+ @view_component_devtools_attribute_readers[name] = instance_method(name)
62
80
  end
63
- callable_without_arguments && (explicitly_declared || classes.include?(method.owner))
64
81
  end
65
82
  end
66
83
 
@@ -75,7 +92,15 @@ module ViewComponentDevtools
75
92
  end
76
93
  }
77
94
 
78
- State.with_component(metadata) { super }
95
+ rendered = State.with_component(metadata) { super }
96
+ component_id = metadata[:devtools_id]
97
+ return rendered unless component_id
98
+
99
+ marker_id = Base64.strict_encode64(component_id)
100
+ start_marker = "<!--#{MARKER_PREFIX}:start:#{marker_id}-->".html_safe
101
+ end_marker = "<!--#{MARKER_PREFIX}:end:#{marker_id}-->".html_safe
102
+
103
+ start_marker + rendered.to_s.html_safe + end_marker
79
104
  end
80
105
 
81
106
  private
@@ -86,9 +111,7 @@ module ViewComponentDevtools
86
111
  return identifier if identifier
87
112
  end
88
113
 
89
- initializer = self.class.send(:component_class_chain).filter_map do |component_class|
90
- self.class.send(:component_initializer, component_class)
91
- end.first
114
+ initializer = self.class.send(:component_initializer)
92
115
  initializer&.source_location&.join(":")
93
116
  end
94
117
  end
@@ -106,8 +106,11 @@ module ViewComponentDevtools
106
106
  @component_count += 1
107
107
  metadata = component_stack.last
108
108
  metadata = nil unless metadata&.fetch(:name, nil) == payload[:name]
109
+ id = "#{request_id}:#{@component_count}"
110
+ metadata[:devtools_id] = id if metadata
111
+
109
112
  {
110
- id: "#{request_id}:#{@component_count}",
113
+ id:,
111
114
  name: payload[:name] || metadata&.fetch(:name, nil) || "AnonymousComponent",
112
115
  sourceLocation: payload[:identifier] || metadata&.fetch(:source_location, nil),
113
116
  templateLocation: payload[:view_identifier],
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ViewComponentDevtools
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component_devtools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent DevTools contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-08-11 00:00:00.000000000 Z
10
+ date: 2026-08-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport