nrser 0.0.25 → 0.0.26

Sign up to get free protection for your applications and to get access to all the features.
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