belt 0.4.2 → 0.4.4

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: e17e0755e497e26d8bd10604e6f050b0c6e0b69e3627276f8d83be984862d5da
4
- data.tar.gz: b4249c4ac11715ae45a091c3eced0d0bad8ecd5d89a750edfaa8586ab6449758
3
+ metadata.gz: 8a4efb62a37845ad3c311558cebdd71fcf74ba7ae31870f3c2728e286dc33a72
4
+ data.tar.gz: 8278be1ba1c44b6d355d00963ca1d33c401749b0af584a4cb5c07f6ae25bca54
5
5
  SHA512:
6
- metadata.gz: abe5b18f7eeeb92bba2863746e7cc71d854078462274993403c4d7b93af81ff2c82a3b5c1db2fbdf537f8a139ec15964d76aad042505ade12ad64e6069f40a5a
7
- data.tar.gz: dba1d0cca64620fc48b495ee844600e7a8e38c577ad3f27ada191263038f38a081a3d640f9a66e00fd97c138e1686ae91654d6995225d357bec8a5016e903f5e
6
+ metadata.gz: b3d9d4782cd23143c6674b674780072784077105eeef396ec04d55b194ef102c643c5cd646311cdf024983c1e1a7f70407bea5bb2e0cb5261938e58d0dcceb87
7
+ data.tar.gz: a7e6cc11a4e2625b135cf967af4f614dcaa21fd7ee7b8cfcbea42bd68eea8976368a6392e257f2f229f8a6e9673414546044628cefa1f4fe0d6a2d9042b0b90f
data/CHANGELOG.md CHANGED
@@ -1,9 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.3
4
+
5
+ ### Bug Fix
6
+
7
+ - **`belt deploy` preflight no longer false-fails on `belongs_to ..., index: false`.**
8
+ The deploy preflight added in 0.4.2 demanded a `{Assoc}Index` GSI for *every*
9
+ `belongs_to`, ignoring `index: false` and `index: 'CustomName'` — the exact
10
+ opt-outs the table generator honours. The two halves of the gem disagreed: a
11
+ model that opts out (its reverse lookup is covered by another GSI, or it doesn't
12
+ need one) got a passing `belt setup tables` and a failing `belt deploy`, with no
13
+ way out but `--force`-ing the generator or pinning back to 0.4.1. The preflight's
14
+ index extraction now mirrors the generator exactly: it skips `index: false` and
15
+ honours an explicit `index: 'Name'`. `foreign_key:`-remapped associations
16
+ (`belongs_to :user, foreign_key: 'cognito_sub', index: false`) no longer demand a
17
+ phantom `UserIndex` either.
18
+
3
19
  ## Unreleased
4
20
 
21
+ ## 0.4.4
22
+
5
23
  ### Bug Fix
6
24
 
25
+ - **Gateway-level `scope path:` with a param segment no longer emits malformed paths.**
26
+ A gateway-level `scope path: 'accounts/:account_id'` used to leak the raw Rails-style
27
+ `:account_id` segment straight into route paths (`/accounts/:account_id/changes`)
28
+ instead of the API Gateway `{account_id}` form, and — worse — a bare-param scope with a
29
+ path that didn't start with `/` (e.g. `get 'summary'`) produced fused garbage like
30
+ `/accounts/{account_idsummary}`. It also folded the param segment into the derived
31
+ controller module (`accounts/:account_id/changes`). Now `build_path` joins the scope
32
+ prefix and route path with exactly one `/` and normalizes `:param` → `{param}`, and
33
+ the controller module is derived only from the scope's *static* segments (matching
34
+ Rails, where `scope path:` shapes the URL, not the module). Static-only scopes
35
+ (`scope path: 'admin'` → `admin/users`) are unchanged. A scope nested inside a
36
+ `resources` block was already fine; only the gateway-level case was broken.
37
+
7
38
  - **`belt setup tables` no longer silently clobbers hand-added tables/GSIs.**
8
39
  Regenerating `dynamodb.tf` is a full overwrite from `lambda/models/*.rb`, so a
9
40
  table or `global_secondary_index` added straight into the `.tf` file — one the
@@ -262,19 +262,33 @@ module Belt
262
262
  end
263
263
  end
264
264
 
265
+ # Which GSIs a model's belongs_to declarations require. This MUST agree with the
266
+ # generator (TablesCommand#extract_belongs_to_indexes) or the two halves of the gem
267
+ # disagree: the generator refuses to create an index the preflight then demands,
268
+ # and `belt setup tables` → `belt deploy` loops forever.
269
+ #
270
+ # `index: false` opts out — the reverse lookup is covered some other way (the
271
+ # model's own indexes() entry under a different key, or no reverse lookup at all).
272
+ # The generator skips those, so the preflight must too.
273
+ #
274
+ # An explicit `index: 'Name'` overrides the convention name; we honour it so the
275
+ # check matches the GSI the model actually declared.
265
276
  def extract_expected_indexes(content)
266
277
  indexes = []
267
- content.lines.reject { |line| line.strip.start_with?('#') }.join
268
- .scan(/belongs_to\s+:(\w+)/) do |match|
269
- association_name = match[0]
270
- indexes << {
271
- name: "#{Belt::Inflector.classify(association_name)}Index",
272
- association: association_name
273
- }
278
+ uncommented(content).scan(/belongs_to\s+:(\w+)([^\n]*)/) do |association_name, options|
279
+ next if options.match?(/index:\s*false/)
280
+
281
+ explicit = options.match(/index:\s*['"]([^'"]+)['"]/)
282
+ name = explicit ? explicit[1] : "#{Belt::Inflector.classify(association_name)}Index"
283
+ indexes << { name: name, association: association_name }
274
284
  end
275
285
  indexes
276
286
  end
277
287
 
288
+ def uncommented(content)
289
+ content.lines.reject { |line| line.strip.start_with?('#') }.join
290
+ end
291
+
278
292
  def check_cognito_auth
279
293
  return unless Belt.root?
280
294
 
@@ -788,14 +788,25 @@ module Belt
788
788
  private
789
789
 
790
790
  def build_path(path)
791
- @scope_prefix.empty? ? path : "/#{@scope_prefix}#{path}"
791
+ path = path.to_s
792
+ sep = @scope_prefix.empty? || path.empty? || path.start_with?('/') ? '' : '/'
793
+ prefix = @scope_prefix.empty? ? '' : "/#{@scope_prefix}"
794
+ # Normalize Rails-style `:param` → API Gateway `{param}` so a gateway-level
795
+ # `scope path:` with a param (e.g. "accounts/:account_id") emits valid paths.
796
+ "#{prefix}#{sep}#{path}".gsub(/:([a-zA-Z_]\w*)/) { "{#{::Regexp.last_match(1)}}" }
797
+ end
798
+
799
+ # Strip param segments (`:param` / `{param}`) from a scope prefix before using it
800
+ # as a controller module: `scope path:` affects the URL, not the module.
801
+ def controller_module_from_prefix(prefix)
802
+ prefix.to_s.split('/').reject { |s| s.empty? || s.start_with?(':', '{') }.join('/')
792
803
  end
793
804
 
794
805
  def determine_scoped_controller(resource_name)
795
806
  if @scope_module && !@scope_module.empty?
796
807
  "#{@scope_module}/#{resource_name}"
797
- elsif !@scope_prefix.empty?
798
- "#{@scope_prefix}/#{resource_name}"
808
+ elsif !@scope_prefix.empty? && !(mod = controller_module_from_prefix(@scope_prefix)).empty?
809
+ "#{mod}/#{resource_name}"
799
810
  else
800
811
  resource_name
801
812
  end
data/lib/belt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Belt
4
- VERSION = '0.4.2'
4
+ VERSION = '0.4.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla