nrser 0.0.25 → 0.0.26

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -11
  3. data/lib/nrser.rb +5 -1
  4. data/lib/nrser/array.rb +10 -53
  5. data/lib/nrser/enumerable.rb +21 -0
  6. data/lib/nrser/hash.rb +13 -476
  7. data/lib/nrser/hash/bury.rb +154 -0
  8. data/lib/nrser/hash/deep_merge.rb +57 -0
  9. data/lib/nrser/hash/except_keys.rb +42 -0
  10. data/lib/nrser/hash/guess_label_key_type.rb +37 -0
  11. data/lib/nrser/hash/slice_keys.rb +41 -0
  12. data/lib/nrser/hash/stringify_keys.rb +37 -0
  13. data/lib/nrser/hash/symbolize_keys.rb +41 -0
  14. data/lib/nrser/hash/transform_keys.rb +45 -0
  15. data/lib/nrser/merge_by.rb +26 -0
  16. data/lib/nrser/message.rb +125 -0
  17. data/lib/nrser/meta/props.rb +2 -2
  18. data/lib/nrser/meta/props/prop.rb +5 -2
  19. data/lib/nrser/object.rb +5 -0
  20. data/lib/nrser/object/as_array.rb +37 -0
  21. data/lib/nrser/object/as_hash.rb +101 -0
  22. data/lib/nrser/{truthy.rb → object/truthy.rb} +0 -0
  23. data/lib/nrser/proc.rb +132 -0
  24. data/lib/nrser/refinements.rb +1 -2
  25. data/lib/nrser/refinements/array.rb +94 -5
  26. data/lib/nrser/refinements/enumerable.rb +5 -0
  27. data/lib/nrser/refinements/hash.rb +43 -6
  28. data/lib/nrser/refinements/object.rb +22 -2
  29. data/lib/nrser/refinements/symbol.rb +12 -0
  30. data/lib/nrser/refinements/tree.rb +41 -0
  31. data/lib/nrser/rspex.rb +329 -0
  32. data/lib/nrser/string.rb +3 -0
  33. data/lib/nrser/string/looks_like.rb +51 -0
  34. data/lib/nrser/temp/where.rb +52 -0
  35. data/lib/nrser/tree.rb +86 -0
  36. data/lib/nrser/tree/leaves.rb +92 -0
  37. data/lib/nrser/tree/map_leaves.rb +63 -0
  38. data/lib/nrser/tree/transform.rb +30 -0
  39. data/lib/nrser/types.rb +9 -4
  40. data/lib/nrser/types/any.rb +1 -1
  41. data/lib/nrser/types/array.rb +167 -25
  42. data/lib/nrser/types/{hash.rb → hashes.rb} +19 -5
  43. data/lib/nrser/types/in.rb +47 -0
  44. data/lib/nrser/types/is_a.rb +2 -2
  45. data/lib/nrser/types/labels.rb +49 -0
  46. data/lib/nrser/types/numbers.rb +63 -27
  47. data/lib/nrser/types/pairs.rb +109 -0
  48. data/lib/nrser/types/responds.rb +2 -3
  49. data/lib/nrser/types/strings.rb +17 -18
  50. data/lib/nrser/types/symbols.rb +39 -0
  51. data/lib/nrser/types/trees.rb +93 -0
  52. data/lib/nrser/types/tuples.rb +116 -0
  53. data/lib/nrser/types/type.rb +26 -2
  54. data/lib/nrser/version.rb +1 -1
  55. data/spec/nrser/hash/{guess_name_type_spec.rb → guess_label_key_type_spec.rb} +3 -3
  56. data/spec/nrser/hash_spec.rb +0 -20
  57. data/spec/nrser/merge_by_spec.rb +73 -0
  58. data/spec/nrser/meta/props_spec.rb +136 -43
  59. data/spec/nrser/op/message_spec.rb +62 -0
  60. data/spec/nrser/refinements/array_spec.rb +36 -0
  61. data/spec/nrser/refinements/hash_spec.rb +34 -0
  62. data/spec/nrser/string/looks_like_spec.rb +31 -0
  63. data/spec/nrser/tree/each_branch_spec.rb +82 -0
  64. data/spec/nrser/tree/leaves_spec.rb +112 -0
  65. data/spec/nrser/tree/transform_spec.rb +165 -0
  66. data/spec/nrser/types/array_spec.rb +82 -0
  67. data/spec/nrser/types/attrs_spec.rb +4 -4
  68. data/spec/nrser/types/pairs_spec.rb +41 -0
  69. data/spec/nrser/types/paths_spec.rb +3 -3
  70. data/spec/nrser/types/strings_spec.rb +66 -0
  71. data/spec/nrser/types/symbols_spec.rb +38 -0
  72. data/spec/nrser/types/tuples_spec.rb +37 -0
  73. data/spec/nrser/types_spec.rb +0 -13
  74. data/spec/spec_helper.rb +71 -22
  75. metadata +58 -10
  76. data/lib/nrser/spex.rb +0 -68
  77. data/lib/nrser/types/symbol.rb +0 -23
@@ -14,8 +14,7 @@ module NRSER::Types
14
14
  super ::Hash, **options
15
15
 
16
16
  @keys = NRSER::Types.make keys
17
- @values = NRSER::Types.make keys
18
-
17
+ @values = NRSER::Types.make values
19
18
  end
20
19
 
21
20
  def test value
@@ -31,9 +30,20 @@ module NRSER::Types
31
30
  end
32
31
  end # HashType
33
32
 
34
- HASH = HashType.new.freeze
35
33
 
36
- def self.hash_ *args
34
+
35
+ # Type satisfied by {Hash} instances.
36
+ #
37
+ # @param [Array] *args
38
+ # Passed to {NRSER::Types::HashType#initialize} unless empty.
39
+ #
40
+ # @return [NRSER::Types::HASH]
41
+ # If `args` are empty.
42
+ #
43
+ # @return [NRSER::Types::Type]
44
+ # Newly constructed hash type from `args`.
45
+ #
46
+ def self.hash_type *args
37
47
  if args.empty?
38
48
  HASH
39
49
  else
@@ -41,5 +51,9 @@ module NRSER::Types
41
51
  end
42
52
  end
43
53
 
44
- singleton_class.send :alias_method, :dict, :hash_
54
+ singleton_class.send :alias_method, :dict, :hash_type
55
+ singleton_class.send :alias_method, :hash_, :hash_type
56
+
57
+ HASH = HashType.new.freeze
58
+
45
59
  end
@@ -0,0 +1,47 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Project / Package
11
+ # -----------------------------------------------------------------------
12
+ require_relative './where'
13
+
14
+
15
+ # Refinements
16
+ # =======================================================================
17
+
18
+ require 'nrser/refinements'
19
+ using NRSER
20
+
21
+
22
+ # Declarations
23
+ # =======================================================================
24
+
25
+ module NRSER; end
26
+
27
+
28
+ # Definitions
29
+ # =======================================================================
30
+
31
+ module NRSER::Types
32
+
33
+ # Type that tests value for membership in a group object via that object's
34
+ # `#include?` method.
35
+ #
36
+ # @param [#include?] group
37
+ # `#include?` will be called on this value to determine type membership.
38
+ #
39
+ # @return [NRSER::Types::Type]
40
+ #
41
+ def self.in group, **options
42
+ where( name: "In(#{ group })", **options ) { |value|
43
+ group.include? value
44
+ }
45
+ end # .in
46
+
47
+ end # module NRSER::Types
@@ -11,8 +11,8 @@ module NRSER::Types
11
11
  @klass = klass
12
12
  end
13
13
 
14
- def name
15
- @name || "#{ self.class.short_name }(#{ @klass })"
14
+ def default_name
15
+ "#{ self.class.short_name }(#{ @klass })"
16
16
  end
17
17
 
18
18
  def test value
@@ -0,0 +1,49 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Project / Package
11
+ # -----------------------------------------------------------------------
12
+
13
+
14
+ # Refinements
15
+ # =======================================================================
16
+
17
+ require 'nrser/refinements'
18
+ using NRSER
19
+
20
+
21
+ # Declarations
22
+ # =======================================================================
23
+
24
+ module NRSER; end
25
+
26
+
27
+ # Definitions
28
+ # =======================================================================
29
+
30
+ module NRSER::Types
31
+
32
+ # A label is a non-empty {String} or {Symbol}.
33
+ #
34
+ # @param [Hash] **options
35
+ # Options to pass to {NRSER::Types::Type#initialize}.
36
+ #
37
+ # @return [NRSER::Types::Type]
38
+ #
39
+ def self.label **options
40
+ if options.empty?
41
+ LABEL
42
+ else
43
+ union non_empty_str, non_empty_sym, **options
44
+ end
45
+ end # .label
46
+
47
+ LABEL = label( name: 'LabelType' ).freeze
48
+
49
+ end # module NRSER::Types
@@ -7,91 +7,127 @@ require 'nrser/types/bounded'
7
7
  using NRSER
8
8
 
9
9
  module NRSER::Types
10
+ # Parse a string into a number.
11
+ #
12
+ # @return [Integer]
13
+ # If the string represents a whole integer.
14
+ #
15
+ # @return [Float]
16
+ # If the string represents a decimal number.
17
+ #
10
18
  def self.parse_number s
11
- float = s.to_f
19
+ float = Float s
12
20
  int = float.to_i
13
21
  if float == int then int else float end
14
22
  end
15
23
 
16
- # zero
17
- # ====
18
24
 
19
- ZERO = is 0, name: 'zero', from_s: method(:parse_number)
25
+ # Zero
26
+ # =====================================================================
27
+
28
+ ZERO = is(
29
+ 0,
30
+ name: 'ZeroType',
31
+ from_s: method( :parse_number )
32
+ ).freeze
20
33
 
21
34
  def self.zero
22
35
  ZERO
23
36
  end
24
37
 
25
- # number (Numeric)
26
- # ================
27
38
 
28
- NUM = IsA.new Numeric, name: 'Num', from_s: method(:parse_number)
39
+ # Number ({Numeric})
40
+ # =====================================================================
41
+
42
+ NUM = IsA.new(
43
+ Numeric,
44
+ name: 'NumType',
45
+ from_s: method( :parse_number )
46
+ ).freeze
29
47
 
30
48
  def self.num
31
49
  NUM
32
50
  end
33
51
 
34
- # integers
35
- # ========
52
+ singleton_class.send :alias_method, :number, :num
53
+
54
+
55
+ # Integers
56
+ # =====================================================================
36
57
 
37
- INT = IsA.new Integer, name: 'Int', from_s: method(:parse_number)
58
+ INT = IsA.new(
59
+ Integer,
60
+ name: 'IntType',
61
+ from_s: method( :parse_number )
62
+ ).freeze
38
63
 
39
64
  def self.int
40
65
  INT
41
66
  end
42
67
 
43
- def self.integer
44
- int
45
- end
68
+ singleton_class.send :alias_method, :integer, :int
46
69
 
47
- # bounded integers
48
- # ================
49
- #
50
70
 
51
- # positive integer
71
+ # Bounded Integers
72
+ # ---------------------------------------------------------------------
73
+
74
+ # Positive Integer
52
75
  # ----------------
53
76
  #
54
- # integer greater than zero.
77
+ # Integer greater than zero.
55
78
  #
56
79
 
57
- POS_INT = intersection INT, bounded(min: 1), name: 'PosInt'
80
+ POS_INT = intersection(
81
+ INT,
82
+ bounded(min: 1),
83
+ name: 'PosIntType'
84
+ ).freeze
58
85
 
59
86
  def self.pos_int
60
87
  POS_INT
61
88
  end
62
89
 
63
- # negative integer
90
+
91
+ # Negative Integer
64
92
  # ----------------
65
93
  #
66
- # integer less than zero
94
+ # Integer less than zero.
67
95
  #
68
96
 
69
- NEG_INT = intersection INT, bounded(max: -1), name: 'NegInt'
97
+ NEG_INT = intersection(
98
+ INT,
99
+ bounded(max: -1),
100
+ name: 'NegIntType'
101
+ ).freeze
70
102
 
71
103
  def self.neg_int
72
104
  NEG_INT
73
105
  end
74
106
 
75
- # non-negative integer
107
+
108
+ # Non-Negative Integer
76
109
  # --------------------
77
110
  #
78
- # positive integers and zero... but it seems more efficient to define these
111
+ # Positive integers and zero... but it seems more efficient to define these
79
112
  # as bounded instead of a union.
80
113
  #
81
114
 
82
- NON_NEG_INT = intersection INT, bounded(min: 0), name: 'NonNegInt'
115
+ NON_NEG_INT = intersection INT, bounded(min: 0), name: 'NonNegIntType'
83
116
 
84
117
  def self.non_neg_int
85
118
  NON_NEG_INT
86
119
  end
87
120
 
88
- # non-positive integer
121
+ singleton_class.send :alias_method, :unsigned, :non_neg_int
122
+
123
+
124
+ # Non-Positive Integer
89
125
  # --------------------
90
126
  #
91
127
  # negative integers and zero.
92
128
  #
93
129
 
94
- NON_POS_INT = intersection INT, bounded(max: 0), name: 'NonPosInt'
130
+ NON_POS_INT = intersection INT, bounded(max: 0), name: 'NonPosIntType'
95
131
 
96
132
  def self.non_pos_int
97
133
  NON_POS_INT
@@ -0,0 +1,109 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Project / Package
11
+ # -----------------------------------------------------------------------
12
+ require_relative './combinators'
13
+
14
+
15
+ # Refinements
16
+ # =======================================================================
17
+
18
+
19
+ # Declarations
20
+ # =======================================================================
21
+
22
+ module NRSER; end
23
+
24
+
25
+ # Definitions
26
+ # =======================================================================
27
+
28
+ module NRSER::Types
29
+
30
+
31
+ # @todo Document array_pair method.
32
+ #
33
+ # @param [type] arg_name
34
+ # @todo Add name param description.
35
+ #
36
+ # @return [return_type]
37
+ # @todo Document return value.
38
+ #
39
+ def self.array_pair **options
40
+ return ARRAY_PAIR if options.empty?
41
+
42
+ key = options.delete(:key) || ANY
43
+ value = options.delete(:value) || ANY
44
+
45
+ tuple key, value, **options
46
+ end # .array_pair
47
+
48
+ ARRAY_PAIR = array_pair( name: 'ArrayPairType' ).freeze
49
+
50
+
51
+ # Type for a {Hash} that consists of only a single key and value pair.
52
+ #
53
+ # @param [String] name:
54
+ # Name to give the new type.
55
+ #
56
+ # @param [Hash] **options
57
+ # Other options to pass to
58
+ #
59
+ # @return [NRSER::Types::Type]
60
+ #
61
+ def self.hash_pair **options
62
+ return HASH_PAIR if options.empty?
63
+
64
+ hash_options = {}
65
+ {key: :keys, value: :values}.each { |from_key, to_key|
66
+ if options.key? from_key
67
+ hash_options[to_key] = options.delete from_key
68
+ end
69
+ }
70
+
71
+ if hash_options.empty?
72
+ intersection is_a( Hash ), length( 1 ), **options
73
+ else
74
+ intersection \
75
+ hash_type( **hash_options ),
76
+ length( 1 ),
77
+ **options
78
+ end
79
+
80
+ end # .hash_pair
81
+
82
+ HASH_PAIR = hash_pair( name: 'HashPairType' ).freeze
83
+
84
+
85
+ # @todo Document pair method.
86
+ #
87
+ # @param [type] arg_name
88
+ # @todo Add name param description.
89
+ #
90
+ # @return [return_type]
91
+ # @todo Document return value.
92
+ #
93
+ def self.pair **options
94
+ if options.empty?
95
+ PAIR
96
+ else
97
+ type_options = NRSER.slice_keys! options, :key, :value
98
+
99
+ union \
100
+ array_pair( **type_options ),
101
+ hash_pair( **type_options ),
102
+ **options
103
+ end
104
+ end # #pair
105
+
106
+ PAIR = pair( name: 'PairType' ).freeze
107
+
108
+ end # module NRSER::Types
109
+
@@ -1,8 +1,8 @@
1
1
 
2
- # @todo document NRSER::Types module.
3
2
  module NRSER::Types
4
3
 
5
- # @todo document Responds class.
4
+ # Type that encodes messages mapped to result types that member values must
5
+ # satisfy.
6
6
  class Responds < NRSER::Types::Type
7
7
 
8
8
  # Constants
@@ -87,7 +87,6 @@ module NRSER::Types
87
87
  end # #responds
88
88
 
89
89
 
90
-
91
90
  # @todo Document respond_to Responds.
92
91
  #
93
92
  # @param [type] arg_name
@@ -6,35 +6,34 @@ require 'nrser/types/attrs'
6
6
  using NRSER
7
7
 
8
8
  module NRSER::Types
9
- STR = IsA.new String, name: 'Str', from_s: ->(s) { s }
10
-
11
- def self.str **options
12
- if options.empty?
9
+ def self.str length: nil, **options
10
+ if length.nil? && options.empty?
13
11
  # if there are no options can point to the constant for efficiency
14
12
  STR
15
13
  else
16
- types = []
17
-
18
- if options[:length]
19
- types << length(options[:length])
14
+ if length.nil?
15
+ IsA.new String, from_s: ->(s) { s }, **options
16
+ else
17
+ intersection \
18
+ IsA.new( String, from_s: ->(s) { s } ),
19
+ NRSER::Types.length( length ),
20
+ **options
20
21
  end
21
-
22
- intersection STR, *types
23
22
  end
24
23
  end # string
25
24
 
26
- def self.string
27
- str
28
- end
29
-
30
- EMPTY_STR = Is.new ''
25
+ singleton_class.send :alias_method, :string, :str
31
26
 
32
- NON_EMPTY_STR = str length: {min: 1}, name: "NonEmptyStr"
27
+ STR = str( name: 'StrType' ).freeze
33
28
 
29
+ EMPTY_STR = Is.new( '' ).freeze
34
30
 
35
- def self.non_empty_str
36
- NON_EMPTY_STR
31
+ def self.non_empty_str **options
32
+ return NON_EMPTY_STR if options.empty?
33
+
34
+ str( length: {min: 1}, **options )
37
35
  end # .non_empty_str
38
36
 
37
+ NON_EMPTY_STR = non_empty_str( name: 'NonEmptyStr' ).freeze
39
38
 
40
39
  end # NRSER::Types