hecks 3.0.4 → 3.1.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/exe/hecks +25 -7
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +2 -2
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +59 -32
- data/lib/hecks/bluebook/hexagon.rb +45 -25
- data/lib/hecks/bluebook/model_check.rb +7 -7
- data/lib/hecks/chapters.rb +31 -3
- data/lib/hecks/cli/check_era.rb +1 -1
- data/lib/hecks/cli/console.rb +24 -8
- data/lib/hecks/cli/deploy_template_diff.rb +1 -1
- data/lib/hecks/cli/doc_coverage.rb +1 -1
- data/lib/hecks/cli/domain_stub.rb +128 -0
- data/lib/hecks/cli/interview_draft.rb +246 -0
- data/lib/hecks/cli/model_check.rb +1 -1
- data/lib/hecks/cli/project_cli.rb +69 -18
- data/lib/hecks/cli/reference.rb +2 -1
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +1 -1
- data/lib/hecks/cli/seed_semantics_corpus.rb +38 -9
- data/lib/hecks/cli/spec_example.rb +1 -1
- data/lib/hecks/cli/stress_concurrency_specs.rb +14 -5
- data/lib/hecks/corpus.rb +11 -8
- data/lib/hecks/deploy/bluebook/deploy.bluebook +85 -12
- data/lib/hecks/deploy/oidc.json +5 -0
- data/lib/hecks/doc/reference.rb +4 -3
- data/lib/hecks/doors/cli_door.rb +13 -13
- data/lib/hecks/doors/cli_runner.rb +52 -32
- data/lib/hecks/doors/launcher_options.rb +23 -5
- data/lib/hecks/doors/mcp_door.rb +40 -18
- data/lib/hecks/doors/mcp_door_scope.rb +244 -24
- data/lib/hecks/doors.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +2 -2
- data/lib/hecks/forms/banking_presentation.rb +1 -1
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -1
- data/lib/hecks/framework/bluebook/console_settings.bluebook +1 -1
- data/lib/hecks/framework/bluebook/framework.hecksagon +4 -4
- data/lib/hecks/framework.rb +1 -1
- data/lib/hecks/fuzzing/combination_miner.rb +1 -1
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +1 -1
- data/lib/hecks/fuzzing/differential.rb +4 -2
- data/lib/hecks/fuzzing/era_boundary.rb +1 -1
- data/lib/hecks/fuzzing/form_census.rb +2 -2
- data/lib/hecks/fuzzing/generated_domain_check.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot.rb +10 -3
- data/lib/hecks/fuzzing/persistence_parity.rb +2 -2
- data/lib/hecks/fuzzing/target_capabilities.rb +3 -3
- data/lib/hecks/gate/stages.yml +8 -4
- data/lib/hecks/grammar/grammar.hecksagon +2 -2
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +4 -4
- data/lib/hecks/hecks/adapters/host_http.rb +4 -3
- data/lib/hecks/hecks/adapters/local_files.rb +59 -0
- data/lib/hecks/hecks/adapters/site_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +54 -0
- data/lib/hecks/hecks/codebase.bluebook +4 -3
- data/lib/hecks/hecks/context_map.hecksagon +2 -2
- data/lib/hecks/hecks/custodian.bluebook +45 -3
- data/lib/hecks/hecks/hecks.hecksagon +10 -4
- data/lib/hecks/hecks/hecks.world +7 -4
- data/lib/hecks/hecks/site.behaviors +17 -0
- data/lib/hecks/hecks/site.world +10 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +24 -47
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +2 -1
- data/lib/hecks/projections/deploy/box/settings.rb +248 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +267 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +70 -0
- data/lib/hecks/projections/deploy/box/templates/fetch-secrets.sh +21 -0
- data/lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl +165 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +43 -0
- data/lib/hecks/projections/deploy/box.rb +387 -0
- data/lib/hecks/projections/deploy/fargate/cdn.rb +13 -1
- data/lib/hecks/projections/deploy/fargate.rb +1 -1
- data/lib/hecks/projections/deploy/lambda.rb +11 -10
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +1 -1
- data/lib/hecks/projections/diagrams.rb +2 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +107 -0
- data/lib/hecks/projections/site/edge/checks.rb +137 -0
- data/lib/hecks/projections/site/edge/pattern.rb +50 -0
- data/lib/hecks/projections/site/edge/rules.rb +128 -0
- data/lib/hecks/projections/site/edge.rb +136 -0
- data/lib/hecks/projections/site/regions.rb +44 -0
- data/lib/hecks/projections/site/routes_ts.rb +284 -0
- data/lib/hecks/projections/site/site_cdn.rb +78 -0
- data/lib/hecks/projections/site/table/checks.rb +124 -0
- data/lib/hecks/projections/site/table/row_builder.rb +155 -0
- data/lib/hecks/projections/site/table.rb +98 -0
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/projector/cli_projector.rb +172 -95
- data/lib/hecks/quality_control/adapters/agent.adapter +1 -1
- data/lib/hecks/quality_control/adapters/agent.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +8 -8
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -4
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +6 -5
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +5 -5
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep.rb +10 -9
- data/lib/hecks/quality_control/cli/qa_tick.rb +3 -3
- data/lib/hecks/quality_control/quality_control.bluebook +18 -18
- data/lib/hecks/quality_control/quality_control.hecksagon +1 -1
- data/lib/hecks/runtime/command_interpreter.rb +2 -0
- data/lib/hecks/runtime/interpreting.rb +21 -0
- data/lib/hecks/runtime/invocation.rb +5 -3
- data/lib/hecks/runtime/registry/verification.rb +15 -29
- data/lib/hecks/runtime/registry.rb +14 -20
- data/lib/hecks/rust_build/append_optionals.rb +79 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +2 -1
- data/lib/hecks/rust_build/domain_name.rb +26 -0
- data/lib/hecks/rust_build/kernel_input.rb +58 -0
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +143 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +1 -1
- data/lib/hecks/rust_build/project_rust.rb +57 -24
- data/lib/hecks/rust_build/rust_literal.rb +33 -0
- data/lib/hecks/rust_build/write_if_changed.rb +86 -0
- data/lib/hecks/site/bluebook/site.bluebook +205 -0
- data/lib/hecks/site/bluebook/site.hecksagon +5 -0
- data/lib/hecks/site/bluebook/site.ports.hecksagon +15 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +336 -0
- data/lib/hecks/sme/bluebook/sme.hecksagon +5 -0
- data/lib/hecks/storehouse.rb +37 -7
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +1 -1
- data/lib/hecks/three_zero/forms.yml +88 -88
- data/lib/hecks/tools/ci_gates.rb +1 -1
- data/lib/hecks/tools/deploy_recipe.rb +14 -6
- data/lib/hecks/tools/oidc_manifests.rb +2 -1
- data/lib/hecks/tools/sequence_script.rb +2 -2
- data/lib/hecks/tools/site_routes.rb +152 -0
- data/lib/hecks/tools/tenant_provisioning.rb +1 -1
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/rust/build/src/cargo_sync.rs +1 -1
- data/rust/build/src/pipeline.rs +23 -16
- data/rust/build/src/resolve.rs +39 -6
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +1 -1
- data/rust/host/src/dispatch.rs +87 -7
- data/rust/host/src/needs.rs +111 -0
- data/rust/host/src/web/newsletter/template.rs +69 -0
- data/rust/host/src/web/newsletter/tests.rs +43 -0
- data/rust/host/src/web/newsletter.rs +6 -1
- data/rust/host/src/web/newsletter_send.rs +1 -1
- data/rust/parser/src/keywords.rs +7 -6
- data/rust/parser/src/main.rs +4 -3
- data/rust/parser/src/parse/chapter.rs +1 -1
- data/rust/parser/src/parse/hecksagon.rs +25 -10
- data/rust/project/mutations.rb +4 -30
- data/rust/project/naming.rb +8 -28
- data/rust/project/write_if_changed.rb +4 -80
- data/rust/project_rust_pipeline.rb +16 -12
- data/rust/src/kernel/cli.rs +2 -0
- data/rust/src/kernel/needs.rs +74 -1
- metadata +39 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
class Table
|
|
7
|
+
# One row with its defaults filled in.
|
|
8
|
+
Row = Struct.new(:path, :kind, :render, :auth, :verbs, :cache, :origin, :source,
|
|
9
|
+
:indexable, :switch, :off, :preview, :label, :seo, :redirect_to, :aliases,
|
|
10
|
+
:nav_group, :nav_order, :mobile_order, :footer_column, :footer_order,
|
|
11
|
+
:admin_key, :admin_order, :compress, :alb_rule, :cdn, keyword_init: true)
|
|
12
|
+
|
|
13
|
+
# Turns a declared member into a `Row`: checks its fields and their types, fills the
|
|
14
|
+
# defaults, and checks each closed-set value against the Site chapter's vocabulary.
|
|
15
|
+
class RowBuilder
|
|
16
|
+
# The fields a row may carry, with the Ruby class each takes (`:bool` is true or false).
|
|
17
|
+
FIELDS = { path: String, kind: String, render: String, auth: String, methods: String,
|
|
18
|
+
cache: String, origin: String, source: String, indexable: :bool, switch: String,
|
|
19
|
+
off: :bool, preview: String, label: String, seo: String, redirect_to: String,
|
|
20
|
+
aliases: String, nav_group: String, nav_order: Integer, mobile_order: Integer,
|
|
21
|
+
footer_column: String, footer_order: Integer, admin_key: String,
|
|
22
|
+
admin_order: Integer, compress: :bool, alb_rule: String, cdn: :bool }.freeze
|
|
23
|
+
|
|
24
|
+
# The value objects of the Site chapter's `Route` aggregate whose members are the closed
|
|
25
|
+
# sets, by the row field each constrains.
|
|
26
|
+
VOCABULARY = { kind: "Kind", render: "Render", auth: "Auth", cache: "CacheClass",
|
|
27
|
+
origin: "Origin", preview: "Preview" }.freeze
|
|
28
|
+
|
|
29
|
+
# A source that names a command or query, whose forms-scheme URL is the route's path.
|
|
30
|
+
DERIVED_SOURCE = /\A(command|query):([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)\z/
|
|
31
|
+
NAMED_SOURCE = /\A(global|collection):[a-z0-9_-]+\z/
|
|
32
|
+
|
|
33
|
+
# @param vocabulary [Hash{Symbol => Array<String>}] the closed sets
|
|
34
|
+
# @param registry [Runtime::Registry, nil] where a `command:` or `query:` is looked up
|
|
35
|
+
# @param problems [Array<String>] collects each problem found, one line each
|
|
36
|
+
def initialize(vocabulary:, registry:, problems:)
|
|
37
|
+
@vocabulary = vocabulary
|
|
38
|
+
@registry = registry
|
|
39
|
+
@problems = problems
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @param member [Hash{Symbol => Object}] the declared fields of one row
|
|
43
|
+
# @param index [Integer] the row's position, for a message about a row with no path
|
|
44
|
+
# @return [Row] the row with its defaults filled in; its problems are in `problems`
|
|
45
|
+
def call(member, index)
|
|
46
|
+
label = member[:path] || "row #{index + 1}"
|
|
47
|
+
fields = checked_fields(member, label)
|
|
48
|
+
source = fields[:source] || "none"
|
|
49
|
+
path = fields[:path] || derived_path(source)
|
|
50
|
+
label = path || label
|
|
51
|
+
check_source(label, source)
|
|
52
|
+
verbs = fields.delete(:methods)
|
|
53
|
+
row = Row.new(**fields, verbs: verbs, path: path, source: source,
|
|
54
|
+
kind: fields[:kind] || default_kind(source), auth: fields[:auth] || "public",
|
|
55
|
+
origin: fields[:origin] || default_origin(source))
|
|
56
|
+
fill_defaults(row)
|
|
57
|
+
check_values(row, label)
|
|
58
|
+
problem(label, "needs a path") if row.path.nil?
|
|
59
|
+
row
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def checked_fields(member, label)
|
|
65
|
+
member.each_key do |key|
|
|
66
|
+
problem(label, "has no field #{key}; fields are #{FIELDS.keys.join(', ')}") unless FIELDS.key?(key)
|
|
67
|
+
end
|
|
68
|
+
known = member.slice(*FIELDS.keys)
|
|
69
|
+
known.each { |key, value| check_type(label, key, value) }
|
|
70
|
+
known
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def check_type(label, key, value)
|
|
74
|
+
expected = FIELDS.fetch(key)
|
|
75
|
+
return if expected == :bool ? [true, false].include?(value) : value.is_a?(expected)
|
|
76
|
+
|
|
77
|
+
problem(label, "has #{key} #{value.inspect}; #{key} is #{expected == :bool ? 'true or false' : "a #{expected}"}")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def default_kind(source) = source.match?(DERIVED_SOURCE) ? "endpoint" : "page"
|
|
81
|
+
|
|
82
|
+
def default_origin(source) = source.match?(DERIVED_SOURCE) ? "domain" : "website"
|
|
83
|
+
|
|
84
|
+
# A `command:` or `query:` source leaves the path to the forms scheme,
|
|
85
|
+
# `/Chapter/Aggregate/Verb`.
|
|
86
|
+
def derived_path(source)
|
|
87
|
+
match = DERIVED_SOURCE.match(source) or return nil
|
|
88
|
+
"/#{match[2]}/#{match[3]}/#{match[4]}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def fill_defaults(row)
|
|
92
|
+
row.off = false if row.off.nil?
|
|
93
|
+
row.compress = true if row.compress.nil?
|
|
94
|
+
row.cdn = true if row.cdn.nil?
|
|
95
|
+
row.render ||= row.kind == "page" ? "prerender" : "ssr"
|
|
96
|
+
row.verbs = list(row.verbs || (row.source.start_with?("command:") ? "POST" : "GET"))
|
|
97
|
+
row.aliases = list(row.aliases)
|
|
98
|
+
row.cache ||= default_cache(row)
|
|
99
|
+
row.indexable = indexable_by_default?(row) if row.indexable.nil?
|
|
100
|
+
fill_slots(row)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def fill_slots(row)
|
|
104
|
+
row.preview ||= "public"
|
|
105
|
+
row.switch ||= ""
|
|
106
|
+
row.footer_order ||= 0 if row.footer_column
|
|
107
|
+
row.admin_order ||= 0 if row.admin_key
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def list(text) = text.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
111
|
+
|
|
112
|
+
def default_cache(row)
|
|
113
|
+
return "no_store" if row.auth != "public" || row.kind == "endpoint"
|
|
114
|
+
|
|
115
|
+
row.origin == "assets" ? "immutable" : "page"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def indexable_by_default?(row) = row.kind == "page" && row.auth == "public" && !row.off
|
|
119
|
+
|
|
120
|
+
def check_values(row, label)
|
|
121
|
+
VOCABULARY.each_key do |field|
|
|
122
|
+
next if @vocabulary.fetch(field).include?(row[field])
|
|
123
|
+
|
|
124
|
+
problem(label, "has #{field} #{row[field].inspect}; #{field} is one of #{@vocabulary.fetch(field).join(', ')}")
|
|
125
|
+
end
|
|
126
|
+
row.verbs.each do |verb|
|
|
127
|
+
next if @vocabulary.fetch(:http_method).include?(verb)
|
|
128
|
+
|
|
129
|
+
problem(label, "has method #{verb.inspect}; methods are #{@vocabulary.fetch(:http_method).join(', ')}")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def check_source(label, source)
|
|
134
|
+
match = DERIVED_SOURCE.match(source)
|
|
135
|
+
unless match || source == "none" || source.match?(NAMED_SOURCE)
|
|
136
|
+
problem(label, "has source #{source.inspect}; use none, global:<slug>, collection:<name>, " \
|
|
137
|
+
"command:<Chapter>.<Aggregate>.<Command> or query:<Chapter>.<Aggregate>.<Query>")
|
|
138
|
+
end
|
|
139
|
+
return unless match && @registry
|
|
140
|
+
return if verb_exists?(*match.captures)
|
|
141
|
+
|
|
142
|
+
problem(label, "names #{source}, which no attached chapter declares")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def verb_exists?(word, chapter, aggregate, name)
|
|
146
|
+
found = @registry.bluebook(chapter)&.aggregate(aggregate) or return false
|
|
147
|
+
(word == "command" ? found.commands : found.queries).any? { |verb| verb.hecks_name == name }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def problem(label, text) = @problems << "#{label} #{text}"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "table/row_builder"
|
|
4
|
+
require_relative "table/checks"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Projections
|
|
8
|
+
module Site
|
|
9
|
+
# A client's route table, read from its chapter and checked against the Site chapter's
|
|
10
|
+
# vocabulary.
|
|
11
|
+
#
|
|
12
|
+
# The rows are the `member` lines of a `value_object "Route"` in any chapter of the project
|
|
13
|
+
# (the Site chapter's own `Route` aggregate names the fields and the closed sets). This class
|
|
14
|
+
# reads them, fills each row's defaults (the cache class from the auth and kind, the render
|
|
15
|
+
# from the kind, the path from a `command:` or `query:` source), and refuses a table that
|
|
16
|
+
# contradicts itself, naming every problem at once.
|
|
17
|
+
class Table
|
|
18
|
+
# A table the projection refuses; the message lists each problem on its own line.
|
|
19
|
+
class Invalid < ArgumentError; end
|
|
20
|
+
|
|
21
|
+
# The name of the value object a client declares its rows in.
|
|
22
|
+
ROW_OBJECT = "Route"
|
|
23
|
+
|
|
24
|
+
# @return [Array<Row>] the rows, in the order the client declared them
|
|
25
|
+
attr_reader :rows
|
|
26
|
+
|
|
27
|
+
# Finds the one chapter of a project that declares the route table.
|
|
28
|
+
#
|
|
29
|
+
# @param registry [Runtime::Registry] the registry the project booted into
|
|
30
|
+
# @return [Bluebook::Chapter] the chapter holding the `Route` value object with member rows
|
|
31
|
+
# @raise [Invalid] when no chapter, or more than one, declares one
|
|
32
|
+
def self.chapter(registry)
|
|
33
|
+
holders = registry.bluebooks.values.reject { |chapter| chapter.name == "Site" }
|
|
34
|
+
.select { |chapter| rows_of(chapter).any? }
|
|
35
|
+
raise Invalid, "no chapter declares a value_object \"#{ROW_OBJECT}\" with member rows" if holders.empty?
|
|
36
|
+
return holders.first if holders.one?
|
|
37
|
+
|
|
38
|
+
raise Invalid, "#{holders.size} chapters declare a value_object \"#{ROW_OBJECT}\": " \
|
|
39
|
+
"#{holders.map(&:name).join(', ')}; a project has one route table"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Reads and checks one chapter's route table.
|
|
43
|
+
#
|
|
44
|
+
# @param chapter [Bluebook::Chapter] the chapter that declares the rows
|
|
45
|
+
# @param registry [Runtime::Registry] the registry the project booted into: it holds the
|
|
46
|
+
# Site chapter and, for a `command:` or `query:` source, the command or query
|
|
47
|
+
# @return [Table] the checked table
|
|
48
|
+
# @raise [Invalid] when the Site chapter is not attached or a row is refused
|
|
49
|
+
def self.read(chapter, registry:)
|
|
50
|
+
new(rows_of(chapter), vocabulary: vocabulary(registry), registry: registry)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param registry [Runtime::Registry] a registry holding the Site chapter
|
|
54
|
+
# @return [Hash{Symbol => Array<String>}] each constrained field to its allowed values, and
|
|
55
|
+
# `:http_method` to the verbs a route may answer
|
|
56
|
+
# @raise [Invalid] when the registry does not hold the Site chapter
|
|
57
|
+
def self.vocabulary(registry)
|
|
58
|
+
site = registry.bluebook("Site") or
|
|
59
|
+
raise Invalid, "the project does not attach the Site chapter (attaches \"Site\")"
|
|
60
|
+
route = site.aggregate("Route")
|
|
61
|
+
members = lambda do |name|
|
|
62
|
+
route.value_objects.find { |object| object.hecks_name == name }.members.map { |member| member.fetch(:value) }
|
|
63
|
+
end
|
|
64
|
+
RowBuilder::VOCABULARY.transform_values(&members).merge(http_method: members.call("HttpMethod"))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @param chapter [Bluebook::Chapter] a chapter to search
|
|
68
|
+
# @param object [String] the name of the value object whose rows are wanted
|
|
69
|
+
# @return [Array<Hash{Symbol => Object}>] the member rows of its value objects of that name
|
|
70
|
+
def self.rows_of(chapter, object = ROW_OBJECT)
|
|
71
|
+
chapter.aggregates.flat_map(&:value_objects)
|
|
72
|
+
.select { |candidate| candidate.hecks_name == object }.flat_map(&:members)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @param members [Array<Hash{Symbol => Object}>] the declared member rows
|
|
76
|
+
# @param vocabulary [Hash{Symbol => Array<String>}] the closed sets
|
|
77
|
+
# @param registry [Runtime::Registry, nil] where a `command:` or `query:` source is looked
|
|
78
|
+
# up; nil skips that check
|
|
79
|
+
# @raise [Invalid] when a row is refused
|
|
80
|
+
def initialize(members, vocabulary:, registry: nil)
|
|
81
|
+
problems = []
|
|
82
|
+
builder = RowBuilder.new(vocabulary: vocabulary, registry: registry, problems: problems)
|
|
83
|
+
@rows = members.each_with_index.map { |member, index| builder.call(member, index) }
|
|
84
|
+
Checks.new(@rows, problems).call
|
|
85
|
+
return if problems.empty?
|
|
86
|
+
|
|
87
|
+
raise Invalid, "the route table is refused:\n#{problems.map { |line| " - #{line}" }.join("\n")}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The navigation slots a row sits in.
|
|
91
|
+
#
|
|
92
|
+
# @param row [Row] a row
|
|
93
|
+
# @return [Array<String>] some of `desktop`, `mobile`, `footer`, `admin`
|
|
94
|
+
def navigation(row) = Checks.navigation(row)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/hecks/projections.rb
CHANGED
|
@@ -22,5 +22,7 @@ require_relative "projections/glossary"
|
|
|
22
22
|
require_relative "projections/deploy/shared"
|
|
23
23
|
require_relative "projections/deploy/lambda"
|
|
24
24
|
require_relative "projections/deploy/fargate"
|
|
25
|
+
require_relative "projections/deploy/box"
|
|
25
26
|
require_relative "projections/deploy/smoke"
|
|
26
27
|
require_relative "projections/deploy/template_diff"
|
|
28
|
+
require_relative "projections/site/routes_ts"
|