poetry-core 0.1.2 → 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: 68f26084516bebd43571b5f687d9fb07cce952f00aba8e41448715b66b996fba
4
- data.tar.gz: d1ec44154a85d4c2b4fda384432f2e73349973dfc21862aeddf06bde0d09303b
3
+ metadata.gz: 74940c713a7f81772d930f9581313847ec1a4044f36c3b31774efafd31bd07ef
4
+ data.tar.gz: 9e869c5574bef206ee03a998d31beda8cc10cf6f70e429134fb80b70fec8e2be
5
5
  SHA512:
6
- metadata.gz: 65965b0865ade83c8b778c3ba773c6d1272bd4da2139e68cd8ab5b798903d12e3224d75df0f77f849cd72403e782d5da87ba470a4597d8654c7ab72a6bdc90ea
7
- data.tar.gz: e3b822469b4ed1253193dd331b9b43b7923aa5626265ec9f6d1cdc6c4112a7306eb93e37f3a7670b9985da406ef804b5eb4b5174b03263004eced90e306e03c3
6
+ metadata.gz: 6bfcc4296038b8766014f1ed52cda1e9c365a9242aabcc0f8bac7a864e63c64a01438ec58d46c48ed1525d0191f7d1e0e9b4826932f996cb0184de123a19f856
7
+ data.tar.gz: c5c56a37ae235c3d88b155a9197138c9f583187814431d957f78efb430c8e925a86fc039111df305fec8d00b90e0b4cfe46eb897eca3415e8bc97b7d0182b670
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.3] - 2026-09-13
4
+
5
+ ### Fixed
6
+
7
+ - `poetry check` no longer flags `with_trigger(compose: true) do |wiring|` (any composed slot) as a yieldless block: `compose: true` is the slot's other contract, and the setter yields the wiring for the caller's own control.
8
+ - The app's own `poetry_*` helper methods are valid names for `poetry check` and the MCP `check` tool: `Poetry::Core::HostComponents.helper_methods` reads `def poetry_*` from `app/helpers` boot-free (the adapter `poetry:pagination` copies in, a wrapper the host wrote), and such a call is linted for its wiring only, since no registry describes its options or its block. A registry helper of the same name keeps the registry's contract.
9
+
3
10
  ## [0.1.2] - 2026-09-13
4
11
 
5
12
  ### Added
@@ -71,8 +71,8 @@ module Poetry
71
71
  # rgb(...)) - a url(#fragment) is not a color.
72
72
  STYLE_COLOR = /(?<!url\()#\h{3,8}\b|\b(?:#{COLOR_FUNCTIONS.join("|")})\([^)]*\)/i
73
73
 
74
- def self.from_registry(root, helpers: nil, icon_names: nil)
75
- from_registries([root], helpers: helpers, icon_names: icon_names)
74
+ def self.from_registry(root, helpers: nil, icon_names: nil, host_helpers: nil)
75
+ from_registries([root], helpers: helpers, icon_names: icon_names, host_helpers: host_helpers)
76
76
  end
77
77
 
78
78
  # A host catalog spans every installed poetry gem (ui + charts): a
@@ -82,7 +82,7 @@ module Poetry
82
82
  # host_registry: the app's own components (HostComponents.registry),
83
83
  # merged in beside the gem registries - their declared helpers join
84
84
  # the valid set and their contracts are checked like any other.
85
- def self.from_registries(roots, helpers: nil, icon_names: nil, host_registry: nil)
85
+ def self.from_registries(roots, helpers: nil, icon_names: nil, host_registry: nil, host_helpers: nil)
86
86
  payloads = roots.map do |root|
87
87
  YAML.load_file(Pathname.new(root).join(Registry::RELATIVE_PATH), aliases: true)
88
88
  end
@@ -96,7 +96,8 @@ module Poetry
96
96
  helpers: helpers,
97
97
  helper_entries: payloads.map { |payload| payload["helpers"] || {} }.reduce({}, :merge),
98
98
  icon_names: icon_names,
99
- helper_args: helper_args)
99
+ helper_args: helper_args,
100
+ host_helpers: host_helpers)
100
101
  end
101
102
 
102
103
  # helpers: the FULL set of valid poetry_* helper method names (from
@@ -110,11 +111,16 @@ module Poetry
110
111
  # helpers (poetry_input_group_addon align: et al). icon_names: the
111
112
  # active icon set's valid names - when given, icon-formatted option
112
113
  # values are checked for membership, not just shape.
113
- def initialize(components, helpers: nil, helper_entries: nil, icon_names: nil, helper_args: nil)
114
+ def initialize(components, helpers: nil, helper_entries: nil, icon_names: nil, helper_args: nil,
115
+ host_helpers: nil)
114
116
  @components = components
115
117
  @helper_entries = helper_entries || {}
116
118
  @helper_args = helper_args || {}
117
119
  @icon_names = icon_names&.to_set(&:to_s)
120
+ # The app's own poetry_* helper METHODS (HostComponents.helper_methods:
121
+ # a pagination adapter, a wrapper of the host's own) - valid by
122
+ # name, contracts their own.
123
+ @host_helpers = (host_helpers || []).to_set(&:to_s)
118
124
  # helper name -> registry path: poetry_ + the path under the gem
119
125
  # namespace (poetry/ui/command/dialog -> poetry_command_dialog,
120
126
  # avoiding the last-segment collision with poetry/ui/dialog). The
@@ -131,6 +137,7 @@ module Poetry
131
137
  end.to_h
132
138
  @helper_by_path = @path_by_helper.invert
133
139
  @helper_names = ((helpers&.map(&:to_s) || @path_by_helper.keys) + @helper_entries.keys).to_set
140
+ @helper_names.merge(@host_helpers)
134
141
  end
135
142
 
136
143
  attr_reader :icon_names
@@ -138,6 +145,15 @@ module Poetry
138
145
  def helper_names = @helper_names.to_a
139
146
 
140
147
  def helper?(name) = @helper_names.include?(name)
148
+
149
+ # A helper method the app defines under the prefix and no registry
150
+ # describes: the name is valid, its contract is the app's own. A
151
+ # registry entry of the same name (a gem helper the app redefines)
152
+ # keeps the registry's contract.
153
+ def host_helper?(name)
154
+ @host_helpers.include?(name) && !@path_by_helper.key?(name) && !@helper_entries.key?(name)
155
+ end
156
+
141
157
  def path_for(helper) = @path_by_helper[helper]
142
158
  # The helper that renders a registry path (the declared name, or the
143
159
  # poetry_ convention).
@@ -428,6 +444,14 @@ module Poetry
428
444
  message: "no poetry component #{helper}", line: line, suggestion: suggestion)]
429
445
  end
430
446
 
447
+ # A helper method of the app's own under the prefix (a pagination
448
+ # adapter poetry:pagination copied in, a wrapper the host wrote):
449
+ # no registry knows its options or whether it yields, so only the
450
+ # wiring rules apply.
451
+ if @catalog.host_helper?(helper)
452
+ return passthrough_findings(keyword_pairs(call), base_line) + data_findings(call, base_line, helper)
453
+ end
454
+
431
455
  # A valid helper with no component mapping (group / provider / item
432
456
  # wrapper): its registry-declared value contracts still check, and
433
457
  # NO wrapper yields anything to its block (a roster invariant) - a
@@ -854,7 +878,7 @@ module Poetry
854
878
  # WITHOUT a block (Carousel with_item).
855
879
  def setter_block_findings(entry, slot_name, call, line)
856
880
  findings = []
857
- if (entry["yieldless"] || []).include?(slot_name) && block_param_name(call)
881
+ if (entry["yieldless"] || []).include?(slot_name) && !composed?(call) && block_param_name(call)
858
882
  findings << Finding.new(rule: "yieldless-block", severity: :error,
859
883
  message: "with_#{slot_name} yields nothing to its block - the param " \
860
884
  "will be nil; remove it and write the content directly",
@@ -924,6 +948,19 @@ module Poetry
924
948
  end
925
949
  end
926
950
 
951
+ # A setter called with `compose: true` is the slot's other
952
+ # contract: it yields the wiring for the caller's own control, so
953
+ # the block param is the point, not a mistake.
954
+ def composed?(call)
955
+ hash = call.arguments&.arguments&.find { |argument| argument.is_a?(Prism::KeywordHashNode) }
956
+ return false unless hash
957
+
958
+ hash.elements.any? do |element|
959
+ element.is_a?(Prism::AssocNode) && element.key.respond_to?(:unescaped) &&
960
+ element.key.unescaped == "compose" && element.value.is_a?(Prism::TrueNode)
961
+ end
962
+ end
963
+
927
964
  # [key_string, literal_value_or_nil, line] per keyword argument.
928
965
  def keyword_pairs(call)
929
966
  hash = call.arguments&.arguments&.find { |argument| argument.is_a?(Prism::KeywordHashNode) }
@@ -127,6 +127,40 @@ module Poetry
127
127
  end.uniq.sort
128
128
  end
129
129
 
130
+ # The app's own `poetry_*` view helper METHODS, boot-free: a source
131
+ # scan of app/helpers for instance-method definitions under the
132
+ # prefix (the adapter `poetry:pagination` copies in, a wrapper the
133
+ # host wrote). They join `poetry check`'s valid names - the MCP
134
+ # server's check tool and `bin/rails poetry:check` alike - with
135
+ # contracts of their own, which no registry describes.
136
+ #
137
+ # @param root [String, Pathname] the app root
138
+ # @return [Array<String>] sorted, unique
139
+ def helper_methods(root:)
140
+ Dir.glob(Pathname.new(root).join("app/helpers/**/*.rb").to_s).flat_map do |file|
141
+ result = Prism.parse(File.read(file))
142
+ result.success? ? HelperMethods.new.tap { |finder| result.value.accept(finder) }.names : []
143
+ end.uniq.sort
144
+ end
145
+
146
+ # The `def poetry_*` instance methods, from the syntax tree - a
147
+ # singleton method, a mention in a string or a comment is not one.
148
+ class HelperMethods < Prism::Visitor
149
+ PREFIX = "poetry_"
150
+
151
+ attr_reader :names
152
+
153
+ def initialize
154
+ super
155
+ @names = []
156
+ end
157
+
158
+ def visit_def_node(node)
159
+ @names << node.name.to_s if node.receiver.nil? && node.name.start_with?(PREFIX)
160
+ super
161
+ end
162
+ end
163
+
130
164
  # The `helper :name` / `helper("name")` calls in class bodies, from
131
165
  # the syntax tree - a mention inside a string, a heredoc or a comment
132
166
  # is not a declaration.
@@ -4,6 +4,6 @@ module Poetry
4
4
  module Core
5
5
  # The gem version. Every gem in the family carries the same version
6
6
  # and pins poetry-core to it exactly.
7
- VERSION = "0.1.2"
7
+ VERSION = "0.1.3"
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poetry-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Solt