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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +90 -0
- data/LICENSE +21 -0
- data/README.md +356 -0
- data/exe/magik +13 -0
- data/lib/magik/action.rb +44 -0
- data/lib/magik/admin.rb +42 -0
- data/lib/magik/api.rb +49 -0
- data/lib/magik/auth.rb +42 -0
- data/lib/magik/billing.rb +42 -0
- data/lib/magik/check.rb +46 -0
- data/lib/magik/cli.rb +196 -0
- data/lib/magik/core.rb +47 -0
- data/lib/magik/domains.rb +45 -0
- data/lib/magik/i18n.rb +47 -0
- data/lib/magik/jobs.rb +45 -0
- data/lib/magik/ledger.rb +47 -0
- data/lib/magik/model.rb +49 -0
- data/lib/magik/notify.rb +42 -0
- data/lib/magik/pwa.rb +43 -0
- data/lib/magik/realtime.rb +51 -0
- data/lib/magik/render.rb +49 -0
- data/lib/magik/router.rb +45 -0
- data/lib/magik/schema.rb +42 -0
- data/lib/magik/testing.rb +51 -0
- data/lib/magik/version.rb +19 -0
- data/lib/magik.rb +258 -0
- metadata +87 -0
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
|
data/lib/magik/check.rb
ADDED
|
@@ -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
|
data/lib/magik/ledger.rb
ADDED
|
@@ -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
|
data/lib/magik/model.rb
ADDED
|
@@ -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
|
data/lib/magik/notify.rb
ADDED
|
@@ -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
|