hecks 3.13.0 → 3.14.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/pizzas.bluebook +4 -4
  3. data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
  4. data/lib/hecks/bluebook/named_pattern.rb +33 -0
  5. data/lib/hecks/bluebook.rb +1 -0
  6. data/lib/hecks/cli/domain_stub.rb +1 -1
  7. data/lib/hecks/cli/interview_aggregate.rb +4 -4
  8. data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -34
  9. data/lib/hecks/framework/bluebook/change.bluebook +2 -2
  10. data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
  11. data/lib/hecks/hecks/codebase.bluebook +37 -37
  12. data/lib/hecks/hecks/custodian.bluebook +14 -14
  13. data/lib/hecks/hecks/hecks.bluebook +2 -2
  14. data/lib/hecks/hecks/rehearsal.bluebook +3 -3
  15. data/lib/hecks/hecks/tooling.bluebook +22 -22
  16. data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
  17. data/lib/hecks/language/bluebook/command.bluebook +1 -0
  18. data/lib/hecks/language/bluebook/entity.bluebook +1 -0
  19. data/lib/hecks/language/bluebook/query.bluebook +1 -0
  20. data/lib/hecks/language/bluebook/shape.bluebook +8 -7
  21. data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
  22. data/lib/hecks/quality_control/quality_control.bluebook +51 -51
  23. data/lib/hecks/site/bluebook/site.bluebook +5 -5
  24. data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
  25. data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
  26. data/lib/hecks/version.rb +1 -1
  27. data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → 1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal} +0 -0
  28. data/prebuilt/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal} +0 -0
  29. data/prebuilt/verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json +1 -0
  30. data/rust/host/HECKS_RELEASE +1 -1
  31. data/rust/host/src/payments/roles.rs +159 -0
  32. data/rust/host/src/payments/tests.rs +25 -0
  33. data/rust/host/src/payments.rs +20 -11
  34. data/rust/parser/src/build/mod.rs +1 -0
  35. data/rust/parser/src/build/named_pattern.rs +23 -0
  36. data/rust/parser/src/keywords.rs +6 -0
  37. data/rust/parser/src/parse/mod.rs +22 -1
  38. metadata +7 -4
  39. data/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1a1eacc3bae3f940928e23269c95e0306f291ec5a2b6d62268e254f12f1902e
4
- data.tar.gz: e4168d9e617dbad8912a65dbe10be8a3a32a6a08b18fd84542a05fa9803aeaef
3
+ metadata.gz: 52208049da300a9004b163c5e48cfe5587711ad45a3cb4d17a5bd1aa4eca8567
4
+ data.tar.gz: 1788cde0c4a89814395a4be9b3396cc8b5fd823aa408da88534aae617afbd0f1
5
5
  SHA512:
6
- metadata.gz: a49c8992b247138d92232d00123dd73986be81040bb1356fc912f97905644bbb35a38af431a1d81475b9ce07c644ab8c2aab3370c3fabaf685c0f8a93a16bd70
7
- data.tar.gz: 48ccea615a818989da2a01babb5c9d344a5a7c94fbe2eea47301a1437f29aa671134011a626f317c128bcaf1aaa912a21f056e30c8b133643123df129aa7a470
6
+ metadata.gz: 5ad958aea6cbf148bf0164808eae59179ec03743f1591f193f85e196c9aa7c5a25376d9857ad3d42d57303c7af721811cddf11f776c88f48914f46c4522edc87
7
+ data.tar.gz: d1148d6aa2ced591c069816c3f17047d08d0aa902e03340dbd4b7c35c20be8eb0907bda11f00c746fdc8955ae0a9225aafe43e3b92715efb42df0f0d73c3b0a8
@@ -14,7 +14,7 @@ Hecks.bluebook "Pizzas" do
14
14
  identified_by :name
15
15
 
16
16
  value_object "PizzaName" do
17
- attribute :value, String, pattern: '[^ \t\n\r]'
17
+ attribute :value, String, matches: :present
18
18
 
19
19
  invariant("a pizza is named") { !value.to_s.empty? }
20
20
  end
@@ -26,13 +26,13 @@ Hecks.bluebook "Pizzas" do
26
26
  end
27
27
 
28
28
  value_object "CustomerName" do
29
- attribute :value, String, pattern: '[^ \t\n\r]'
29
+ attribute :value, String, matches: :present
30
30
 
31
31
  invariant("a customer is named") { !value.to_s.empty? }
32
32
  end
33
33
 
34
34
  value_object "ToppingName" do
35
- attribute :value, String, pattern: '[^ \t\n\r]'
35
+ attribute :value, String, matches: :present
36
36
 
37
37
  invariant("a topping is named") { !value.to_s.empty? }
38
38
  # The pattern lets a form feed through; `.strip` is what sees past it.
@@ -46,7 +46,7 @@ Hecks.bluebook "Pizzas" do
46
46
  end
47
47
 
48
48
  value_object "Topping" do
49
- attribute :name, String, pattern: '[^ \t\n\r]'
49
+ attribute :name, String, matches: :present
50
50
  attribute :amount, Integer
51
51
  end
52
52
 
@@ -17,7 +17,7 @@ module Hecks
17
17
  private_constant :UNSET
18
18
 
19
19
  ATTRIBUTE_FIELD_OPTIONS = %i[default optional pattern admits].freeze
20
- ATTRIBUTE_OPTIONS = [*ATTRIBUTE_FIELD_OPTIONS, :one_of].freeze
20
+ ATTRIBUTE_OPTIONS = [*ATTRIBUTE_FIELD_OPTIONS, :one_of, :matches].freeze
21
21
  private_constant :ATTRIBUTE_FIELD_OPTIONS, :ATTRIBUTE_OPTIONS
22
22
 
23
23
  # Returns the attributes declared so far, minting the accumulating list on first use.
@@ -34,12 +34,12 @@ module Hecks
34
34
  # Declares one field on the owning construct; the type is a bare constant (ADR 0025).
35
35
  #
36
36
  # attribute :op, String, admits: "Vocabulary::QueryComparator"
37
- # `admits:` names an already-declared closed set as qualified text, checked on read.
38
- # `one_of:` declares an inline closed set, meaningful only inside a `value_object`.
37
+ # `admits:` names a declared closed set as qualified text. `one_of:` declares an inline one.
39
38
  #
40
39
  # @param name [Symbol] the field's name
41
40
  # @param type [Module, ListOf, OneOf] a bare constant or a wrapper
42
- # @param options [Hash] `default:`, `optional:`, `pattern:`, `admits:` or `one_of:`
41
+ # @param options [Hash] `default:`, `optional:`, `pattern:`, `matches:` (a `NamedPattern`),
42
+ # `admits:` or `one_of:`
43
43
  # @return [void]
44
44
  # @raise [Malformed] on a duplicate name, a missing or quoted type, a disallowed pattern
45
45
  # @raise [ArgumentError] on any other keyword
@@ -86,7 +86,17 @@ module Hecks
86
86
  raise ArgumentError, "unknown keyword#{"s" if unknown.size > 1}: #{unknown.map(&:inspect).join(", ")}"
87
87
  end
88
88
 
89
- { default: nil, optional: false, pattern: nil, admits: nil, one_of: nil }.merge(options)
89
+ given = { default: nil, optional: false, pattern: nil, admits: nil, one_of: nil }.merge(options)
90
+ expand_matches(given)
91
+ end
92
+
93
+ # `matches: :name` stands for the pattern the language gives that name.
94
+ def expand_matches(given)
95
+ matches = given.delete(:matches)
96
+ return given unless matches
97
+ raise ArgumentError, "matches: and pattern: both state the pattern" if given[:pattern]
98
+
99
+ given.merge(pattern: NamedPattern.source(matches))
90
100
  end
91
101
 
92
102
  def typed_attribute(name, type, field_options)
@@ -0,0 +1,33 @@
1
+ module Hecks
2
+ module Bluebook
3
+ # The patterns a bluebook names instead of spelling: `matches: :snake_name` says what
4
+ # `pattern: '^[a-z][a-z0-9_]*$'` does. A name stands for exactly its regex, so the IR, the
5
+ # generated Rust and the runtime check see the pattern and never the name.
6
+ #
7
+ # `rust/parser/src/build/named_pattern.rs` holds the same table; spec/named_pattern_spec.rb
8
+ # holds the two to each other.
9
+ module NamedPattern
10
+ # Name => the regex source it stands for, each the exact source the corpus spelled by hand.
11
+ TABLE = {
12
+ present: '[^ \t\n\r]',
13
+ not_empty: '[^\t\n\r]',
14
+ no_whitespace: '^[^ \t\n\r]+$',
15
+ snake_name: "^[a-z][a-z0-9_]*$",
16
+ git_sha: "^[0-9a-fA-F]{7,40}$",
17
+ digits: "^[0-9]+$"
18
+ }.freeze
19
+
20
+ module_function
21
+
22
+ # @param name [Symbol, String] a name in `TABLE`
23
+ # @return [String] the regex source it stands for
24
+ # @raise [ArgumentError] when the name is not one the language gives
25
+ def source(name)
26
+ TABLE.fetch(name.to_sym) do
27
+ raise ArgumentError, "matches: #{name.inspect} is not a named pattern — " \
28
+ "the names are #{TABLE.keys.map(&:inspect).join(", ")}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -44,6 +44,7 @@ require_relative "bluebook/project_loader"
44
44
 
45
45
  require_relative "bluebook/dsl"
46
46
  require_relative "bluebook/pattern_subset"
47
+ require_relative "bluebook/named_pattern"
47
48
 
48
49
  require_relative "bluebook/meta_validator"
49
50
  require_relative "bluebook/meta_validator/plan"
@@ -96,7 +96,7 @@ module Hecks
96
96
  attribute :name, ExampleName
97
97
 
98
98
  value_object "ExampleName" do
99
- attribute :value, String, pattern: '[^ \\t\\n\\r]'
99
+ attribute :value, String, matches: :present
100
100
  end
101
101
 
102
102
  # TODO: replace Create with the first thing that happens to an Example.
@@ -6,8 +6,8 @@ module Hecks
6
6
  # The bluebook text of one accepted thing: its aggregate, with its value objects, commands and
7
7
  # lifecycle. `InterviewDraft` extends it, and supplies the naming and citing helpers it calls.
8
8
  module InterviewAggregate
9
- # The pattern every generated string field carries: it refuses a blank value.
10
- PATTERN = %q('[^ \t\n\r]').freeze
9
+ # The option every generated string field carries: it refuses a blank value.
10
+ PRESENT = "matches: :present".freeze
11
11
 
12
12
  # What stands in for a creating command nothing in the interview named.
13
13
  STUB_NOTE = " # TODO: no action that creates was accepted; replace Create with the real one.".freeze
@@ -15,7 +15,7 @@ module Hecks
15
15
  # One accepted thing: what it is called and identified by, its fields, its transitions, the
16
16
  # actions that create it and the others.
17
17
  Shape = Struct.new(:name, :identifier, :type, :fields, :steps, :creating, :rest, keyword_init: true)
18
- private_constant :PATTERN, :STUB_NOTE, :Shape
18
+ private_constant :PRESENT, :STUB_NOTE, :Shape
19
19
 
20
20
  # @api private
21
21
  def aggregate(interview, thing, actions)
@@ -66,7 +66,7 @@ module Hecks
66
66
  # @api private
67
67
  def type_lines(shape)
68
68
  [" value_object #{shape.type.inspect} do",
69
- " attribute :value, String, pattern: #{PATTERN}", " end",
69
+ " attribute :value, String, #{PRESENT}", " end",
70
70
  *InterviewShape.value_objects(shape.fields)]
71
71
  end
72
72
 
@@ -451,7 +451,7 @@ Hecks.bluebook "Deploy" do
451
451
 
452
452
  # The domain's own directory on disk, where `environments/<slug>.world` is written.
453
453
  value_object "DirectoryPath" do
454
- attribute :value, String, pattern: '[^\t\n\r]'
454
+ attribute :value, String, matches: :not_empty
455
455
  end
456
456
 
457
457
  value_object "Report" do
@@ -581,14 +581,14 @@ Hecks.bluebook "Deploy" do
581
581
  identified_by :run
582
582
 
583
583
  value_object "RunKey" do
584
- attribute :value, String, pattern: '[^ \t\n\r]'
584
+ attribute :value, String, matches: :present
585
585
 
586
586
  invariant("a run is named") { !value.to_s.empty? }
587
587
  end
588
588
 
589
589
  # A domain directory, whose `bluebook/` holds the `.world` that declares `deployed_to`.
590
590
  value_object "DomainPath" do
591
- attribute :value, String, pattern: '[^\t\n\r]'
591
+ attribute :value, String, matches: :not_empty
592
592
 
593
593
  invariant("a domain is named by its path") { !value.to_s.empty? }
594
594
  end
@@ -604,7 +604,7 @@ Hecks.bluebook "Deploy" do
604
604
 
605
605
  # Where the recipe is written, for a domain that lives in a client's repository.
606
606
  value_object "Location" do
607
- attribute :value, String, pattern: '[^\t\n\r]'
607
+ attribute :value, String, matches: :not_empty
608
608
  end
609
609
 
610
610
  # An `environments/<name>.world` overlay of the domain, which must exist.
@@ -707,14 +707,14 @@ Hecks.bluebook "Deploy" do
707
707
  identified_by :run
708
708
 
709
709
  value_object "RunKey" do
710
- attribute :value, String, pattern: '[^ \t\n\r]'
710
+ attribute :value, String, matches: :present
711
711
 
712
712
  invariant("a run is named") { !value.to_s.empty? }
713
713
  end
714
714
 
715
715
  # A domain directory, whose `bluebook/` holds the `.world` that declares `deployed_to`.
716
716
  value_object "DomainPath" do
717
- attribute :value, String, pattern: '[^\t\n\r]'
717
+ attribute :value, String, matches: :not_empty
718
718
 
719
719
  invariant("a domain is named by its path") { !value.to_s.empty? }
720
720
  end
@@ -722,7 +722,7 @@ Hecks.bluebook "Deploy" do
722
722
  # A directory the cleared worlds are written to, at their paths below `bluebook/`, instead of
723
723
  # over the originals.
724
724
  value_object "Location" do
725
- attribute :value, String, pattern: '[^\t\n\r]'
725
+ attribute :value, String, matches: :not_empty
726
726
  end
727
727
 
728
728
  value_object "Report" do
@@ -813,14 +813,14 @@ Hecks.bluebook "Deploy" do
813
813
  identified_by :run
814
814
 
815
815
  value_object "RunKey" do
816
- attribute :value, String, pattern: '[^ \t\n\r]'
816
+ attribute :value, String, matches: :present
817
817
 
818
818
  invariant("a run is named") { !value.to_s.empty? }
819
819
  end
820
820
 
821
821
  # Makefile paths, comma separated; three generated fixture domains when absent.
822
822
  value_object "PathList" do
823
- attribute :value, String, pattern: '[^\t\n\r]'
823
+ attribute :value, String, matches: :not_empty
824
824
  end
825
825
 
826
826
  value_object "Report" do
@@ -909,7 +909,7 @@ Hecks.bluebook "Deploy" do
909
909
  identified_by :run
910
910
 
911
911
  value_object "RunKey" do
912
- attribute :value, String, pattern: '[^ \t\n\r]'
912
+ attribute :value, String, matches: :present
913
913
 
914
914
  invariant("a run is named") { !value.to_s.empty? }
915
915
  end
@@ -1020,13 +1020,13 @@ Hecks.bluebook "Deploy" do
1020
1020
  identified_by :run
1021
1021
 
1022
1022
  value_object "RunKey" do
1023
- attribute :value, String, pattern: '[^ \t\n\r]'
1023
+ attribute :value, String, matches: :present
1024
1024
 
1025
1025
  invariant("a run is named") { !value.to_s.empty? }
1026
1026
  end
1027
1027
 
1028
1028
  value_object "Location" do
1029
- attribute :value, String, pattern: '[^\t\n\r]'
1029
+ attribute :value, String, matches: :not_empty
1030
1030
 
1031
1031
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1032
1032
  end
@@ -1159,13 +1159,13 @@ Hecks.bluebook "Deploy" do
1159
1159
  identified_by :run
1160
1160
 
1161
1161
  value_object "RunKey" do
1162
- attribute :value, String, pattern: '[^ \t\n\r]'
1162
+ attribute :value, String, matches: :present
1163
1163
 
1164
1164
  invariant("a run is named") { !value.to_s.empty? }
1165
1165
  end
1166
1166
 
1167
1167
  value_object "Location" do
1168
- attribute :value, String, pattern: '[^\t\n\r]'
1168
+ attribute :value, String, matches: :not_empty
1169
1169
 
1170
1170
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1171
1171
  end
@@ -1187,7 +1187,7 @@ Hecks.bluebook "Deploy" do
1187
1187
 
1188
1188
  # A local image reference to push, as the script's LOCAL_IMAGE takes it.
1189
1189
  value_object "ImageName" do
1190
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
1190
+ attribute :value, String, matches: :no_whitespace
1191
1191
  end
1192
1192
 
1193
1193
  value_object "Switch" do
@@ -1352,13 +1352,13 @@ Hecks.bluebook "Deploy" do
1352
1352
  identified_by :run
1353
1353
 
1354
1354
  value_object "RunKey" do
1355
- attribute :value, String, pattern: '[^ \t\n\r]'
1355
+ attribute :value, String, matches: :present
1356
1356
 
1357
1357
  invariant("a run is named") { !value.to_s.empty? }
1358
1358
  end
1359
1359
 
1360
1360
  value_object "Location" do
1361
- attribute :value, String, pattern: '[^\t\n\r]'
1361
+ attribute :value, String, matches: :not_empty
1362
1362
 
1363
1363
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1364
1364
  end
@@ -1538,13 +1538,13 @@ Hecks.bluebook "Deploy" do
1538
1538
  identified_by :run
1539
1539
 
1540
1540
  value_object "RunKey" do
1541
- attribute :value, String, pattern: '[^ \t\n\r]'
1541
+ attribute :value, String, matches: :present
1542
1542
 
1543
1543
  invariant("a run is named") { !value.to_s.empty? }
1544
1544
  end
1545
1545
 
1546
1546
  value_object "Location" do
1547
- attribute :value, String, pattern: '[^\t\n\r]'
1547
+ attribute :value, String, matches: :not_empty
1548
1548
 
1549
1549
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1550
1550
  end
@@ -1560,7 +1560,7 @@ Hecks.bluebook "Deploy" do
1560
1560
 
1561
1561
  # A Secrets Manager id or ARN; the script reads the password from it, so no value travels here.
1562
1562
  value_object "Secret" do
1563
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
1563
+ attribute :value, String, matches: :no_whitespace
1564
1564
  end
1565
1565
 
1566
1566
  # A database's name; empty leaves it to the generated script's own default.
@@ -1826,13 +1826,13 @@ Hecks.bluebook "Deploy" do
1826
1826
  identified_by :run
1827
1827
 
1828
1828
  value_object "RunKey" do
1829
- attribute :value, String, pattern: '[^ \t\n\r]'
1829
+ attribute :value, String, matches: :present
1830
1830
 
1831
1831
  invariant("a run is named") { !value.to_s.empty? }
1832
1832
  end
1833
1833
 
1834
1834
  value_object "Location" do
1835
- attribute :value, String, pattern: '[^\t\n\r]'
1835
+ attribute :value, String, matches: :not_empty
1836
1836
 
1837
1837
  invariant("a project or script is named by its path") { !value.to_s.empty? }
1838
1838
  end
@@ -1846,7 +1846,7 @@ Hecks.bluebook "Deploy" do
1846
1846
  end
1847
1847
 
1848
1848
  value_object "Secret" do
1849
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
1849
+ attribute :value, String, matches: :no_whitespace
1850
1850
  end
1851
1851
 
1852
1852
  # A database's name; empty leaves it to the generated script's own default.
@@ -1984,13 +1984,13 @@ Hecks.bluebook "Deploy" do
1984
1984
  identified_by :run
1985
1985
 
1986
1986
  value_object "RunKey" do
1987
- attribute :value, String, pattern: '[^ \t\n\r]'
1987
+ attribute :value, String, matches: :present
1988
1988
 
1989
1989
  invariant("a run is named") { !value.to_s.empty? }
1990
1990
  end
1991
1991
 
1992
1992
  value_object "Location" do
1993
- attribute :value, String, pattern: '[^\t\n\r]'
1993
+ attribute :value, String, matches: :not_empty
1994
1994
 
1995
1995
  invariant("a project, script or file is named by its path") { !value.to_s.empty? }
1996
1996
  end
@@ -2134,13 +2134,13 @@ Hecks.bluebook "Deploy" do
2134
2134
  identified_by :run
2135
2135
 
2136
2136
  value_object "RunKey" do
2137
- attribute :value, String, pattern: '[^ \t\n\r]'
2137
+ attribute :value, String, matches: :present
2138
2138
 
2139
2139
  invariant("a run is named") { !value.to_s.empty? }
2140
2140
  end
2141
2141
 
2142
2142
  value_object "Location" do
2143
- attribute :value, String, pattern: '[^\t\n\r]'
2143
+ attribute :value, String, matches: :not_empty
2144
2144
 
2145
2145
  invariant("a project or script is named by its path") { !value.to_s.empty? }
2146
2146
  end
@@ -2152,7 +2152,7 @@ Hecks.bluebook "Deploy" do
2152
2152
 
2153
2153
  # A git branch name. A preview is never of a production branch.
2154
2154
  value_object "Branch" do
2155
- attribute :value, String, pattern: '^[^ \t\n\r]+$'
2155
+ attribute :value, String, matches: :no_whitespace
2156
2156
 
2157
2157
  invariant("a preview is never of main or master") { value != "main" && value != "master" }
2158
2158
  end
@@ -2510,13 +2510,13 @@ Hecks.bluebook "Deploy" do
2510
2510
  identified_by :run
2511
2511
 
2512
2512
  value_object "RunKey" do
2513
- attribute :value, String, pattern: '[^ \t\n\r]'
2513
+ attribute :value, String, matches: :present
2514
2514
 
2515
2515
  invariant("a run is named") { !value.to_s.empty? }
2516
2516
  end
2517
2517
 
2518
2518
  value_object "Location" do
2519
- attribute :value, String, pattern: '[^\t\n\r]'
2519
+ attribute :value, String, matches: :not_empty
2520
2520
 
2521
2521
  invariant("a project or script is named by its path") { !value.to_s.empty? }
2522
2522
  end
@@ -2656,13 +2656,13 @@ Hecks.bluebook "Deploy" do
2656
2656
  identified_by :run
2657
2657
 
2658
2658
  value_object "RunKey" do
2659
- attribute :value, String, pattern: '[^ \t\n\r]'
2659
+ attribute :value, String, matches: :present
2660
2660
 
2661
2661
  invariant("a run is named") { !value.to_s.empty? }
2662
2662
  end
2663
2663
 
2664
2664
  value_object "Location" do
2665
- attribute :value, String, pattern: '[^\t\n\r]'
2665
+ attribute :value, String, matches: :not_empty
2666
2666
 
2667
2667
  invariant("a template is named by its path") { !value.to_s.empty? }
2668
2668
  end
@@ -2785,14 +2785,14 @@ Hecks.bluebook "Deploy" do
2785
2785
  identified_by :run
2786
2786
 
2787
2787
  value_object "RunKey" do
2788
- attribute :value, String, pattern: '[^ \t\n\r]'
2788
+ attribute :value, String, matches: :present
2789
2789
 
2790
2790
  invariant("a run is named") { !value.to_s.empty? }
2791
2791
  end
2792
2792
 
2793
2793
  # Domain directories, comma separated; every domain of the checkout when absent.
2794
2794
  value_object "DomainList" do
2795
- attribute :value, String, pattern: '[^\t\n\r]'
2795
+ attribute :value, String, matches: :not_empty
2796
2796
  end
2797
2797
 
2798
2798
  value_object "Report" do
@@ -18,14 +18,14 @@ Hecks.bluebook "Change" do
18
18
  identified_by :run
19
19
 
20
20
  value_object "RunKey" do
21
- attribute :value, String, pattern: '[^ \t\n\r]'
21
+ attribute :value, String, matches: :present
22
22
 
23
23
  invariant("a change is named") { !value.to_s.empty? }
24
24
  end
25
25
 
26
26
  # The name the script is registered under with the adapter.
27
27
  value_object "ScriptName" do
28
- attribute :value, String, pattern: '[^ \t\n\r]'
28
+ attribute :value, String, matches: :present
29
29
 
30
30
  invariant("a change names its script") { !value.to_s.empty? }
31
31
  end
@@ -19,7 +19,7 @@ module Hecks
19
19
  end
20
20
 
21
21
  def string_body(name)
22
- ["attribute :value, String, pattern: '[^ \\t\\n\\r]'",
22
+ ["attribute :value, String, matches: :present",
23
23
  "invariant(#{"a #{name} is not blank".inspect}) { !value.to_s.empty? }"]
24
24
  end
25
25