plan_my_stuff 0.30.0 → 1.0.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: bf390e409623eaacc5e3dcd727984ed93212cdc23847fc9a848de150d1c025af
4
- data.tar.gz: 70eeabeadf4d593c5a97806ad013e24da2175123e4f6e5bf071b3beee9da2937
3
+ metadata.gz: fb9b79b7a7ef0890f6cfe2d4be9f5886afaaeec2a032e11fc44b0948a41c680b
4
+ data.tar.gz: 6582b1c90d2a78dd0c07e7789efba2d1938f6ce62c0a986778d0001e1c073ea5
5
5
  SHA512:
6
- metadata.gz: 928aa5abb220e785a6bb04586645af1a5bdc0c3a889f49d08ddc4882ecb0b21f5aa0bf53a724749d2458905ad58da4a116e09e31aeb59686ddeda1b16f53b9e1
7
- data.tar.gz: 66d5e0ec45da7e3b5ed2e9d2a0e1a088012008a5bca1ff5dd4773d66fd3d84ad08ffb977c29ff9276611d011b25a543464d00c0063fda29786a6610d27c6166f
6
+ metadata.gz: 9e2c0221e7396240f4b5452b000caae6c5a180b474250c2dedebb8e4a899e5c7c6eccad178a242858f1e5a2f04a37c778c23b4a7f973198db59e4c8d8aa04ea6
7
+ data.tar.gz: c91d0a3aec30798acff56582232baa332c1e556a98e78844ea7c05ff82abde81e65e953c56a6ac3c248be33c789030963b7918fe1fc5a1286ab5d40fe5196837
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.1
4
+
5
+ ### Fixed
6
+
7
+ - `eager_load_controllers_on_boot` no longer crashes host-app boot with `NameError: uninitialized constant
8
+ PlanMyStuff::Issues` when `config.eager_load` is `false` (dev/test/console/rake) on zeitwerk 2.6.x, as shipped with
9
+ Rails 7.1. The engine now requires its controller files directly instead of driving zeitwerk's `eager_load_dir`,
10
+ which fails to register child autoloads on the gem-defined `PlanMyStuff` namespace on those versions (closes #104).
11
+
12
+ ## 1.0.0
13
+
14
+ Official release.
15
+
16
+ ### Breaking
17
+
18
+ - `MetadataParser.parse` no longer recognizes the legacy `<!-- pms-metadata:... -->` HTML-comment format.
19
+ Bodies written before 0.17.0 that have not been re-saved will now be returned as plain body text with
20
+ empty metadata. The deprecation warning emitted by 0.17.0+ has been removed along with the legacy
21
+ pattern.
22
+ - Removed the deprecated `IssueMetadata#priority_list`, `#priority_list=`, `#priority_list?`,
23
+ `#priority_list_priority`, and `#priority_list_priority=` accessors, along with the legacy-write forwarding
24
+ that turned `metadata.priority_list = true` into `Priority List` / `Priority List Priority` issue-field writes
25
+ on `save!` / `update!` / `create!`. Use `Issue#priority_list?` / `#priority_list_priority` for reads and
26
+ `Issue#add_to_priority_list!(priority: N)` / `#remove_from_priority_list!` for writes (closes #57).
27
+ - `CommentMetadata` no longer parses the legacy `{filename, url}` attachment shape into structured fields.
28
+ Entries must now carry the `{filename, owner, repo, sha, path}` shape; legacy entries that were never
29
+ re-saved are dropped as malformed. The `LEGACY_URL_REGEXES` / `LEGACY_ATTACHMENT_DEPRECATION_MESSAGE`
30
+ constants and the migrate-on-write deprecation warning have been removed.
31
+
3
32
  ## 0.30.0
4
33
 
5
34
  ### Added
data/README.md CHANGED
@@ -612,12 +612,20 @@ Cache keys include both the gem's `CACHE_VERSION` ("v1") and `config.cache_versi
612
612
 
613
613
  ## Metadata
614
614
 
615
- All state lives on GitHub. Each domain class carries a typed metadata object hidden in the corresponding GitHub body (issue/comment body, project readme) as an HTML comment that's invisible when rendered on github.com:
615
+ All state lives on GitHub. Each domain class carries a typed metadata object embedded in the corresponding GitHub
616
+ body (issue/comment body, project readme) as a collapsible `<details>` block containing a JSON code fence, which
617
+ GitHub renders in-page:
616
618
 
617
- ```html
618
- <!-- pms-metadata:{"schema_version":1,"gem_version":"0.0.0","app_name":"MyApp",...} -->
619
+ ````markdown
620
+ <details><summary>pms-metadata</summary>
621
+
622
+ ```json
623
+ {"schema_version":1,"gem_version":"0.0.0","app_name":"MyApp", ...}
619
624
  ```
620
625
 
626
+ </details>
627
+ ````
628
+
621
629
  `MetadataParser` strips the block out of the raw body on read and re-inserts it on write — callers see `issue.body` (human text) and `issue.metadata` (typed object) separately.
622
630
 
623
631
  ### Metadata classes
@@ -2,17 +2,6 @@
2
2
 
3
3
  module PlanMyStuff
4
4
  class CommentMetadata < PlanMyStuff::BaseMetadata
5
- LEGACY_URL_REGEXES = [
6
- %r{\Ahttps://raw\.githubusercontent\.com/(?<owner>[^/\s]+)/(?<repo>[^/\s]+)/(?<sha>[^/\s]+)/(?<path>.+)\z},
7
- %r{\Ahttps://github\.com/(?<owner>[^/\s]+)/(?<repo>[^/\s]+)/blob/(?<sha>[^/\s]+)/(?<path>.+)\z},
8
- ].freeze
9
-
10
- LEGACY_ATTACHMENT_DEPRECATION_MESSAGE =
11
- 'PlanMyStuff: legacy attachment metadata shape {filename, url} detected. It will continue to parse ' \
12
- 'until 1.0.0, at which point legacy detection will be removed. New writes use the structured ' \
13
- '{filename, owner, repo, sha, path} shape introduced in #70; existing entries migrate on their ' \
14
- 'next write.'
15
-
16
5
  # @return [Boolean] true if this comment holds the issue's body content
17
6
  attr_accessor :issue_body
18
7
  # @return [Array<PlanMyStuff::Attachment>] consuming-app attachment records associated with this comment
@@ -95,11 +84,7 @@ module PlanMyStuff
95
84
  # Builds a +PlanMyStuff::Attachment+ from each parsed entry.
96
85
  # Accepts:
97
86
  # - an existing +PlanMyStuff::Attachment+
98
- # - the new shape +{filename:, owner:, repo:, sha:, path:}+
99
- # - the legacy shape +{filename:, url:}+ where +url+ is a raw
100
- # +raw.githubusercontent.com+ permalink or a +github.com/.../blob/...+
101
- # blob-viewer URL; parsed once into structured fields so the
102
- # next write drops the +url+ key (migrate-on-write).
87
+ # - the structured shape +{filename:, owner:, repo:, sha:, path:}+
103
88
  #
104
89
  # Malformed hash entries are silently dropped so a single bad
105
90
  # historical entry doesn't crash +Comment.find+. An already-
@@ -118,45 +103,17 @@ module PlanMyStuff
118
103
  if entry.is_a?(PlanMyStuff::Attachment)
119
104
  entry
120
105
  elsif entry.respond_to?(:transform_keys)
121
- PlanMyStuff::Attachment.new(structured_attrs(entry.transform_keys(&:to_sym)))
106
+ PlanMyStuff::Attachment.new(entry.transform_keys(&:to_sym))
122
107
  end
123
108
  next if attachment.nil?
124
109
 
125
110
  attachment.validate!
126
111
  attachment
127
- rescue ActiveModel::ValidationError, ArgumentError
112
+ rescue ActiveModel::ValidationError, ActiveModel::UnknownAttributeError, ArgumentError
128
113
  raise if entry.is_a?(PlanMyStuff::Attachment)
129
114
 
130
115
  next
131
116
  end
132
117
  end
133
-
134
- # Returns +attrs+ untouched when it already carries structured
135
- # fields, otherwise parses a legacy +url+ into
136
- # +owner+/+repo+/+sha+/+path+. Unparseable URLs yield a hash
137
- # with no structured fields, leaving +Attachment+ validation to
138
- # drop the entry.
139
- #
140
- # @param attrs [Hash{Symbol=>Object}]
141
- #
142
- # @return [Hash{Symbol=>Object}]
143
- #
144
- def structured_attrs(attrs)
145
- return attrs if attrs.key?(:owner)
146
-
147
- PlanMyStuff.deprecator.warn(LEGACY_ATTACHMENT_DEPRECATION_MESSAGE)
148
-
149
- url = attrs[:url].to_s
150
- match = LEGACY_URL_REGEXES.lazy.filter_map { |re| re.match(url) }.first
151
-
152
- return attrs.except(:url) if match.nil?
153
-
154
- attrs.except(:url).merge(
155
- owner: match[:owner],
156
- repo: match[:repo],
157
- sha: match[:sha],
158
- path: match[:path],
159
- )
160
- end
161
118
  end
162
119
  end
@@ -10,14 +10,15 @@ module PlanMyStuff
10
10
  config.after_initialize do
11
11
  next unless PlanMyStuff.configuration.eager_load_controllers_on_boot
12
12
 
13
+ # Require the controller files directly rather than going through zeitwerk's +eager_load_dir+ /
14
+ # +eager_load_namespace+. +PlanMyStuff+ is an external namespace pre-defined by the gem's +lib/+, and some
15
+ # zeitwerk versions (notably 2.6.12, shipped with Rails 7.1) never register child autoloads on it. On those
16
+ # versions the namespace-aware loaders raise +NameError+ descending into +PlanMyStuff::Issues+ / +::Webhooks+
17
+ # -- and so does a plain +constantize+. Requiring the files defines the constants directly and works on every
18
+ # supported zeitwerk version: zeitwerk's +require+ decoration keeps its bookkeeping in sync on versions that do
19
+ # manage these files, and in eager-loading hosts the files are already loaded so +require+ is a no-op.
13
20
  controllers_dir = File.expand_path('../../app/controllers', __dir__)
14
- loader = Rails.autoloaders.main
15
-
16
- if loader.respond_to?(:eager_load_dir)
17
- loader.eager_load_dir(controllers_dir)
18
- else
19
- Dir.glob(File.join(controllers_dir, '**/*.rb')).each { |path| require path }
20
- end
21
+ Dir.glob(File.join(controllers_dir, '**/*.rb')).each { |path| require(path) }
21
22
  end
22
23
  end
23
24
  end
@@ -901,8 +901,6 @@ module PlanMyStuff
901
901
  # @return [self]
902
902
  #
903
903
  def save!(user: nil, skip_notification: false)
904
- forward_legacy_priority_list_metadata!
905
-
906
904
  if new_record?
907
905
  created = self.class.create!(
908
906
  title: title,
@@ -1243,28 +1241,6 @@ module PlanMyStuff
1243
1241
  reload
1244
1242
  end
1245
1243
 
1246
- # Forwards a pending legacy +metadata.priority_list+ /
1247
- # +#priority_list_priority+ write into +@pending_issue_fields+ so the next
1248
- # +save!+ persists the values into the +Priority List+ /
1249
- # +Priority List Priority+ GitHub Issue Fields. Caller-supplied
1250
- # +issue_fields:+ entries win on key collision. Silently skipped when
1251
- # +config.issue_fields_enabled+ is +false+.
1252
- #
1253
- # @return [void]
1254
- #
1255
- def forward_legacy_priority_list_metadata!
1256
- return unless PlanMyStuff.configuration.issue_fields_enabled
1257
- return unless metadata.instance_variable_get(:@legacy_priority_list_pending)
1258
-
1259
- legacy_pl = metadata.instance_variable_get(:@priority_list)
1260
- legacy_plp = metadata.instance_variable_get(:@priority_list_priority)
1261
-
1262
- legacy_fields = { 'Priority List' => legacy_pl ? 'Yes' : nil }
1263
- legacy_fields['Priority List Priority'] = legacy_plp unless legacy_plp == -1
1264
-
1265
- @pending_issue_fields = legacy_fields.merge(@pending_issue_fields || {})
1266
- end
1267
-
1268
1244
  # Applies in-memory updates from an +update!+ kwargs hash. Top-level scalars go through their setters so
1269
1245
  # +@body_dirty+ and friends stay in sync; +metadata:+ is merged into +@metadata+ (top-level attrs assigned
1270
1246
  # directly, custom_fields merged key-by-key).
@@ -2,13 +2,6 @@
2
2
 
3
3
  module PlanMyStuff
4
4
  class IssueMetadata < PlanMyStuff::BaseMetadata
5
- PRIORITY_LIST_METADATA_DEPRECATION =
6
- 'PlanMyStuff: IssueMetadata#priority_list / #priority_list_priority are deprecated. priority_list moved to ' \
7
- "GitHub Issue Fields ('Priority List' single_select, 'Priority List Priority' number) in 0.20.0. Reads " \
8
- 'now come from Issue#priority_list? / #priority_list_priority; for writes, use ' \
9
- 'Issue#add_to_priority_list!(priority: N) and Issue#remove_from_priority_list!. The metadata accessors ' \
10
- 'will be removed in 1.0.0.'
11
-
12
5
  # @return [Time, nil] first support action timestamp, nil until set
13
6
  attr_accessor :responded_at
14
7
  # @return [String, nil] user-facing URL in the consuming app
@@ -53,14 +46,6 @@ module PlanMyStuff
53
46
 
54
47
  metadata.responded_at = parse_time(hash[:responded_at])
55
48
  metadata.issues_url = hash[:issues_url]
56
- if hash.key?(:priority_list) || hash.key?(:priority_list_priority)
57
- PlanMyStuff.deprecator.warn(PRIORITY_LIST_METADATA_DEPRECATION)
58
- metadata.instance_variable_set(:@priority_list, hash[:priority_list]) if hash.key?(:priority_list)
59
- if hash.key?(:priority_list_priority)
60
- metadata.instance_variable_set(:@priority_list_priority, hash[:priority_list_priority])
61
- end
62
- metadata.instance_variable_set(:@legacy_priority_list_pending, true)
63
- end
64
49
  metadata.visibility_allowlist = Array.wrap(hash[:visibility_allowlist])
65
50
  metadata.commit_sha = hash[:commit_sha]
66
51
  metadata.auto_complete = hash.fetch(:auto_complete, true)
@@ -193,57 +178,6 @@ module PlanMyStuff
193
178
  !!auto_complete
194
179
  end
195
180
 
196
- # @deprecated Use +Issue#priority_list?+. Removed in 1.0.0.
197
- #
198
- # @return [Object, nil]
199
- #
200
- def priority_list
201
- PlanMyStuff.deprecator.warn(PRIORITY_LIST_METADATA_DEPRECATION)
202
- @priority_list
203
- end
204
-
205
- # @deprecated Use +Issue#add_to_priority_list!(priority: N)+ / +Issue#remove_from_priority_list!+. Removed in 1.0.0.
206
- #
207
- # @param value [Object]
208
- #
209
- # @return [Object]
210
- #
211
- def priority_list=(value)
212
- PlanMyStuff.deprecator.warn(PRIORITY_LIST_METADATA_DEPRECATION)
213
- @legacy_priority_list_pending = true
214
- @priority_list = value
215
- end
216
-
217
- # @deprecated Use +Issue#priority_list?+. Removed in 1.0.0.
218
- #
219
- # @return [Boolean]
220
- #
221
- def priority_list?
222
- PlanMyStuff.deprecator.warn(PRIORITY_LIST_METADATA_DEPRECATION)
223
- !!@priority_list
224
- end
225
-
226
- # @deprecated Use +Issue#priority_list_priority+. Removed in 1.0.0.
227
- #
228
- # @return [Object, nil]
229
- #
230
- def priority_list_priority
231
- PlanMyStuff.deprecator.warn(PRIORITY_LIST_METADATA_DEPRECATION)
232
- @priority_list_priority
233
- end
234
-
235
- # @deprecated Use +Issue#add_to_priority_list!(priority: N)+ / +Issue#remove_from_priority_list!+. Removed in 1.0.0.
236
- #
237
- # @param value [Object]
238
- #
239
- # @return [Object]
240
- #
241
- def priority_list_priority=(value)
242
- PlanMyStuff.deprecator.warn(PRIORITY_LIST_METADATA_DEPRECATION)
243
- @legacy_priority_list_pending = true
244
- @priority_list_priority = value
245
- end
246
-
247
181
  # @return [Boolean]
248
182
  def responded?
249
183
  !responded_at.nil?
@@ -1,17 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/deprecation'
4
3
  require 'json'
5
4
 
6
5
  module PlanMyStuff
7
6
  module MetadataParser
8
- LEGACY_DEPRECATION_MESSAGE =
9
- 'PlanMyStuff: legacy <!-- pms-metadata: ... --> body format detected. It will continue to parse ' \
10
- 'until 1.0.0, at which point legacy detection will be removed. New writes already use the visible ' \
11
- '<details> block format introduced in 0.17.0; existing bodies migrate on their next write.'
12
-
13
- # New format: collapsible <details> block containing a JSON code fence.
14
- # Renders visibly on GitHub (issue #58) instead of being hidden in an HTML comment.
7
+ # Collapsible <details> block containing a JSON code fence. Renders visibly on
8
+ # GitHub (issue #58) instead of being hidden in an HTML comment.
15
9
  METADATA_PATTERN = %r{
16
10
  \A<details><summary>pms-metadata</summary>\n\n
17
11
  ```json\n(.*?)\n```\n\n
@@ -27,10 +21,6 @@ module PlanMyStuff
27
21
  </details>\n*
28
22
  }mx
29
23
 
30
- # Legacy format kept for parsing only - existing issues serialized before 0.17.0
31
- # used a hidden HTML comment. They migrate to the new format on the next write.
32
- LEGACY_METADATA_PATTERN = /\A<!-- pms-metadata:(.*?) -->\n*/m
33
-
34
24
  module_function
35
25
 
36
26
  # Extracts metadata JSON from the raw body
@@ -41,15 +31,11 @@ module PlanMyStuff
41
31
  #
42
32
  def parse(raw_body)
43
33
  return { metadata: {}, body: '' } if raw_body.blank?
34
+ return { metadata: {}, body: raw_body } unless raw_body.match?(METADATA_PATTERN)
44
35
 
45
- pattern = matching_pattern(raw_body)
46
- return { metadata: {}, body: raw_body } if pattern.nil?
47
-
48
- PlanMyStuff.deprecator.warn(LEGACY_DEPRECATION_MESSAGE) if pattern == LEGACY_METADATA_PATTERN
49
-
50
- match = raw_body.match(pattern)
36
+ match = raw_body.match(METADATA_PATTERN)
51
37
  metadata = JSON.parse(match[1], symbolize_names: true)
52
- body = raw_body.sub(pattern, '').sub(ATTACHMENTS_PATTERN, '')
38
+ body = raw_body.sub(METADATA_PATTERN, '').sub(ATTACHMENTS_PATTERN, '')
53
39
 
54
40
  { metadata: metadata, body: body }
55
41
  rescue JSON::ParserError
@@ -102,14 +88,5 @@ module PlanMyStuff
102
88
  safe_filename = attachment[:filename].to_s.gsub(']', '\]')
103
89
  "- [#{safe_filename}](#{url})"
104
90
  end
105
-
106
- # @param raw_body [String]
107
- #
108
- # @return [Regexp, nil] the pattern that matches at the start of +raw_body+, or +nil+ when neither matches
109
- #
110
- def matching_pattern(raw_body)
111
- return METADATA_PATTERN if raw_body.match?(METADATA_PATTERN)
112
- return LEGACY_METADATA_PATTERN if raw_body.match?(LEGACY_METADATA_PATTERN)
113
- end
114
91
  end
115
92
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module PlanMyStuff
4
4
  module VERSION
5
- MAJOR = 0
6
- MINOR = 30
7
- TINY = 0
5
+ MAJOR = 1
6
+ MINOR = 0
7
+ TINY = 1
8
8
 
9
9
  # Set PRE to nil unless it's a pre-release (beta, rc, etc.)
10
10
  PRE = nil
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plan_my_stuff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brands Insurance
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-29 00:00:00.000000000 Z
11
+ date: 2026-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails