activeitem 0.0.21 → 0.0.22
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 +4 -4
- data/CHANGELOG.md +14 -0
- data/lib/active_item/base.rb +4 -9
- data/lib/active_item/relation.rb +20 -5
- data/lib/active_item/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7d5990fc9c0532dc9a65f803bd9886c1769dc2b4c3071fd4748c079fa7559b3
|
|
4
|
+
data.tar.gz: 1c480246c6a898502eacedda1b456e003f54910a3f8562893cc5c070c06f168e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db67b991fe6c8c9ed9ce18a362dc15dfd9f1b46c63ed20a49e8b1f82bf0bb2dfe3738b1e2f7d7b29a31396e61d40ee3e6d528393b01a93a92bf81c2e9d9f234b
|
|
7
|
+
data.tar.gz: dc5a3c76df184f02bf6fa4f0cd040e9a98e42729326f35243b198fc2d7c8ae4e4a86429bdc457be7f7e9198892ba93aaddf5a4d5650806afcba6cb528d6d54e7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.22
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Scoped `find` on associations** — `Relation#find(id)` now enforces association scope, matching Rails behavior. When called on an association (e.g., `@project.epics.find(id)`), it validates that the found record belongs to the association before returning it. If the record exists but belongs to a different parent, `ActiveItem::RecordNotFound` is raised. This prevents accidentally accessing records outside the association scope.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# Before: would return the epic even if it belonged to a different project
|
|
11
|
+
@project.epics.find(epic_id)
|
|
12
|
+
|
|
13
|
+
# After: raises RecordNotFound if epic.project_id != @project.id
|
|
14
|
+
@project.epics.find(epic_id)
|
|
15
|
+
```
|
|
16
|
+
|
|
3
17
|
## 0.0.21
|
|
4
18
|
|
|
5
19
|
### Added
|
data/lib/active_item/base.rb
CHANGED
|
@@ -912,15 +912,11 @@ module ActiveItem
|
|
|
912
912
|
indexes.each do |index_name, config|
|
|
913
913
|
# Check partition key if it's being changed
|
|
914
914
|
partition_key = config[:partition_key]&.to_s
|
|
915
|
-
if partition_key && changed_dynamo_keys.key?(partition_key)
|
|
916
|
-
validate_gsi_key_value!(partition_key, changed_dynamo_keys[partition_key], index_name)
|
|
917
|
-
end
|
|
915
|
+
validate_gsi_key_value!(partition_key, changed_dynamo_keys[partition_key], index_name) if partition_key && changed_dynamo_keys.key?(partition_key)
|
|
918
916
|
|
|
919
917
|
# Check sort key if it's being changed
|
|
920
918
|
sort_key = config[:sort_key]&.to_s
|
|
921
|
-
if sort_key && changed_dynamo_keys.key?(sort_key)
|
|
922
|
-
validate_gsi_key_value!(sort_key, changed_dynamo_keys[sort_key], index_name)
|
|
923
|
-
end
|
|
919
|
+
validate_gsi_key_value!(sort_key, changed_dynamo_keys[sort_key], index_name) if sort_key && changed_dynamo_keys.key?(sort_key)
|
|
924
920
|
end
|
|
925
921
|
end
|
|
926
922
|
|
|
@@ -953,9 +949,8 @@ module ActiveItem
|
|
|
953
949
|
case value
|
|
954
950
|
when String
|
|
955
951
|
!value.empty? # Empty strings are not allowed for GSI keys
|
|
956
|
-
when Integer, Float, BigDecimal
|
|
957
|
-
|
|
958
|
-
when StringIO
|
|
952
|
+
when Integer, Float, BigDecimal, StringIO
|
|
953
|
+
# Numeric (Integer, Float, BigDecimal) and Binary (StringIO) are valid GSI key types
|
|
959
954
|
true
|
|
960
955
|
else
|
|
961
956
|
false
|
data/lib/active_item/relation.rb
CHANGED
|
@@ -294,18 +294,26 @@ module ActiveItem
|
|
|
294
294
|
|
|
295
295
|
# Find by id within the current scope, or find by block (like Enumerable#find)
|
|
296
296
|
#
|
|
297
|
+
# When called on an association (e.g., @project.epics.find(id)), validates that
|
|
298
|
+
# the found record belongs to the association's scope. This matches Rails behavior
|
|
299
|
+
# where association.find(id) only returns records belonging to that association.
|
|
300
|
+
#
|
|
297
301
|
# @overload find(id)
|
|
298
302
|
# Find a record by ID within the current scope
|
|
299
303
|
# @param id [String] The ID to find
|
|
300
|
-
# @return [Object
|
|
304
|
+
# @return [Object] The found record
|
|
305
|
+
# @raise [ActiveItem::RecordNotFound] If record not found or not in scope
|
|
301
306
|
#
|
|
302
307
|
# @overload find(&block)
|
|
303
308
|
# Find the first record matching the block condition (like Enumerable#find/detect)
|
|
304
309
|
# @yield [record] Evaluates the block for each record
|
|
305
310
|
# @return [Object, nil] The first record where block returns true, or nil
|
|
306
311
|
#
|
|
307
|
-
# @example Find by ID
|
|
308
|
-
#
|
|
312
|
+
# @example Find by ID (scoped to association)
|
|
313
|
+
# @project.epics.find('epic-123') # Only finds if epic belongs to @project
|
|
314
|
+
#
|
|
315
|
+
# @example Find by ID (unscoped)
|
|
316
|
+
# Epic.where(status: 'active').find('epic-123') # Finds any active epic
|
|
309
317
|
#
|
|
310
318
|
# @example Find by block
|
|
311
319
|
# User.where(status: 'active').find { |u| u.email.include?('@example.com') }
|
|
@@ -317,13 +325,20 @@ module ActiveItem
|
|
|
317
325
|
elsif id
|
|
318
326
|
# Use direct GetItem instead of scanning — O(1) vs O(n)
|
|
319
327
|
record = resolved_model.find(id)
|
|
328
|
+
|
|
329
|
+
# When called on an association (has conditions), validate scope
|
|
330
|
+
# This ensures @project.epics.find(id) only returns epics belonging to @project
|
|
331
|
+
if conditions.any? && !conditions[:_empty]
|
|
332
|
+
foreign_key, expected_value = conditions.first
|
|
333
|
+
actual_value = record.send(foreign_key)
|
|
334
|
+
raise ActiveItem::RecordNotFound, "Couldn't find #{resolved_model.name} with id=#{id}" unless actual_value == expected_value
|
|
335
|
+
end
|
|
336
|
+
|
|
320
337
|
preload_associations_for_records([record]) if includes_associations.any?
|
|
321
338
|
record
|
|
322
339
|
else
|
|
323
340
|
raise ArgumentError, 'find requires either an ID or a block'
|
|
324
341
|
end
|
|
325
|
-
rescue ActiveItem::RecordNotFound
|
|
326
|
-
nil
|
|
327
342
|
end
|
|
328
343
|
|
|
329
344
|
# Find by conditions within current scope
|
data/lib/active_item/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeitem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.22
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Davis
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activemodel
|