magik 0.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.
data/lib/magik/auth.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Generated authentication flows, Rodauth-backed.
5
+ #
6
+ # Implements: **Phase 7 — Auth, Billing, Admin** of `docs/idea/00-build-spec.md`.
7
+ #
8
+ # Planned DSL surface, copied from the spec:
9
+ #
10
+ # * `auth do strategy; oauth_providers; two_factor end`
11
+ #
12
+ # Status: **Not implemented — spec only.** Every entry point below raises
13
+ # {NotImplementedError}. Nothing here reads config, touches a database or
14
+ # emits a byte of HTML.
15
+ #
16
+ # @see Magik::SUBSYSTEMS
17
+ module Auth
18
+ # The build-spec phase this subsystem implements.
19
+ # @return [String]
20
+ SPEC_PHASE = "Phase 7 — Auth, Billing, Admin"
21
+
22
+ # The DSL this subsystem will expose, verbatim from the spec.
23
+ # @return [Array<String>]
24
+ DSL_SURFACE = [
25
+ "auth do strategy; oauth_providers; two_factor end"
26
+ ].freeze
27
+
28
+ # Implementation status of this subsystem.
29
+ # @return [String]
30
+ STATUS = "Not implemented — spec only"
31
+
32
+ # Entry point for the Auth DSL.
33
+ #
34
+ # @param _args [Array] ignored
35
+ # @param _options [Hash] ignored
36
+ # @return [void] never returns
37
+ # @raise [NotImplementedError] always, until Phase 7 lands
38
+ def self.define(*_args, **_options)
39
+ raise NotImplementedError, "Magik::Auth is spec-only; see docs/idea/00-build-spec.md"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Subscriptions, trials and metered billing over the Stripe/Paddle SDKs.
5
+ #
6
+ # Implements: **Phase 7 — Auth, Billing, Admin** of `docs/idea/00-build-spec.md`.
7
+ #
8
+ # Planned DSL surface, copied from the spec:
9
+ #
10
+ # * `billing provider: :stripe do plans end`
11
+ #
12
+ # Status: **Not implemented — spec only.** Every entry point below raises
13
+ # {NotImplementedError}. Nothing here reads config, touches a database or
14
+ # emits a byte of HTML.
15
+ #
16
+ # @see Magik::SUBSYSTEMS
17
+ module Billing
18
+ # The build-spec phase this subsystem implements.
19
+ # @return [String]
20
+ SPEC_PHASE = "Phase 7 — Auth, Billing, Admin"
21
+
22
+ # The DSL this subsystem will expose, verbatim from the spec.
23
+ # @return [Array<String>]
24
+ DSL_SURFACE = [
25
+ "billing provider: :stripe do plans end"
26
+ ].freeze
27
+
28
+ # Implementation status of this subsystem.
29
+ # @return [String]
30
+ STATUS = "Not implemented — spec only"
31
+
32
+ # Entry point for the Billing DSL.
33
+ #
34
+ # @param _args [Array] ignored
35
+ # @param _options [Hash] ignored
36
+ # @return [void] never returns
37
+ # @raise [NotImplementedError] always, until Phase 7 lands
38
+ def self.define(*_args, **_options)
39
+ raise NotImplementedError, "Magik::Billing is spec-only; see docs/idea/00-build-spec.md"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # The guardrail linter. The guardrails are the product: ledgers must balance, `field
5
+ # :card_number` is refused, domain boundaries hold, screens and actions hold no
6
+ # in-process state, timestamps carry a timezone.
7
+ #
8
+ # Implements: **Build order step 12 — `magik check` linter** of `docs/idea/00-build-spec.md`.
9
+ #
10
+ # Planned DSL surface, copied from the spec:
11
+ #
12
+ # * `magik check --scale — warns on queries missing tenant_id in WHERE`
13
+ # * `boot guardrails enforced as a standalone lint pass`
14
+ #
15
+ # Status: **Not implemented — spec only.** Every entry point below raises
16
+ # {NotImplementedError}. Nothing here reads config, touches a database or
17
+ # emits a byte of HTML.
18
+ #
19
+ # @see Magik::SUBSYSTEMS
20
+ module Check
21
+ # The build-spec phase this subsystem implements.
22
+ # @return [String]
23
+ SPEC_PHASE = "Build order step 12 — `magik check` linter"
24
+
25
+ # The DSL this subsystem will expose, verbatim from the spec.
26
+ # @return [Array<String>]
27
+ DSL_SURFACE = [
28
+ "magik check --scale — warns on queries missing tenant_id in WHERE",
29
+ "boot guardrails enforced as a standalone lint pass"
30
+ ].freeze
31
+
32
+ # Implementation status of this subsystem.
33
+ # @return [String]
34
+ STATUS = "Not implemented — spec only"
35
+
36
+ # Entry point for the Check DSL.
37
+ #
38
+ # @param _args [Array] ignored
39
+ # @param _options [Hash] ignored
40
+ # @return [void] never returns
41
+ # @raise [NotImplementedError] always, until Build order step 12 lands
42
+ def self.define(*_args, **_options)
43
+ raise NotImplementedError, "Magik::Check is spec-only; see docs/idea/00-build-spec.md"
44
+ end
45
+ end
46
+ end
data/lib/magik/cli.rb ADDED
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "optparse"
5
+
6
+ module Magik
7
+ # The `magik` command line.
8
+ #
9
+ # Two commands work today — {version} and {help}. Every other command named
10
+ # in `docs/idea/00-build-spec.md` is listed by `magik help` with status
11
+ # `planned` and exits non-zero with `MAGIK_COMMAND_NOT_IMPLEMENTED` if you
12
+ # run it. That is deliberate: the CLI tells you the truth about what exists.
13
+ #
14
+ # Both working commands support `--json`, so scripts and CI can consume the
15
+ # output without parsing prose. Errors are emitted as JSON too when `--json`
16
+ # is set, using {Magik::Error#to_h}.
17
+ #
18
+ # Implemented with the stdlib `OptionParser`; Magik takes no third-party CLI
19
+ # dependency.
20
+ #
21
+ # @example Human output
22
+ # $ magik version
23
+ # magik 0.0.1 (spec only)
24
+ # @example Machine output
25
+ # $ magik version --json
26
+ # {"name":"magik","version":"0.0.1","status":"spec only",...}
27
+ module CLI
28
+ # Exit status for a command that did what it said.
29
+ # @return [Integer]
30
+ EXIT_SUCCESS = 0
31
+
32
+ # Exit status for any {Magik::Error} — unknown command, planned command,
33
+ # bad option.
34
+ # @return [Integer]
35
+ EXIT_ERROR = 1
36
+
37
+ # Short status label reused in every output format.
38
+ # @return [String]
39
+ STATUS = "spec only"
40
+
41
+ # Every command the spec calls for, in the order `magik help` prints them.
42
+ #
43
+ # `:status` is `"ready"` for commands that work today and `"planned"` for
44
+ # commands that only exist in `docs/idea/00-build-spec.md`.
45
+ #
46
+ # @return [Hash{String => Hash{Symbol => String}}]
47
+ COMMANDS = {
48
+ "new" => { summary: "Generate a new Magik application", status: "planned" },
49
+ "generate" => { summary: "Generate a model, screen, action or job", status: "planned" },
50
+ "console" => { summary: "Open an application console", status: "planned" },
51
+ "server" => { summary: "Run the Falcon-backed application server", status: "planned" },
52
+ "worker" => { summary: "Run the background job worker", status: "planned" },
53
+ "test" => { summary: "Run the application test suite", status: "planned" },
54
+ "check" => { summary: "Lint an application against Magik's boot guardrails", status: "planned" },
55
+ "version" => { summary: "Print the magik gem version", status: "ready" },
56
+ "help" => { summary: "List every magik command and its status", status: "ready" }
57
+ }.freeze
58
+
59
+ class << self
60
+ # Run the CLI.
61
+ #
62
+ # Never raises for user error: a {Magik::Error} is rendered to `err` (or
63
+ # to `out` as JSON when `--json` is set) and reported as {EXIT_ERROR}.
64
+ #
65
+ # @param argv [Array<String>] the raw command line, usually `ARGV`
66
+ # @param out [IO] stream for command output
67
+ # @param err [IO] stream for error output
68
+ # @return [Integer] a process exit status: {EXIT_SUCCESS} or {EXIT_ERROR}
69
+ # @example
70
+ # Magik::CLI.start(["version", "--json"]) # => 0
71
+ def start(argv = [], out: $stdout, err: $stderr)
72
+ options = { json: false }
73
+ args = parse_options(Array(argv), options)
74
+ dispatch(args, options, out)
75
+ EXIT_SUCCESS
76
+ rescue Magik::Error => e
77
+ report_error(e, options[:json], out, err)
78
+ EXIT_ERROR
79
+ end
80
+
81
+ # Print the gem version.
82
+ #
83
+ # @param out [IO] stream to write to
84
+ # @param json [Boolean] emit JSON instead of prose
85
+ # @return [void]
86
+ def version(out: $stdout, json: false)
87
+ if json
88
+ out.puts JSON.generate(
89
+ name: "magik",
90
+ version: Magik::VERSION,
91
+ status: STATUS,
92
+ ruby: RUBY_VERSION,
93
+ ruby_engine: RUBY_ENGINE
94
+ )
95
+ else
96
+ out.puts "magik #{Magik::VERSION} (#{STATUS})"
97
+ end
98
+ end
99
+
100
+ # Print usage and every command in {COMMANDS} with its status.
101
+ #
102
+ # @param out [IO] stream to write to
103
+ # @param json [Boolean] emit JSON instead of prose
104
+ # @return [void]
105
+ def help(out: $stdout, json: false)
106
+ return out.puts(JSON.generate(usage: usage, status: STATUS, commands: commands_as_data)) if json
107
+
108
+ out.puts usage
109
+ out.puts
110
+ out.puts "Commands:"
111
+ width = COMMANDS.keys.map(&:length).max
112
+ COMMANDS.each do |name, meta|
113
+ out.puts format(" %-#{width}s %-8s %s", name, meta[:status], meta[:summary])
114
+ end
115
+ out.puts
116
+ out.puts "Global options:"
117
+ out.puts " --json Emit machine-readable JSON"
118
+ out.puts " -v, --version Print the magik gem version"
119
+ out.puts " -h, --help Show this message"
120
+ out.puts
121
+ out.puts "Magik is #{STATUS}: `planned` commands are specified in " \
122
+ "docs/idea/00-build-spec.md and not implemented."
123
+ end
124
+
125
+ # The one-line usage banner.
126
+ # @return [String]
127
+ def usage
128
+ "Usage: magik <command> [options]"
129
+ end
130
+
131
+ # {COMMANDS} flattened into an array of plain hashes, for JSON output.
132
+ # @return [Array<Hash{Symbol => String}>]
133
+ def commands_as_data
134
+ COMMANDS.map { |name, meta| { name: name, summary: meta[:summary], status: meta[:status] } }
135
+ end
136
+
137
+ private
138
+
139
+ # @param argv [Array<String>] raw arguments
140
+ # @param options [Hash] mutated with parsed global options
141
+ # @return [Array<String>] the remaining positional arguments
142
+ # @raise [Magik::InvalidOptionError] on an unrecognised flag
143
+ def parse_options(argv, options)
144
+ rest = argv.dup
145
+ parser = OptionParser.new do |o|
146
+ o.banner = usage
147
+ o.on("--json", "Emit machine-readable JSON") { options[:json] = true }
148
+ o.on("-v", "--version", "Print the magik gem version") { options[:command] = "version" }
149
+ o.on("-h", "--help", "Show this message") { options[:command] = "help" }
150
+ end
151
+ parser.parse!(rest)
152
+ rest
153
+ rescue OptionParser::ParseError => e
154
+ raise InvalidOptionError, e.message
155
+ end
156
+
157
+ # @param args [Array<String>] positional arguments
158
+ # @param options [Hash] parsed global options
159
+ # @param out [IO] stream for command output
160
+ # @return [void]
161
+ # @raise [Magik::CommandNotImplementedError] for a `planned` command
162
+ # @raise [Magik::UnknownCommandError] for anything else
163
+ def dispatch(args, options, out)
164
+ command = options[:command] || args.first || "help"
165
+
166
+ case command
167
+ when "version" then version(out: out, json: options[:json])
168
+ when "help" then help(out: out, json: options[:json])
169
+ else unsupported!(command)
170
+ end
171
+ end
172
+
173
+ # @param command [String] the requested command
174
+ # @raise [Magik::CommandNotImplementedError, Magik::UnknownCommandError] always
175
+ # @return [void]
176
+ def unsupported!(command)
177
+ raise CommandNotImplementedError, "`magik #{command}` is planned but not implemented" if COMMANDS.key?(command)
178
+
179
+ raise UnknownCommandError, "`#{command}` is not a magik command"
180
+ end
181
+
182
+ # @param error [Magik::Error] the error to report
183
+ # @param json [Boolean] emit JSON instead of prose
184
+ # @param out [IO] stream used for JSON output
185
+ # @param err [IO] stream used for prose output
186
+ # @return [void]
187
+ def report_error(error, json, out, err)
188
+ if json
189
+ out.puts JSON.generate(error: error.to_h)
190
+ else
191
+ err.puts error.message
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
data/lib/magik/core.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Application composition root: the `App.define` boot process, the Sequel connection,
5
+ # and the config swap points every opinionated default must honour.
6
+ #
7
+ # Implements: **Phase 1 — Foundation** of `docs/idea/00-build-spec.md`.
8
+ #
9
+ # Planned DSL surface, copied from the spec:
10
+ #
11
+ # * `App.define :Name do ... end`
12
+ # * `tenant_by :subdomain`
13
+ # * `swap points: db, cache, jobs, search, realtime backends`
14
+ #
15
+ # Status: **Not implemented — spec only.** Every entry point below raises
16
+ # {NotImplementedError}. Nothing here reads config, touches a database or
17
+ # emits a byte of HTML.
18
+ #
19
+ # @see Magik::SUBSYSTEMS
20
+ module Core
21
+ # The build-spec phase this subsystem implements.
22
+ # @return [String]
23
+ SPEC_PHASE = "Phase 1 — Foundation"
24
+
25
+ # The DSL this subsystem will expose, verbatim from the spec.
26
+ # @return [Array<String>]
27
+ DSL_SURFACE = [
28
+ "App.define :Name do ... end",
29
+ "tenant_by :subdomain",
30
+ "swap points: db, cache, jobs, search, realtime backends"
31
+ ].freeze
32
+
33
+ # Implementation status of this subsystem.
34
+ # @return [String]
35
+ STATUS = "Not implemented — spec only"
36
+
37
+ # Entry point for the Core DSL.
38
+ #
39
+ # @param _args [Array] ignored
40
+ # @param _options [Hash] ignored
41
+ # @return [void] never returns
42
+ # @raise [NotImplementedError] always, until Phase 1 lands
43
+ def self.define(*_args, **_options)
44
+ raise NotImplementedError, "Magik::Core is spec-only; see docs/idea/00-build-spec.md"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # The domain module system for large apps, with boot-time boundary enforcement: a
5
+ # domain reaches another domain only through its published interface or events.
6
+ #
7
+ # Implements: **Build order step 12 — Domain module system** of `docs/idea/00-build-spec.md`.
8
+ #
9
+ # Planned DSL surface, copied from the spec:
10
+ #
11
+ # * `domains/<name>/domain.rb declares depends_on, exposes, publishes_events`
12
+ # * `boot-time enforcement of cross-domain access`
13
+ #
14
+ # Status: **Not implemented — spec only.** Every entry point below raises
15
+ # {NotImplementedError}. Nothing here reads config, touches a database or
16
+ # emits a byte of HTML.
17
+ #
18
+ # @see Magik::SUBSYSTEMS
19
+ module Domains
20
+ # The build-spec phase this subsystem implements.
21
+ # @return [String]
22
+ SPEC_PHASE = "Build order step 12 — Domain module system"
23
+
24
+ # The DSL this subsystem will expose, verbatim from the spec.
25
+ # @return [Array<String>]
26
+ DSL_SURFACE = [
27
+ "domains/<name>/domain.rb declares depends_on, exposes, publishes_events",
28
+ "boot-time enforcement of cross-domain access"
29
+ ].freeze
30
+
31
+ # Implementation status of this subsystem.
32
+ # @return [String]
33
+ STATUS = "Not implemented — spec only"
34
+
35
+ # Entry point for the Domains DSL.
36
+ #
37
+ # @param _args [Array] ignored
38
+ # @param _options [Hash] ignored
39
+ # @return [void] never returns
40
+ # @raise [NotImplementedError] always, until Build order step 12 lands
41
+ def self.define(*_args, **_options)
42
+ raise NotImplementedError, "Magik::Domains is spec-only; see docs/idea/00-build-spec.md"
43
+ end
44
+ end
45
+ end
data/lib/magik/i18n.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Localisation and timezone-safe time handling. Timestamps cannot be rendered without
5
+ # explicit timezone conversion — that is a boot guardrail, not a lint.
6
+ #
7
+ # Implements: **Phase 8 — i18n, PWA, Notifications** of `docs/idea/00-build-spec.md`.
8
+ #
9
+ # Planned DSL surface, copied from the spec:
10
+ #
11
+ # * `locales :en, :es, ...`
12
+ # * `translatable: true fields`
13
+ # * `timezone-safe :timestamp type`
14
+ #
15
+ # Status: **Not implemented — spec only.** Every entry point below raises
16
+ # {NotImplementedError}. Nothing here reads config, touches a database or
17
+ # emits a byte of HTML.
18
+ #
19
+ # @see Magik::SUBSYSTEMS
20
+ module I18n
21
+ # The build-spec phase this subsystem implements.
22
+ # @return [String]
23
+ SPEC_PHASE = "Phase 8 — i18n, PWA, Notifications"
24
+
25
+ # The DSL this subsystem will expose, verbatim from the spec.
26
+ # @return [Array<String>]
27
+ DSL_SURFACE = [
28
+ "locales :en, :es, ...",
29
+ "translatable: true fields",
30
+ "timezone-safe :timestamp type"
31
+ ].freeze
32
+
33
+ # Implementation status of this subsystem.
34
+ # @return [String]
35
+ STATUS = "Not implemented — spec only"
36
+
37
+ # Entry point for the I18n DSL.
38
+ #
39
+ # @param _args [Array] ignored
40
+ # @param _options [Hash] ignored
41
+ # @return [void] never returns
42
+ # @raise [NotImplementedError] always, until Phase 8 lands
43
+ def self.define(*_args, **_options)
44
+ raise NotImplementedError, "Magik::I18n is spec-only; see docs/idea/00-build-spec.md"
45
+ end
46
+ end
47
+ end
data/lib/magik/jobs.rb ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Background jobs on a Postgres-backed transactional queue (Que-style) by default,
5
+ # swappable to Kafka.
6
+ #
7
+ # Implements: **Phase 4 — Jobs & Async** of `docs/idea/00-build-spec.md`.
8
+ #
9
+ # Planned DSL surface, copied from the spec:
10
+ #
11
+ # * `job :Name do retry; schedule :cron/:every; perform do |args| end end`
12
+ # * `magik worker — horizontally scalable worker process`
13
+ #
14
+ # Status: **Not implemented — spec only.** Every entry point below raises
15
+ # {NotImplementedError}. Nothing here reads config, touches a database or
16
+ # emits a byte of HTML.
17
+ #
18
+ # @see Magik::SUBSYSTEMS
19
+ module Jobs
20
+ # The build-spec phase this subsystem implements.
21
+ # @return [String]
22
+ SPEC_PHASE = "Phase 4 — Jobs & Async"
23
+
24
+ # The DSL this subsystem will expose, verbatim from the spec.
25
+ # @return [Array<String>]
26
+ DSL_SURFACE = [
27
+ "job :Name do retry; schedule :cron/:every; perform do |args| end end",
28
+ "magik worker — horizontally scalable worker process"
29
+ ].freeze
30
+
31
+ # Implementation status of this subsystem.
32
+ # @return [String]
33
+ STATUS = "Not implemented — spec only"
34
+
35
+ # Entry point for the Jobs DSL.
36
+ #
37
+ # @param _args [Array] ignored
38
+ # @param _options [Hash] ignored
39
+ # @return [void] never returns
40
+ # @raise [NotImplementedError] always, until Phase 4 lands
41
+ def self.define(*_args, **_options)
42
+ raise NotImplementedError, "Magik::Jobs is spec-only; see docs/idea/00-build-spec.md"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Double-entry ledgers with boot-time balance validation and append-only entries, plus
5
+ # the audit and multi-step flow primitives that fintech work needs.
6
+ #
7
+ # Implements: **Phase 5 — Money & Compliance** of `docs/idea/00-build-spec.md`.
8
+ #
9
+ # Planned DSL surface, copied from the spec:
10
+ #
11
+ # * `ledger :Name do account; entry do debit/credit/guard end end`
12
+ # * `audited + immutable_after: model annotations`
13
+ # * `flow :Name do step ... end — multi-step wizards`
14
+ #
15
+ # Status: **Not implemented — spec only.** Every entry point below raises
16
+ # {NotImplementedError}. Nothing here reads config, touches a database or
17
+ # emits a byte of HTML.
18
+ #
19
+ # @see Magik::SUBSYSTEMS
20
+ module Ledger
21
+ # The build-spec phase this subsystem implements.
22
+ # @return [String]
23
+ SPEC_PHASE = "Phase 5 — Money & Compliance"
24
+
25
+ # The DSL this subsystem will expose, verbatim from the spec.
26
+ # @return [Array<String>]
27
+ DSL_SURFACE = [
28
+ "ledger :Name do account; entry do debit/credit/guard end end",
29
+ "audited + immutable_after: model annotations",
30
+ "flow :Name do step ... end — multi-step wizards"
31
+ ].freeze
32
+
33
+ # Implementation status of this subsystem.
34
+ # @return [String]
35
+ STATUS = "Not implemented — spec only"
36
+
37
+ # Entry point for the Ledger DSL.
38
+ #
39
+ # @param _args [Array] ignored
40
+ # @param _options [Hash] ignored
41
+ # @return [void] never returns
42
+ # @raise [NotImplementedError] always, until Phase 5 lands
43
+ def self.define(*_args, **_options)
44
+ raise NotImplementedError, "Magik::Ledger is spec-only; see docs/idea/00-build-spec.md"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # The model DSL — a thin, explicit wrapper over Sequel. No lazy-loading magic, no
5
+ # implicit N+1.
6
+ #
7
+ # Implements: **Phase 1 — Foundation** of `docs/idea/00-build-spec.md`.
8
+ #
9
+ # Planned DSL surface, copied from the spec:
10
+ #
11
+ # * `model :Name do field/belongs_to/has_many/validate/scope end`
12
+ # * `:money field type, integer-cents backed (floats forbidden)`
13
+ # * `UUIDv7 primary keys`
14
+ # * `tenant_id auto-injection`
15
+ #
16
+ # Status: **Not implemented — spec only.** Every entry point below raises
17
+ # {NotImplementedError}. Nothing here reads config, touches a database or
18
+ # emits a byte of HTML.
19
+ #
20
+ # @see Magik::SUBSYSTEMS
21
+ module Model
22
+ # The build-spec phase this subsystem implements.
23
+ # @return [String]
24
+ SPEC_PHASE = "Phase 1 — Foundation"
25
+
26
+ # The DSL this subsystem will expose, verbatim from the spec.
27
+ # @return [Array<String>]
28
+ DSL_SURFACE = [
29
+ "model :Name do field/belongs_to/has_many/validate/scope end",
30
+ ":money field type, integer-cents backed (floats forbidden)",
31
+ "UUIDv7 primary keys",
32
+ "tenant_id auto-injection"
33
+ ].freeze
34
+
35
+ # Implementation status of this subsystem.
36
+ # @return [String]
37
+ STATUS = "Not implemented — spec only"
38
+
39
+ # Entry point for the Model DSL.
40
+ #
41
+ # @param _args [Array] ignored
42
+ # @param _options [Hash] ignored
43
+ # @return [void] never returns
44
+ # @raise [NotImplementedError] always, until Phase 1 lands
45
+ def self.define(*_args, **_options)
46
+ raise NotImplementedError, "Magik::Model is spec-only; see docs/idea/00-build-spec.md"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magik
4
+ # Multi-channel notifications from a single declaration.
5
+ #
6
+ # Implements: **Phase 8 — i18n, PWA, Notifications** of `docs/idea/00-build-spec.md`.
7
+ #
8
+ # Planned DSL surface, copied from the spec:
9
+ #
10
+ # * `notification :name do channel :email, :in_app, :push end`
11
+ #
12
+ # Status: **Not implemented — spec only.** Every entry point below raises
13
+ # {NotImplementedError}. Nothing here reads config, touches a database or
14
+ # emits a byte of HTML.
15
+ #
16
+ # @see Magik::SUBSYSTEMS
17
+ module Notify
18
+ # The build-spec phase this subsystem implements.
19
+ # @return [String]
20
+ SPEC_PHASE = "Phase 8 — i18n, PWA, Notifications"
21
+
22
+ # The DSL this subsystem will expose, verbatim from the spec.
23
+ # @return [Array<String>]
24
+ DSL_SURFACE = [
25
+ "notification :name do channel :email, :in_app, :push end"
26
+ ].freeze
27
+
28
+ # Implementation status of this subsystem.
29
+ # @return [String]
30
+ STATUS = "Not implemented — spec only"
31
+
32
+ # Entry point for the Notify DSL.
33
+ #
34
+ # @param _args [Array] ignored
35
+ # @param _options [Hash] ignored
36
+ # @return [void] never returns
37
+ # @raise [NotImplementedError] always, until Phase 8 lands
38
+ def self.define(*_args, **_options)
39
+ raise NotImplementedError, "Magik::Notify is spec-only; see docs/idea/00-build-spec.md"
40
+ end
41
+ end
42
+ end