nrser 0.3.9 → 0.3.10

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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser/char/alpha_numeric_sub.rb +9 -19
  3. data/lib/nrser/char/special.rb +5 -5
  4. data/lib/nrser/core_ext/array.rb +36 -13
  5. data/lib/nrser/core_ext/enumerable.rb +1 -0
  6. data/lib/nrser/core_ext/enumerable/find_map.rb +1 -1
  7. data/lib/nrser/core_ext/hash/bury.rb +3 -0
  8. data/lib/nrser/core_ext/hash/extract_values_at.rb +2 -2
  9. data/lib/nrser/core_ext/method/full_name.rb +1 -1
  10. data/lib/nrser/core_ext/module/method_objects.rb +1 -1
  11. data/lib/nrser/core_ext/module/source_locations.rb +27 -15
  12. data/lib/nrser/core_ext/object/lazy_var.rb +1 -1
  13. data/lib/nrser/core_ext/pathname.rb +67 -12
  14. data/lib/nrser/core_ext/pathname/subpath.rb +86 -0
  15. data/lib/nrser/core_ext/string.rb +28 -1
  16. data/lib/nrser/core_ext/symbol.rb +11 -12
  17. data/lib/nrser/errors/README.md +154 -0
  18. data/lib/nrser/errors/attr_error.rb +146 -53
  19. data/lib/nrser/errors/count_error.rb +61 -12
  20. data/lib/nrser/errors/nicer_error.rb +42 -71
  21. data/lib/nrser/errors/value_error.rb +53 -58
  22. data/lib/nrser/functions.rb +0 -2
  23. data/lib/nrser/functions/enumerable.rb +5 -17
  24. data/lib/nrser/functions/enumerable/associate.rb +14 -5
  25. data/lib/nrser/functions/enumerable/find_all_map.rb +1 -1
  26. data/lib/nrser/functions/enumerable/include_slice/array_include_slice.rb +1 -1
  27. data/lib/nrser/functions/hash/bury.rb +2 -12
  28. data/lib/nrser/functions/merge_by.rb +2 -2
  29. data/lib/nrser/functions/module/method_objects.rb +2 -2
  30. data/lib/nrser/functions/path.rb +185 -165
  31. data/lib/nrser/functions/path/normalized.rb +84 -0
  32. data/lib/nrser/functions/string.rb +4 -4
  33. data/lib/nrser/functions/text/README.md +4 -0
  34. data/lib/nrser/functions/text/format.rb +53 -0
  35. data/lib/nrser/functions/text/indentation.rb +6 -6
  36. data/lib/nrser/functions/text/word_wrap.rb +2 -2
  37. data/lib/nrser/functions/tree/map_leaves.rb +3 -3
  38. data/lib/nrser/functions/tree/map_tree.rb +2 -2
  39. data/lib/nrser/functions/tree/transform.rb +1 -18
  40. data/lib/nrser/gem_ext/README.md +4 -0
  41. data/lib/nrser/labs/README.md +8 -0
  42. data/lib/nrser/labs/config.rb +163 -0
  43. data/lib/nrser/labs/i8.rb +49 -159
  44. data/lib/nrser/labs/i8/struct.rb +167 -0
  45. data/lib/nrser/labs/i8/struct/hash.rb +140 -0
  46. data/lib/nrser/labs/i8/struct/vector.rb +149 -0
  47. data/lib/nrser/labs/i8/surjection.rb +211 -0
  48. data/lib/nrser/labs/lots/consumer.rb +19 -0
  49. data/lib/nrser/labs/lots/parser.rb +21 -1
  50. data/lib/nrser/labs/stash.rb +4 -4
  51. data/lib/nrser/log.rb +25 -21
  52. data/lib/nrser/log/appender/sync.rb +15 -11
  53. data/lib/nrser/log/formatters/color.rb +0 -3
  54. data/lib/nrser/log/formatters/mixin.rb +4 -4
  55. data/lib/nrser/log/logger.rb +54 -6
  56. data/lib/nrser/log/mixin.rb +2 -1
  57. data/lib/nrser/log/plugin.rb +6 -6
  58. data/lib/nrser/log/types.rb +46 -29
  59. data/lib/nrser/mean_streak.rb +0 -8
  60. data/lib/nrser/mean_streak/document.rb +1 -4
  61. data/lib/nrser/message.rb +3 -3
  62. data/lib/nrser/meta/README.md +4 -0
  63. data/lib/nrser/meta/lazy_attr.rb +2 -2
  64. data/lib/nrser/meta/source/location.rb +1 -1
  65. data/lib/nrser/props.rb +34 -3
  66. data/lib/nrser/props/class_methods.rb +2 -1
  67. data/lib/nrser/props/instance_methods.rb +9 -9
  68. data/lib/nrser/props/metadata.rb +4 -12
  69. data/lib/nrser/props/mutable/stash.rb +5 -2
  70. data/lib/nrser/props/prop.rb +10 -19
  71. data/lib/nrser/rspex.rb +1 -20
  72. data/lib/nrser/rspex/example_group/describe_attribute.rb +3 -0
  73. data/lib/nrser/rspex/example_group/describe_called_with.rb +9 -4
  74. data/lib/nrser/rspex/example_group/describe_case.rb +1 -0
  75. data/lib/nrser/rspex/example_group/describe_class.rb +2 -0
  76. data/lib/nrser/rspex/example_group/describe_group.rb +1 -1
  77. data/lib/nrser/rspex/example_group/describe_instance.rb +3 -1
  78. data/lib/nrser/rspex/example_group/describe_message.rb +1 -1
  79. data/lib/nrser/rspex/example_group/describe_method.rb +64 -30
  80. data/lib/nrser/rspex/example_group/describe_response_to.rb +1 -1
  81. data/lib/nrser/rspex/example_group/describe_section.rb +4 -1
  82. data/lib/nrser/rspex/example_group/describe_sent_to.rb +1 -1
  83. data/lib/nrser/rspex/example_group/describe_setup.rb +1 -0
  84. data/lib/nrser/rspex/example_group/describe_source_file.rb +1 -1
  85. data/lib/nrser/rspex/example_group/describe_spec_file.rb +4 -2
  86. data/lib/nrser/rspex/example_group/describe_when.rb +2 -1
  87. data/lib/nrser/rspex/example_group/describe_x.rb +5 -5
  88. data/lib/nrser/rspex/format.rb +0 -15
  89. data/lib/nrser/sugar/method_missing_forwarder.rb +3 -3
  90. data/lib/nrser/sys/env/path.rb +2 -28
  91. data/lib/nrser/types.rb +63 -12
  92. data/lib/nrser/types/README.md +76 -0
  93. data/lib/nrser/types/arrays.rb +192 -137
  94. data/lib/nrser/types/attributes.rb +269 -0
  95. data/lib/nrser/types/booleans.rb +134 -83
  96. data/lib/nrser/types/bounded.rb +110 -47
  97. data/lib/nrser/types/collections.rb +119 -0
  98. data/lib/nrser/types/combinators.rb +283 -196
  99. data/lib/nrser/types/doc/display_table.md +66 -0
  100. data/lib/nrser/types/eqiuvalent.rb +91 -0
  101. data/lib/nrser/types/errors/check_error.rb +5 -11
  102. data/lib/nrser/types/errors/from_string_error.rb +3 -3
  103. data/lib/nrser/types/factory.rb +287 -20
  104. data/lib/nrser/types/hashes.rb +227 -179
  105. data/lib/nrser/types/in.rb +73 -36
  106. data/lib/nrser/types/is.rb +67 -60
  107. data/lib/nrser/types/is_a.rb +141 -84
  108. data/lib/nrser/types/labels.rb +45 -16
  109. data/lib/nrser/types/maybe.rb +6 -3
  110. data/lib/nrser/types/nil.rb +64 -27
  111. data/lib/nrser/types/not.rb +92 -34
  112. data/lib/nrser/types/numbers.rb +224 -169
  113. data/lib/nrser/types/pairs.rb +113 -89
  114. data/lib/nrser/types/paths.rb +250 -137
  115. data/lib/nrser/types/responds.rb +167 -89
  116. data/lib/nrser/types/selector.rb +234 -0
  117. data/lib/nrser/types/shape.rb +136 -65
  118. data/lib/nrser/types/strings.rb +189 -63
  119. data/lib/nrser/types/symbols.rb +83 -33
  120. data/lib/nrser/types/top.rb +89 -0
  121. data/lib/nrser/types/tuples.rb +134 -98
  122. data/lib/nrser/types/type.rb +617 -505
  123. data/lib/nrser/types/when.rb +123 -98
  124. data/lib/nrser/types/where.rb +182 -91
  125. data/lib/nrser/version.rb +1 -1
  126. data/spec/lib/nrser/core_ext/pathname/subpath_spec.rb +22 -0
  127. data/spec/lib/nrser/errors/attr_error_spec.rb +68 -0
  128. data/spec/lib/nrser/errors/count_error_spec.rb +69 -0
  129. data/spec/lib/nrser/functions/path/normalize_path_spec.rb +35 -0
  130. data/spec/lib/nrser/functions/tree/map_tree_spec.rb +74 -96
  131. data/spec/lib/nrser/functions/tree/transform_spec.rb +11 -11
  132. data/spec/lib/nrser/labs/config_spec.rb +22 -0
  133. data/spec/lib/nrser/labs/i8/struct_spec.rb +39 -0
  134. data/spec/lib/nrser/types/display_spec.rb +50 -0
  135. data/spec/lib/nrser/types/paths_spec.rb +16 -10
  136. data/spec/lib/nrser/types/selector_spec.rb +125 -0
  137. data/spec/spec_helper.rb +4 -5
  138. metadata +105 -22
  139. data/lib/nrser/types/any.rb +0 -41
  140. data/lib/nrser/types/attrs.rb +0 -213
  141. data/lib/nrser/types/trees.rb +0 -42
@@ -0,0 +1,66 @@
1
+ NRSER::Types Display Table
2
+ ==============================================================================
3
+
4
+ Example table of `source` code creating types to the output of the various display
5
+ methods:
6
+
7
+ 1. {NRSER::Types::Type#name}
8
+ 1. {NRSER::Types::Type#symbolic}
9
+ 1. {NRSER::Types::Type#explain}
10
+
11
+ ### Verification
12
+
13
+ These values are verified in `//spec/lib/nrser/types/display_spec.rb`, so they
14
+ should remain accurate.
15
+
16
+ ### Adding & Updating
17
+
18
+ **_Don't update the values here, they will get overwritten!_**
19
+
20
+ I keep the values in a Quip doc at
21
+
22
+ <https://beiarea.quip.com/iBBRAuEZyxui>
23
+
24
+ because it's just a lot easier to manage in an actual spreadsheet (generally,
25
+ there have been some weird corner cases like columns not liking the word "true"
26
+ to be spelled like anything other than "TRUE", weird Unicode whitespace that
27
+ Quip sticks in that needs to be sub'd out, etc.).
28
+
29
+ There's a script at
30
+
31
+ [//dev/packages/gems/nrser/dev/bin/pull_types_display_table.rb][pull script]
32
+
33
+ [pull script]: ../../../../dev/bin/pull_types_display_table.rb
34
+
35
+ that will pull the Quip spreadsheet contents and **overwrite** what's here. Even
36
+ though the spreadsheet is world-viewable, you still seem to nee a Quip API token
37
+ to access it via the API.
38
+
39
+ > ##### NOTE #####
40
+ >
41
+ > The `source` column assumes you have {NRSER::Types} available as `t`, as
42
+ >
43
+ > require 'nrser/refinements/types'
44
+ > using NRSER::Types
45
+ >
46
+ > provides. Due to how refinements bind, you may need to set `t` up as a global
47
+ > in certain testing, REPL or other funky situations.
48
+
49
+ | `source` | `#name` | `#symbolic` | `#explain` |
50
+ | -------------------------------------------- | ---------------------------- | ---------------------- | ------------------------------------------------------------- |
51
+ | `t.Numeric` | `Numeric` | `Numeric` | `Numeric` |
52
+ | `t.Integer` | `Integer` | `ℤ` | `Integer` |
53
+ | `t.PositiveInteger` | `PositiveInteger` | `ℤ⁺` | `(Integer & Bounded<min=1>)` |
54
+ | `t.NegativeInteger` | `NegativeInteger` | `ℤ⁻` | `(Integer & Bounded<max=-1>)` |
55
+ | `t.NonNegativeInteger` | `NonNegativeInteger` | `ℕ⁰` | `(Integer & Bounded<min=0>)` |
56
+ | `t.Boolean` | `Boolean` | `Boolean` | `(Is<true> \| Is<false>)` |
57
+ | `t.Bounded( min: 1, max: 2 )` | `Bounded<min=1, max=2>` | `(1..2)` | `Bounded<min=1, max=2>` |
58
+ | `t.Bounded( min: 1 )` | `Bounded<min=1>` | `(1..)` | `Bounded<min=1>` |
59
+ | `t.Array` | `Array` | `[*]` | `Array` |
60
+ | `t.Array( t.Integer )` | `Array<Integer>` | `[ℤ]` | `Array<Integer>` |
61
+ | `t.Attributes(x: t.Integer, y: t.String)` | `(#x→Integer & #y→String)` | `(#x→ℤ & #y→String)` | `Attributes<#x→Integer, #y→String>` |
62
+ | `~t.Integer` | `¬Integer` | `*∖ℤ` | `Not<Integer>` |
63
+ | `t.ArrayPair` | `Array<(*, *)>` | `[*, *]` | `Array<(Top, Top)>` |
64
+ | `t.HashPair` | `Hash<(*, *)>` | `(*=>*)` | `(Hash & Attributes<#length→Is<1>>)` |
65
+ | `t.Pair` | `Pair` | `(*, *)` | `(Array<(Top, Top)> \| (Hash & Attributes<#length→Is<1>>))` |
66
+ | `t.Hash keys: t.String, values: t.Integer` | `Hash<String, Integer>` | `{String=>ℤ}` | `Hash<String, Integer>` |
@@ -0,0 +1,91 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # ========================================================================
6
+
7
+ # Project / Package
8
+ # ------------------------------------------------------------------------
9
+
10
+ require_relative './type'
11
+
12
+
13
+ # Namespace
14
+ # ========================================================================
15
+
16
+ module NRSER
17
+ module Types
18
+
19
+
20
+ # Definitions
21
+ # ========================================================================
22
+
23
+ # Type satisfied only by anything `#==` it's {#value}.
24
+ #
25
+ class Equivalent < Type
26
+
27
+ # Attributes
28
+ # ========================================================================
29
+
30
+ attr_reader :value
31
+
32
+ def initialize value, **options
33
+ super **options
34
+ @value = value
35
+ end
36
+
37
+
38
+ def explain
39
+ "Equivalent<#{ value.inspect }>"
40
+ end
41
+
42
+
43
+ def test? value
44
+ @value.equal? value
45
+ end
46
+
47
+
48
+ def == other
49
+ equal?(other) ||
50
+ ( self.class == other.class &&
51
+ @value == other.value )
52
+ end
53
+
54
+
55
+ def default_symbolic
56
+ "{ x : #{ value.inspect }==x }"
57
+ end
58
+
59
+ end # class Equivalent
60
+
61
+
62
+ # @!group Equivalent Type Factories
63
+ # ----------------------------------------------------------------------------
64
+
65
+ # @!method self.Equivalent value, **options
66
+ # Satisfied by values that `value` is `#==` to (`{ x : value == x }`).
67
+ #
68
+ # @param [Object] value
69
+ # Value that all members of the type will be equal to.
70
+ #
71
+ # @param [Hash] options
72
+ # Passed to {Type#initialize}.
73
+ #
74
+ # @return [Type]
75
+ # A type whose members are all instances of Ruby's {Numeric} class.
76
+ #
77
+ def_type :Equivalent,
78
+ aliases: [ :eq ],
79
+ parameterize: :value,
80
+ &->( value, **options ) do
81
+ Equivalent.new value, **options
82
+ end
83
+
84
+ # @!endgroup Equivalent Type Factories # *************************************
85
+
86
+
87
+ # /Namespace
88
+ # ========================================================================
89
+
90
+ end # module Types
91
+ end # module NRSER
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Project / Package
8
8
  # -----------------------------------------------------------------------
9
- require 'nrser/errors/nicer_error'
9
+ require 'nrser/errors/type_error'
10
10
 
11
11
 
12
12
  # Definitions
@@ -15,13 +15,7 @@ require 'nrser/errors/nicer_error'
15
15
  # This error (or a subclass) is thrown when types fail to
16
16
  # {NRSER::Types::Type.check!}.
17
17
  #
18
- class NRSER::Types::CheckError < ::TypeError
19
-
20
- # Mixins
21
- # ==========================================================================
22
-
23
- include NRSER::NicerError
24
-
18
+ class NRSER::Types::CheckError < NRSER::TypeError
25
19
 
26
20
  # Attributes
27
21
  # ========================================================================
@@ -45,15 +39,15 @@ class NRSER::Types::CheckError < ::TypeError
45
39
 
46
40
  # Construct a `NicerError`.
47
41
  #
48
- # @param [*] value:
42
+ # @param [*] value
49
43
  # The {#value} that failed the check.
50
44
  #
51
- # @param [NRSER::Types::Type] type:
45
+ # @param [NRSER::Types::Type] type
52
46
  # The type that was checked.
53
47
  #
54
48
  # @param details: (see NRSER::NicerError#initialize)
55
49
  #
56
- # @param **kwds
50
+ # @param [Hash] kwds
57
51
  # See {NRSER::NicerError#initialize}
58
52
  #
59
53
  def initialize *message, value:, type:, details: nil, **kwds
@@ -50,13 +50,13 @@ class NRSER::Types::FromStringError < ::ArgumentError
50
50
  #
51
51
  # @param *message (see NRSER::NicerError#initialize)
52
52
  #
53
- # @param [String] string:
53
+ # @param [String] string
54
54
  # The string the type was trying to load a value from.
55
55
  #
56
- # @param [NRSER::Types::Type] type:
56
+ # @param [NRSER::Types::Type] type
57
57
  # The type that was trying to load.
58
58
  #
59
- # @param **kwds
59
+ # @param [Hash] kwds
60
60
  # See {NRSER::NicerError#initialize}
61
61
  #
62
62
  def initialize *message, string:, type:, **kwds
@@ -7,43 +7,40 @@
7
7
  # Stdlib
8
8
  # -----------------------------------------------------------------------
9
9
 
10
- # Deps
11
- # -----------------------------------------------------------------------
10
+ require 'set'
12
11
 
13
12
  # Project / Package
14
13
  # -----------------------------------------------------------------------
15
14
 
15
+ require 'nrser/errors/type_error'
16
16
 
17
- # Refinements
17
+ # Namespace
18
18
  # =======================================================================
19
19
 
20
-
21
- # Declarations
22
- # =======================================================================
20
+ module NRSER
21
+ module Types
23
22
 
24
23
 
25
24
  # Definitions
26
25
  # =======================================================================
27
26
 
28
-
29
- # @todo document NRSER::Types::Factory module.
30
- module NRSER::Types::Factory
27
+ # Mixin that provides {#def_type} to create type factory class methods.
28
+ #
29
+ # Mixed in to {NRSER::Types}, but can also be mixed in by libraries using
30
+ # the types system to define their own types.
31
+ #
32
+ module Factory
31
33
 
32
34
  # Define a type factory.
33
35
  #
34
- # @!macro [attach] factory
35
- # @param [Hash] **options
36
- # Common type construction options, see {Type#initialize}.
37
- #
38
- # @return [NRSER::Types::Type]
39
- # The type.
36
+ # @deprecated Use {#def_type}
40
37
  #
41
38
  def def_factory name, maybe: true, aliases: [], &body
42
39
  define_singleton_method name, &body
43
-
40
+
44
41
  aliases.each do |alias_name|
45
42
  if self.respond_to? alias_name
46
- alias_name = alias_name + '_'
43
+ alias_name = alias_name.to_s + '_'
47
44
  end
48
45
 
49
46
  singleton_class.send :alias_method, alias_name, name
@@ -84,10 +81,280 @@ module NRSER::Types::Factory
84
81
  end
85
82
 
86
83
  aliases.each do |alias_name|
87
- singleton_class.send :alias_method, "#{ alias_name }?", maybe_name
84
+ maybe_alias_name = "#{ alias_name }?"
85
+
86
+ if self.respond_to? maybe_alias_name
87
+ maybe_alias_name = "#{ alias_name }_?"
88
+ end
89
+
90
+ singleton_class.send :alias_method, maybe_alias_name, maybe_name
91
+ end
92
+
93
+ end
94
+ end # #def_factory
95
+
96
+
97
+ # Define a new type factory class method.
98
+ #
99
+ # @param [#to_s] name
100
+ # The name of the type. Will be normalized to a string via it's `#to_s`
101
+ # method.
102
+ #
103
+ # @param [Enumerable<#to_s>] aliases
104
+ # Aliases to add for the type factory method. Normalized to a {Set} of
105
+ # strings before use.
106
+ #
107
+ # @param [nil | Proc<(s:String): MEMBER>] from_s
108
+ # Optional function to load type members from strings.
109
+ #
110
+ # @param [Boolean] maybe
111
+ # When `true` will add `?`-suffixed versions of the factory that
112
+ # create {.Maybe} versions of the type.
113
+ #
114
+ # @param [nil | false | Proc<(*args, &block): String>] default_name
115
+ #
116
+ # Controls what - if anything - is done with the `name:` value in
117
+ # `options` when the factory method is called.
118
+ #
119
+ # Everything here is done *before* the `options` are passed to the
120
+ # factory method's `&body`, so the body will see any `name:` option that
121
+ # is filled in.
122
+ #
123
+ # When...
124
+ #
125
+ # - `nil` - when...
126
+ # - `parameterize:` is `nil` - `name` will be used as the created
127
+ # type's {Type#name} unless a `name:` option is explicitly
128
+ # provided by the factory caller.
129
+ #
130
+ # This situation covers "static" types that will only differ by
131
+ # their `options` - things like custom {Type#from_s},
132
+ # {Type#to_data}, etc.. Really, these are more like aliases since
133
+ # their member sets are identical.
134
+ #
135
+ # - `parameterize:` is *not* `nil` - the `name:` option will be left
136
+ # as `nil` if none is provided by the factory caller.
137
+ #
138
+ # - `false` - the `name:` option will not be touched - it will stay `nil`
139
+ # unless the factory caller provides a value.
140
+ #
141
+ # - `Proc<(*args, &block)->String>` - when the factory caller does not
142
+ # provide a `name:` option this function will be called with the
143
+ # arguments (including `options`) and block (if any) that the
144
+ # factory method was called with, and is expected to return a {String}
145
+ # that will be set as the `name:` option.
146
+ #
147
+ # @param [nil | Symbol | Array<Symbol>] parameterize
148
+ # Indicates if the type is parameterized, and, if so, what arguments
149
+ # it's parameterized over.
150
+ #
151
+ # Right now, just prevents the `name:` being assigned as the type's name
152
+ # when one isn't specified (see the `default_name:` parameter a
153
+ # complete(-ly confusing) explanation.
154
+ #
155
+ # The hope was to use this for something useful in the future, but who the
156
+ # hell knows to be honest.
157
+ #
158
+ # @param [nil | String | Proc<(*args, &block): String>] symbolic
159
+ # Controls what's done with the `symbolic:` option - which affects what
160
+ # the new type's {Type#symbolic} will return - when the factory methods
161
+ # are called with the `symbolic:` option `nil` or missing:
162
+ #
163
+ # - `nil` - nothing changes. `nil` goes in to the type initialization
164
+ # method, and should end up as a `symbolic: nil` option in
165
+ # {Type#initialize}.
166
+ #
167
+ # - `String` - This value is used. Makes sense for `static` types
168
+ # who only accept options that don't affect the members of the types
169
+ # they produce.
170
+ #
171
+ # - `Proc<(*args, &block)->String>` - Gets called with the arguments
172
+ # (including `options`) and block (if any) the factory method is called
173
+ # with and is expected to return the symbolic string representation.
174
+ #
175
+ # @param [nil | Proc<(MEMBER): DATA>] to_data
176
+ # I'm getting tired of writing this shit so I'm going to be brief here -
177
+ # provides a value that will get set as the `to_data:` option and become
178
+ # responsible for turning type member values into "data" (think things
179
+ # you can JSON encode).
180
+ #
181
+ # @param [Proc] body
182
+ # The type factory method body. **MUST** return a {Type} instance.
183
+ #
184
+ # @return [nil]
185
+ # Just creates class methods on whatever it's mixed in to.
186
+ #
187
+ def def_type name,
188
+ aliases: [],
189
+ from_s: nil,
190
+ maybe: true,
191
+ default_name: nil,
192
+ parameterize: nil,
193
+ symbolic: nil,
194
+ to_data: nil,
195
+ &body
196
+ # Normalize to strings
197
+ name = name.to_s
198
+ aliases = aliases.map( &:to_s ).to_set
199
+
200
+ unless default_name.nil? ||
201
+ default_name == false ||
202
+ default_name.is_a?( Proc )
203
+ raise NRSER::TypeError,
204
+ "`default_name:` keyword argument must be {nil}, {false} or a {Proc},",
205
+ "found", default_name,
206
+ expected: [ nil, false, Proc ],
207
+ received: default_name
208
+ end
209
+
210
+ # Count the required params so we know if we can take the last one as
211
+ # options or not.
212
+ #
213
+ # For this to work, {#def_type} has to be called like
214
+ #
215
+ # def_type name,
216
+ # &->( arg, **option ) do
217
+ # # ...
218
+ # end
219
+ #
220
+ # because the `do |arg, **option|` form marks *all* arguments as optional.
221
+ #
222
+ num_req_params = body.parameters.count { |type, name| type == :req }
223
+
224
+ define_singleton_method name, &->(*args, &block) do
225
+ if args.length > num_req_params &&
226
+ args[-1].is_a?( Hash ) &&
227
+ args[-1].keys.all? { |k| k.is_a? Symbol }
228
+ options = args[-1]
229
+ args = args[0..-2]
230
+ else
231
+ options = {}
232
+ end
233
+
234
+ if args.length < num_req_params
235
+ raise ArgumentError,
236
+ "wrong number of arguments (given #{ args.length }, " +
237
+ "expected #{ num_req_params })"
238
+ end
239
+
240
+ # If `default_name` is {false} it means we don't fuck with the name at
241
+ # all, and if it's not `nil` it's been user-set.
242
+ if options[:name].nil? && default_name != false
243
+ if default_name.is_a? Proc
244
+ options[:name] = default_name.call *args, &block
245
+
246
+ # The "old" (like, two days ago) way of signalling not to write `name`
247
+ # in (before we had `default_name=false`) was to tell {#def_type} that
248
+ # you were parameterizing, in which case it wouldn't make any sense
249
+ # to write `name` in for all the types coming out.
250
+ #
251
+ # And it still doesn't, though - despite high hopes for a future of
252
+ # parameterized enlightenment - that's all we've been using
253
+ # `parameterize` for at the time, and there will def be some argument
254
+ # structure kinks to work out in order to actually do something useful
255
+ # with the information, though I'm sure that is solvable.
256
+ #
257
+ # So I'm saying I wouldn't be surprised if `parameterize` ended up
258
+ # never really going anywhere except away.
259
+ elsif parameterize.nil?
260
+ options[:name] = name
261
+ end
262
+ end # if options[:name].nil? && default_name != false
263
+
264
+ options[:from_s] ||= from_s
265
+
266
+ options[:symbolic] ||= case symbolic
267
+ when Proc
268
+ symbolic.call *args, &block
269
+ else
270
+ symbolic
271
+ end
272
+
273
+ options[:to_data] ||= to_data
274
+
275
+ body.call( *args, **options, &block ).tap { |type|
276
+ unless type.is_a? Type
277
+ raise NRSER::TypeError.new \
278
+ "Type factory method #{ self.safe_name }.#{ __method__ } did",
279
+ "not return a {NRSER::Types::Type}! All type factory methods",
280
+ "**MUST** always return type instances. This method needs to be",
281
+ "fixed."
282
+ end
283
+ }
284
+ end
285
+
286
+ underscored = name.underscore
287
+
288
+ # Underscored names are also available!
289
+ unless name == underscored ||
290
+ aliases.include?( underscored )
291
+ aliases << underscored
292
+ end
293
+
294
+ aliases.each do |alias_name|
295
+ if self.respond_to? alias_name
296
+ alias_name = alias_name.to_s + '_'
297
+ end
298
+
299
+ singleton_class.send :alias_method, alias_name, name
300
+ end
301
+
302
+ if maybe && !name.end_with?( '?' )
303
+ maybe_name = "#{ name }?"
304
+
305
+ if self.respond_to? maybe_name
306
+ maybe_name = "#{ name }_?"
307
+ end
308
+
309
+ # HACK Ugh maybe I wrote this quick to fix it, not sure if it's a decent
310
+ # idea.. basically, need to figure out what `options` keys go
311
+ # to {.maybe} and which ones go to the regular factory... matters
312
+ # for shit like {.attrs} and {.hash_type} 'cause they use option
313
+ # keys (whether they *should* is something I've debated... sigh,
314
+ # it is what it is for now).
315
+ #
316
+ # So the options that go to {.maybe} just go strait through to
317
+ # {Type#initialize}, so just grab that method, see what keys it
318
+ # takes, and then can slice and dice off that...
319
+ #
320
+ maybe_option_keys = Set.new \
321
+ NRSER::Types::Type.
322
+ instance_method( :initialize ).
323
+ parameters.
324
+ select { |param_type, name| param_type == :key }.
325
+ map { |param_type, name| name }
326
+
327
+ define_singleton_method maybe_name do |*args, **options|
328
+ maybe_options = options.slice *maybe_option_keys
329
+ factory_options = options.except *maybe_option_keys
330
+
331
+ NRSER::Types.maybe \
332
+ public_send( name, *args, **factory_options ),
333
+ **maybe_options
334
+ end
335
+
336
+ aliases.each do |alias_name|
337
+ maybe_alias_name = "#{ alias_name }?"
338
+
339
+ if self.respond_to? maybe_alias_name
340
+ maybe_alias_name = "#{ alias_name }_?"
341
+ end
342
+
343
+ singleton_class.send :alias_method, maybe_alias_name, maybe_name
88
344
  end
89
345
 
90
346
  end
91
- end
347
+
348
+ nil
349
+ end # #def_type
350
+
92
351
 
93
- end # module NRSER::Types::Factory
352
+ end # module Factory
353
+
354
+
355
+ # /Namespace
356
+ # =======================================================================
357
+
358
+ end # module Types
359
+ end # module NRSER
360
+