dynamoid-cdk-schema 0.1.0 → 0.2.0
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 +10 -0
- data/README.md +25 -0
- data/lib/dynamoid/cdk/schema/introspector.rb +6 -2
- data/lib/dynamoid/cdk/schema/serializer.rb +90 -0
- data/lib/dynamoid/cdk/schema/version.rb +1 -1
- data/lib/dynamoid/cdk/schema.rb +25 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51132f9f8072871ab9915db6a75821e4c3f90582e356c2cb9f22f7a1022ae9e7
|
|
4
|
+
data.tar.gz: ba430e02974802775b082e672f4ec100a4f0a2ae4aceccfc547861665d7cb38f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c717cbfe3c318a7b390d34653bc94cf05f6120a51c8ae5cb7ef7a7925606bcb2945d64518b83a88c8fe0bef5c68035dfa6e20353872c25957a0f5085cd6d0e07
|
|
7
|
+
data.tar.gz: b98d384b5febcebca7a81376407c0100c27bc37f6c80da2b9ce07f8625c436f6063cfe07df5a045795bb7229c228c1eea4979661a95c57c19d6ef06ba3754347
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-08-04
|
|
4
|
+
|
|
5
|
+
- `Dynamoid::CDK::Schema.dump(model)` / `.load(data)` / `.table_from(scope, id, data, **table_props)` — carry a
|
|
6
|
+
schema between processes as JSON-native data. For CDK apps that deliberately cannot load the application:
|
|
7
|
+
a separate infra bundle, a synth step without the app's gems, or models that need config the deploy machine
|
|
8
|
+
lacks. The app dumps its schema at build time, the CDK app reads the file at synth time.
|
|
9
|
+
- Dynamoid is no longer a runtime dependency, matching how `aws-cdk-lib` was already treated. Anyone introspecting
|
|
10
|
+
a model already has Dynamoid loaded; a CDK app building from dumped data should not have to install an ORM.
|
|
11
|
+
**Upgrading:** if you call `.describe` or `.table` and relied on this gem to pull Dynamoid in, declare it yourself.
|
|
12
|
+
|
|
3
13
|
## [0.1.0] - 2026-07-27
|
|
4
14
|
|
|
5
15
|
- Initial release.
|
data/README.md
CHANGED
|
@@ -74,6 +74,31 @@ is yours to pass through as keyword arguments — this gem is about the *schema*
|
|
|
74
74
|
application config at load time (e.g. its table name from Rails config), make that available in the synth process — the
|
|
75
75
|
CDK never uses the physical table name (it assigns one), so a placeholder is enough.
|
|
76
76
|
|
|
77
|
+
## When the CDK app can't load your models
|
|
78
|
+
|
|
79
|
+
Sometimes it shouldn't have to: an infra bundle kept deliberately separate from the application, a synth step with none
|
|
80
|
+
of the app's gems, models that need configuration the deploy machine doesn't have. Dump the schema where the models
|
|
81
|
+
live, and read it where the tables are built:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# in the app, at build time — Dynamoid only
|
|
85
|
+
require "json"
|
|
86
|
+
File.write("build/tables.json", JSON.pretty_generate(Dynamoid::CDK::Schema.dump(Post)))
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
# in the CDK app, at synth time — aws-cdk-lib only, no Dynamoid, no app
|
|
91
|
+
Dynamoid::CDK::Schema.table_from(
|
|
92
|
+
self, "Posts", JSON.parse(File.read("build/tables.json")), removal_policy: ...
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`dump` returns JSON-native data (strings, arrays, hashes, nil), and `load` turns it back into the same descriptor
|
|
97
|
+
`describe` would have produced. `table_from` accepts either.
|
|
98
|
+
|
|
99
|
+
This also makes the schema a build artifact you can diff, review or check into a deployment bundle — synthesis then
|
|
100
|
+
depends only on what was built, not on whether the app happens to load.
|
|
101
|
+
|
|
77
102
|
## Development
|
|
78
103
|
|
|
79
104
|
After checking out the repo, run `bin/setup` to install dependencies, then `bundle exec rspec` to run the tests and
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "dynamoid"
|
|
4
|
-
|
|
5
3
|
module Dynamoid
|
|
6
4
|
module CDK
|
|
7
5
|
module Schema
|
|
@@ -13,6 +11,12 @@ module Dynamoid
|
|
|
13
11
|
module_function
|
|
14
12
|
|
|
15
13
|
def call(model)
|
|
14
|
+
# Required here rather than at load: {Schema.table_from} builds tables
|
|
15
|
+
# from dumped data, and a CDK app doing only that has no reason to
|
|
16
|
+
# carry Dynamoid. Anyone introspecting a model already has it loaded —
|
|
17
|
+
# the model IS a Dynamoid class.
|
|
18
|
+
require "dynamoid"
|
|
19
|
+
|
|
16
20
|
Descriptor.new(
|
|
17
21
|
partition_key: attribute(model, model.hash_key),
|
|
18
22
|
sort_key: (attribute(model, model.range_key) if model.range_key),
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dynamoid
|
|
4
|
+
module CDK
|
|
5
|
+
module Schema
|
|
6
|
+
# {Descriptor} to and from plain data, so the two halves of this gem can run
|
|
7
|
+
# in different processes.
|
|
8
|
+
#
|
|
9
|
+
# The usual arrangement loads the models at synth time and calls
|
|
10
|
+
# {Schema.table}. That is not always possible: a CDK app may deliberately
|
|
11
|
+
# not depend on the application it deploys — a separate infra bundle, a
|
|
12
|
+
# synth step with no database gems, an app whose models cannot load without
|
|
13
|
+
# config the deploy machine lacks. Then the app dumps its schema during
|
|
14
|
+
# build and the CDK app reads the result:
|
|
15
|
+
#
|
|
16
|
+
# # in the app, where Dynamoid and the models are loaded
|
|
17
|
+
# File.write("build/tables.json", JSON.dump(Schema.dump(Post)))
|
|
18
|
+
#
|
|
19
|
+
# # in the CDK app, which needs neither
|
|
20
|
+
# Schema.table_from(self, "Posts", JSON.parse(File.read("build/tables.json")))
|
|
21
|
+
#
|
|
22
|
+
# Keys are strings and values are strings, arrays, hashes or nil, so the
|
|
23
|
+
# result survives a JSON round trip unchanged.
|
|
24
|
+
module Serializer
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def dump(descriptor)
|
|
28
|
+
{
|
|
29
|
+
"partition_key" => dump_attribute(descriptor.partition_key),
|
|
30
|
+
"sort_key" => dump_attribute(descriptor.sort_key),
|
|
31
|
+
"global_secondary_indexes" => descriptor.global_secondary_indexes.map { |i| dump_index(i) },
|
|
32
|
+
"local_secondary_indexes" => descriptor.local_secondary_indexes.map { |i| dump_index(i) }
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def load(data)
|
|
37
|
+
data = stringify(data)
|
|
38
|
+
|
|
39
|
+
Descriptor.new(
|
|
40
|
+
partition_key: load_attribute(data["partition_key"]),
|
|
41
|
+
sort_key: load_attribute(data["sort_key"]),
|
|
42
|
+
global_secondary_indexes: Array(data["global_secondary_indexes"]).map { |i| load_index(i) },
|
|
43
|
+
local_secondary_indexes: Array(data["local_secondary_indexes"]).map { |i| load_index(i) }
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def dump_attribute(attribute)
|
|
48
|
+
return nil if attribute.nil?
|
|
49
|
+
|
|
50
|
+
{ "name" => attribute.name, "type" => attribute.type.to_s }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def load_attribute(data)
|
|
54
|
+
return nil if data.nil?
|
|
55
|
+
|
|
56
|
+
data = stringify(data)
|
|
57
|
+
Attribute.new(name: data.fetch("name"), type: data.fetch("type").to_sym)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dump_index(index)
|
|
61
|
+
{
|
|
62
|
+
"name" => index.name,
|
|
63
|
+
"partition_key" => dump_attribute(index.partition_key),
|
|
64
|
+
"sort_key" => dump_attribute(index.sort_key),
|
|
65
|
+
"projection_type" => index.projection_type.to_s,
|
|
66
|
+
"non_key_attributes" => index.non_key_attributes
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_index(data)
|
|
71
|
+
data = stringify(data)
|
|
72
|
+
|
|
73
|
+
Index.new(
|
|
74
|
+
name: data.fetch("name"),
|
|
75
|
+
partition_key: load_attribute(data["partition_key"]),
|
|
76
|
+
sort_key: load_attribute(data["sort_key"]),
|
|
77
|
+
projection_type: data.fetch("projection_type").to_sym,
|
|
78
|
+
non_key_attributes: data["non_key_attributes"]
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Accept symbol keys too: a descriptor that has not been through JSON is
|
|
83
|
+
# a reasonable thing to hand back, and failing on it would be a trap.
|
|
84
|
+
def stringify(data)
|
|
85
|
+
data.is_a?(Hash) ? data.transform_keys(&:to_s) : data
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/dynamoid/cdk/schema.rb
CHANGED
|
@@ -12,6 +12,7 @@ end
|
|
|
12
12
|
|
|
13
13
|
require_relative "schema/descriptor"
|
|
14
14
|
require_relative "schema/introspector"
|
|
15
|
+
require_relative "schema/serializer"
|
|
15
16
|
require_relative "schema/table_builder"
|
|
16
17
|
|
|
17
18
|
module Dynamoid
|
|
@@ -38,6 +39,30 @@ module Dynamoid
|
|
|
38
39
|
def table(scope, id, model, **table_props)
|
|
39
40
|
TableBuilder.build(scope, id, describe(model), **table_props)
|
|
40
41
|
end
|
|
42
|
+
|
|
43
|
+
# The model's table shape as plain data that survives JSON — for when the
|
|
44
|
+
# CDK app cannot load the models, and the schema has to travel as a file
|
|
45
|
+
# from build time to synth time. Needs only Dynamoid.
|
|
46
|
+
#
|
|
47
|
+
# File.write("build/tables.json", JSON.dump(Schema.dump(Post)))
|
|
48
|
+
def dump(model)
|
|
49
|
+
Serializer.dump(describe(model))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The inverse: a {Descriptor} from whatever {dump} produced.
|
|
53
|
+
def load(data)
|
|
54
|
+
Serializer.load(data)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Build a CDK +TableV2+ from dumped data (or a {Descriptor}) instead of
|
|
58
|
+
# from a model, so the CDK app needs neither Dynamoid nor the app itself.
|
|
59
|
+
# Table-level props pass through exactly as in {table}.
|
|
60
|
+
#
|
|
61
|
+
# Schema.table_from(self, "Posts", JSON.parse(File.read("build/tables.json")))
|
|
62
|
+
def table_from(scope, id, data, **table_props)
|
|
63
|
+
descriptor = data.is_a?(Descriptor) ? data : load(data)
|
|
64
|
+
TableBuilder.build(scope, id, descriptor, **table_props)
|
|
65
|
+
end
|
|
41
66
|
end
|
|
42
67
|
end
|
|
43
68
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dynamoid-cdk-schema
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Omar Qureshi
|
|
@@ -19,7 +19,7 @@ dependencies:
|
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
21
|
version: '4'
|
|
22
|
-
type: :
|
|
22
|
+
type: :development
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
25
|
requirements:
|
|
@@ -46,6 +46,7 @@ files:
|
|
|
46
46
|
- lib/dynamoid/cdk/schema.rb
|
|
47
47
|
- lib/dynamoid/cdk/schema/descriptor.rb
|
|
48
48
|
- lib/dynamoid/cdk/schema/introspector.rb
|
|
49
|
+
- lib/dynamoid/cdk/schema/serializer.rb
|
|
49
50
|
- lib/dynamoid/cdk/schema/table_builder.rb
|
|
50
51
|
- lib/dynamoid/cdk/schema/version.rb
|
|
51
52
|
- sig/dynamoid/cdk/schema.rbs
|