nrser 0.0.30 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser.rb +56 -12
  3. data/lib/nrser/collection.rb +4 -7
  4. data/lib/nrser/ext.rb +5 -0
  5. data/lib/nrser/{refinements → ext}/enumerable.rb +11 -9
  6. data/lib/nrser/ext/pathname.rb +74 -0
  7. data/lib/nrser/{refinements → ext}/tree.rb +2 -26
  8. data/lib/nrser/functions.rb +18 -0
  9. data/lib/nrser/{array.rb → functions/array.rb} +2 -3
  10. data/lib/nrser/{binding.rb → functions/binding.rb} +0 -2
  11. data/lib/nrser/functions/enumerable.rb +355 -0
  12. data/lib/nrser/functions/enumerable/find_all_map.rb +33 -0
  13. data/lib/nrser/functions/enumerable/find_map.rb +53 -0
  14. data/lib/nrser/functions/exception.rb +17 -0
  15. data/lib/nrser/{hash.rb → functions/hash.rb} +0 -0
  16. data/lib/nrser/functions/hash/bury.rb +147 -0
  17. data/lib/nrser/{hash → functions/hash}/deep_merge.rb +5 -5
  18. data/lib/nrser/{hash → functions/hash}/except_keys.rb +2 -0
  19. data/lib/nrser/{hash → functions/hash}/guess_label_key_type.rb +3 -1
  20. data/lib/nrser/{hash → functions/hash}/slice_keys.rb +3 -1
  21. data/lib/nrser/{hash → functions/hash}/stringify_keys.rb +2 -0
  22. data/lib/nrser/{hash → functions/hash}/symbolize_keys.rb +3 -1
  23. data/lib/nrser/{hash → functions/hash}/transform_keys.rb +3 -1
  24. data/lib/nrser/functions/merge_by.rb +29 -0
  25. data/lib/nrser/{object.rb → functions/object.rb} +0 -0
  26. data/lib/nrser/{object → functions/object}/as_array.rb +2 -0
  27. data/lib/nrser/{object → functions/object}/as_hash.rb +7 -5
  28. data/lib/nrser/{object → functions/object}/truthy.rb +46 -7
  29. data/lib/nrser/{open_struct.rb → functions/open_struct.rb} +0 -0
  30. data/lib/nrser/functions/path.rb +150 -0
  31. data/lib/nrser/{proc.rb → functions/proc.rb} +1 -22
  32. data/lib/nrser/functions/string.rb +297 -0
  33. data/lib/nrser/functions/string/looks_like.rb +44 -0
  34. data/lib/nrser/{text.rb → functions/text.rb} +0 -0
  35. data/lib/nrser/{text → functions/text}/indentation.rb +2 -16
  36. data/lib/nrser/{text → functions/text}/lines.rb +1 -2
  37. data/lib/nrser/{text → functions/text}/word_wrap.rb +2 -4
  38. data/lib/nrser/{tree.rb → functions/tree.rb} +0 -0
  39. data/lib/nrser/{tree → functions/tree}/each_branch.rb +6 -7
  40. data/lib/nrser/functions/tree/leaves.rb +92 -0
  41. data/lib/nrser/{tree → functions/tree}/map_branches.rb +31 -32
  42. data/lib/nrser/functions/tree/map_leaves.rb +56 -0
  43. data/lib/nrser/{tree → functions/tree}/map_tree.rb +9 -20
  44. data/lib/nrser/{tree → functions/tree}/transform.rb +0 -10
  45. data/lib/nrser/logger.rb +9 -10
  46. data/lib/nrser/message.rb +3 -7
  47. data/lib/nrser/meta.rb +2 -0
  48. data/lib/nrser/meta/class_attrs.rb +3 -9
  49. data/lib/nrser/meta/props.rb +19 -19
  50. data/lib/nrser/meta/props/base.rb +4 -10
  51. data/lib/nrser/meta/props/prop.rb +12 -28
  52. data/lib/nrser/no_arg.rb +1 -3
  53. data/lib/nrser/refinements.rb +5 -0
  54. data/lib/nrser/refinements/array.rb +5 -17
  55. data/lib/nrser/refinements/enumerator.rb +1 -3
  56. data/lib/nrser/refinements/hash.rb +3 -15
  57. data/lib/nrser/refinements/object.rb +2 -2
  58. data/lib/nrser/refinements/open_struct.rb +0 -2
  59. data/lib/nrser/refinements/pathname.rb +3 -46
  60. data/lib/nrser/refinements/set.rb +2 -6
  61. data/lib/nrser/refinements/string.rb +2 -2
  62. data/lib/nrser/rspex.rb +16 -13
  63. data/lib/nrser/types.rb +6 -20
  64. data/lib/nrser/types/any.rb +0 -1
  65. data/lib/nrser/types/booleans.rb +1 -1
  66. data/lib/nrser/types/combinators.rb +5 -5
  67. data/lib/nrser/types/in.rb +0 -21
  68. data/lib/nrser/types/responds.rb +1 -0
  69. data/lib/nrser/types/trees.rb +1 -0
  70. data/lib/nrser/version.rb +2 -3
  71. data/spec/nrser/{template_spec.rb → functions/binding/template_spec.rb} +0 -0
  72. data/spec/nrser/functions/enumerable/find_all_map_spec.rb +28 -0
  73. data/spec/nrser/functions/enumerable/find_bounded_spec.rb +70 -0
  74. data/spec/nrser/functions/enumerable/find_map_spec.rb +38 -0
  75. data/spec/nrser/functions/enumerable/find_only_spec.rb +25 -0
  76. data/spec/nrser/functions/enumerable/to_h_by_spec.rb +28 -0
  77. data/spec/nrser/{format_exception_spec.rb → functions/exception/format_exception_spec.rb} +0 -0
  78. data/spec/nrser/{hash → functions/hash}/bury_spec.rb +0 -0
  79. data/spec/nrser/{hash → functions/hash}/guess_label_key_type_spec.rb +0 -0
  80. data/spec/nrser/{hash_spec.rb → functions/hash_spec.rb} +0 -7
  81. data/spec/nrser/{merge_by_spec.rb → functions/merge_by_spec.rb} +0 -0
  82. data/spec/nrser/{truthy_spec.rb → functions/object/truthy_spec.rb} +0 -0
  83. data/spec/nrser/{open_struct_spec.rb → functions/open_struct_spec.rb} +0 -0
  84. data/spec/nrser/{string → functions/string}/common_prefix_spec.rb +0 -0
  85. data/spec/nrser/{string → functions/string}/looks_like_spec.rb +0 -0
  86. data/spec/nrser/{truncate_spec.rb → functions/string/truncate_spec.rb} +0 -0
  87. data/spec/nrser/{text → functions/text}/dedent/gotchas_spec.rb +0 -0
  88. data/spec/nrser/{text → functions/text}/dedent_spec.rb +0 -0
  89. data/spec/nrser/{indent_spec.rb → functions/text/indent_spec.rb} +0 -0
  90. data/spec/nrser/{tree → functions/tree}/each_branch_spec.rb +0 -0
  91. data/spec/nrser/{tree → functions/tree}/leaves_spec.rb +0 -0
  92. data/spec/nrser/{tree → functions/tree}/map_branch_spec.rb +0 -0
  93. data/spec/nrser/{tree → functions/tree}/map_tree_spec.rb +0 -0
  94. data/spec/nrser/{tree → functions/tree}/transform_spec.rb +0 -0
  95. data/spec/nrser/{tree → functions/tree}/transformer_spec.rb +0 -0
  96. data/spec/nrser/meta/class_attrs_spec.rb +12 -14
  97. data/spec/spec_helper.rb +2 -3
  98. metadata +136 -110
  99. data/lib/nrser/enumerable.rb +0 -288
  100. data/lib/nrser/exception.rb +0 -7
  101. data/lib/nrser/hash/bury.rb +0 -154
  102. data/lib/nrser/merge_by.rb +0 -26
  103. data/lib/nrser/string.rb +0 -294
  104. data/lib/nrser/string/looks_like.rb +0 -51
  105. data/lib/nrser/tree/leaves.rb +0 -92
  106. data/lib/nrser/tree/map_leaves.rb +0 -63
  107. data/spec/nrser/enumerable_spec.rb +0 -111
@@ -0,0 +1,33 @@
1
+ module NRSER
2
+
3
+ # @!group Enumerable Functions
4
+
5
+ # Find all truthy (not `nil` or `false`) results of calling `&block`
6
+ # with entries from `enum`.
7
+ #
8
+ # @example
9
+ #
10
+ # NRSER.find_all_map( [1, 2, 3, 4] ) do |i|
11
+ # if i.even?
12
+ # "#{ i } is even!"
13
+ # end
14
+ # end
15
+ # # => ["2 is even!", "4 is even!"]
16
+ #
17
+ # @param [Enumerable<E>] enum
18
+ # Entries to search (in order).
19
+ #
20
+ # @param [Proc<(E)=>R>] &block
21
+ # Block mapping entires to results.
22
+ #
23
+ # @return [nil]
24
+ # When `block.call( E )` is `nil` or `false` for all `E` in `enum`.
25
+ #
26
+ # @return [R]
27
+ # The first result `R = block.call( E )` where `R` is not `nil` or `false`.
28
+ #
29
+ def self.find_all_map enum, &block
30
+ enum.map( &block ).select { |entry| entry }
31
+ end # .find_map
32
+
33
+ end # module NRSER
@@ -0,0 +1,53 @@
1
+ module NRSER
2
+
3
+ # @!group Enumerable Functions
4
+
5
+ # Find the first truthy (not `nil` or `false`) result of calling `&block`
6
+ # with entries from `enum`.
7
+ #
8
+ # Like {Enumerable#find}, accept an optional `ifnone` procedure to call if
9
+ # no match is found.
10
+ #
11
+ # @example
12
+ #
13
+ # NRSER.find_map( [1, 2, 3, 4] ) do |i|
14
+ # if i.even?
15
+ # "#{ i } is even!"
16
+ # end
17
+ # end
18
+ # # => "2 is even!"
19
+ #
20
+ # @param [Enumerable<E>] enum
21
+ # Entries to search (in order).
22
+ #
23
+ # @param [nil | Proc<()=>DEFAULT>] ifnone
24
+ # Optional lambda to call for the return value when no match is found.
25
+ #
26
+ # @param [Proc<(E)=>RESLUT>] &block
27
+ # Block mapping entires to results.
28
+ #
29
+ # @return [nil]
30
+ # When `block.call( E )` is `nil` or `false` for all `E` in `enum`
31
+ # *and* `ifnone` is `nil` or not provided.
32
+ #
33
+ # @return [V]
34
+ # When `block.call( E )` is `nil` or `false` for all `E` in `enum`
35
+ # *and* `ifnone` is a lambda that returns `DEFAULT`.
36
+ #
37
+ # @return [R]
38
+ # The first result `RESLUT = block.call( E )`
39
+ # where `RESLUT` is not `nil` or `false`.
40
+ #
41
+ def self.find_map enum, ifnone = nil, &block
42
+ enum.each do |entry|
43
+ if result = block.call( entry )
44
+ # Found a match, short-circuit
45
+ return result
46
+ end
47
+ end
48
+
49
+ # No matches, return `ifnone`
50
+ ifnone.call if ifnone
51
+ end # .find_map
52
+
53
+ end # module NRSER
@@ -0,0 +1,17 @@
1
+ module NRSER
2
+ # @!group Exception Functions
3
+
4
+ # String format an exception the same way they are printed to the CLI when
5
+ # not handled (when they crash programs - what you're used to seeing),
6
+ # including the message, class and backtrace.
7
+ #
8
+ # @param [Exception] e
9
+ # Exception to format.
10
+ #
11
+ # @return [String]
12
+ #
13
+ def self.format_exception e
14
+ "#{ e.message } (#{ e.class }):\n #{ e.backtrace.join("\n ") }"
15
+ end
16
+
17
+ end # module NRSER
File without changes
@@ -0,0 +1,147 @@
1
+ # Definitions
2
+ # =======================================================================
3
+
4
+ module NRSER
5
+
6
+ # @!group Hash Functions
7
+
8
+ # The opposite of `#dig` - set a value at a deep key path, creating
9
+ # necessary structures along the way and optionally clobbering whatever's
10
+ # in the way to achieve success.
11
+ #
12
+ # @param [Hash] hash
13
+ # Hash to bury the value in.
14
+ #
15
+ # @param [Array | #to_s] key_path
16
+ # - When an {Array}, each entry is used exactly as-is for each key.
17
+ #
18
+ # - Otherwise, the `key_path` is converted to a string and split by
19
+ # `.` to produce the key array, and the actual keys used depend on
20
+ # the `parsed_key_type` option.
21
+ #
22
+ # @param [Object] value
23
+ # The value to set at the end of the path.
24
+ #
25
+ # @param [Class | :guess] parsed_key_type:
26
+ # How to handle parsed key path segments:
27
+ #
28
+ # - `String` - use the strings that naturally split from a parsed
29
+ # key path.
30
+ #
31
+ # Note that this is the *String class itself, **not** a value that
32
+ # is a String*.
33
+ #
34
+ # - `Symbol` - convert the strings that are split from the key path
35
+ # to symbols.
36
+ #
37
+ # Note that this is the *Symbol class itself, **not** a value that
38
+ # is a Symbol*.``
39
+ #
40
+ # - `:guess` (default) -
41
+ #
42
+ # @return [return_type]
43
+ # @todo Document return value.
44
+ #
45
+ def self.bury! hash,
46
+ key_path,
47
+ value,
48
+ parsed_key_type: :guess,
49
+ clobber: false,
50
+ create_arrays_for_unsigned_keys: false
51
+
52
+ # Parse the key if it's not an array
53
+ unless key_path.is_a?( Array )
54
+ key_path = key_path.to_s.split '.'
55
+
56
+ # Convert the keys to symbols now if that's what we want to use
57
+ if parsed_key_type == Symbol
58
+ key_path.map! &:to_sym
59
+ end
60
+ end
61
+
62
+ _internal_bury! \
63
+ hash,
64
+ key_path,
65
+ value,
66
+ guess_key_type: ( parsed_key_type == :guess ),
67
+ clobber: clobber,
68
+ create_arrays_for_unsigned_keys: create_arrays_for_unsigned_keys
69
+ end # .bury!
70
+
71
+
72
+ # @todo Document _internal_bury! method.
73
+ #
74
+ # @private
75
+ #
76
+ # @param [type] arg_name
77
+ # @todo Add name param description.
78
+ #
79
+ # @return [return_type]
80
+ # @todo Document return value.
81
+ #
82
+ def self._internal_bury! tree,
83
+ key_path,
84
+ value,
85
+ guess_key_type:,
86
+ clobber:,
87
+ create_arrays_for_unsigned_keys:
88
+
89
+ # Split the key path into the current key and the rest of the keys
90
+ key, *rest = key_path
91
+
92
+ # If we are
93
+ #
94
+ # - Guessing the key type
95
+ # - The tree is keyed
96
+ # - The tree uses some {Symbol} (and no {String}) keys
97
+ #
98
+ # then convert the key to a symbol.
99
+ #
100
+ if guess_key_type &&
101
+ tree.respond_to?( :keys ) &&
102
+ guess_label_key_type( tree ) == Symbol
103
+ key = key.to_sym
104
+ end
105
+
106
+ # Terminating case: we're at the last segment
107
+ if rest.empty?
108
+ # Set the value
109
+ tree[key] = value
110
+
111
+ else
112
+ # Go deeper...
113
+
114
+ # See if there is a hash in place
115
+ unless NRSER::Types.tree.test tree[key]
116
+ # There is not... so we need to do some figurin'
117
+
118
+ # If we're clobbering or the hash has no value, we're good:
119
+ # assign a new hash to set in
120
+ if clobber || tree[key].nil?
121
+ if create_arrays_for_unsigned_keys &&
122
+ NRSER::Types.unsigned.test( key )
123
+ tree[key] = []
124
+ else
125
+ tree[key] = {}
126
+ end
127
+
128
+ else
129
+ # We've got an intractable state conflict; raise
130
+ raise NRSER::ConflictError.new squish <<-END
131
+ can not set key #{ key.inspect } due to conflicting value
132
+ #{ tree[key].inspect } in tree #{ tree.inspect } (:clobber
133
+ option not set)
134
+ END
135
+
136
+ end
137
+ end # unless hash[key].is_a?( Hash )
138
+
139
+ # Dive in...
140
+ bury! tree[key], rest, value
141
+
142
+ end # if rest.empty? / else
143
+ end # ._internal_bury!
144
+
145
+ private_class_method :_internal_bury!
146
+
147
+ end # module NRSER
@@ -1,9 +1,10 @@
1
-
2
1
  # Definitions
3
2
  # =======================================================================
4
3
 
5
4
  module NRSER
6
5
 
6
+ # @!group Hash Functions
7
+
7
8
  # Returns a new hash created by recursively merging `other_hash` on top of
8
9
  # `base_hash`.
9
10
  #
@@ -12,7 +13,7 @@ module NRSER
12
13
  # @see https://github.com/rails/rails/blob/23c8f6918d4e6b9a823aa7a91377c6e3b5d60e13/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
13
14
  #
14
15
  # @param [Hash] base_hash
15
- # Base hash - it's values will be overwritten by any key paths shared with
16
+ # Base hash - it's values will be overwritten by any key paths shared with
16
17
  # the other hash.
17
18
  #
18
19
  # @param [Hash] other_hash
@@ -38,7 +39,7 @@ module NRSER
38
39
  other_hash.each_pair do |current_key, other_value|
39
40
  this_value = base_hash[current_key]
40
41
 
41
- base_hash[current_key] = if this_value.is_a?(Hash) &&
42
+ base_hash[current_key] = if this_value.is_a?(Hash) &&
42
43
  other_value.is_a?(Hash)
43
44
  deep_merge this_value, other_value, &block
44
45
  else
@@ -52,6 +53,5 @@ module NRSER
52
53
 
53
54
  base_hash
54
55
  end
55
-
56
-
56
+
57
57
  end # module NRSER
@@ -3,6 +3,8 @@
3
3
 
4
4
  module NRSER
5
5
 
6
+ # @!group Hash Functions
7
+
6
8
  # Removes the given keys from hash and returns it.
7
9
  #
8
10
  # Lifted from ActiveSupport.
@@ -2,8 +2,10 @@
2
2
  # =======================================================================
3
3
 
4
4
  module NRSER
5
+
6
+ # @!group Hash Functions
5
7
 
6
- # Guess which type of "label" key - strings or symbols - a hash (or other
8
+ # Guess which type of "label" key - strings or symbols - a hash (or other
7
9
  # object that responds to `#keys` and `#empty`) uses.
8
10
  #
9
11
  # @param [#keys & #empty] keyed
@@ -2,6 +2,8 @@
2
2
  # =======================================================================
3
3
 
4
4
  module NRSER
5
+
6
+ # @!group Hash Functions
5
7
 
6
8
  # Lifted from ActiveSupport.
7
9
  #
@@ -21,7 +23,7 @@ module NRSER
21
23
 
22
24
 
23
25
  # Meant to be a drop-in replacement for the ActiveSupport version, though
24
- # I've changed the implementation a bit... because honestly I didn't
26
+ # I've changed the implementation a bit... because honestly I didn't
25
27
  # understand why they were doing it the way they do :/
26
28
  #
27
29
  # @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:slice!
@@ -3,6 +3,8 @@
3
3
 
4
4
  module NRSER
5
5
 
6
+ # @!group Hash Functions
7
+
6
8
  # Converts all keys into strings by calling `#to_s` on them. **Mutates the
7
9
  # hash.**
8
10
  #
@@ -3,6 +3,8 @@
3
3
 
4
4
  module NRSER
5
5
 
6
+ # @!group Hash Functions
7
+
6
8
  # Mutates `hash` by converting all keys that respond to `#to_sym` to symbols.
7
9
  #
8
10
  # Lifted from ActiveSupport.
@@ -36,7 +38,7 @@ module NRSER
36
38
  transform_keys(hash) { |key| key.to_sym rescue key }
37
39
  end
38
40
 
39
- singleton_class.send :alias_method, :to_sym_keys, :symbolize_keys
41
+ singleton_class.send :alias_method, :to_sym_keys, :symbolize_keys
40
42
 
41
43
 
42
44
  # @todo Document deep_symbolize_keys method.
@@ -1,5 +1,7 @@
1
1
  module NRSER
2
2
 
3
+ # @!group Hash Functions
4
+
3
5
  # Lifted from ActiveSupport.
4
6
  #
5
7
  # @see http://www.rubydoc.info/gems/activesupport/5.1.3/Hash:transform_keys!
@@ -84,7 +86,7 @@ module NRSER
84
86
  # Anything; see examples.
85
87
  #
86
88
  # @param [Proc] &block
87
- # Proc that should accept each key as it's only argument and return the
89
+ # Proc that should accept each key as it's only argument and return the
88
90
  # new key to replace it with.
89
91
  #
90
92
  def self.deep_transform_keys object, &block
@@ -0,0 +1,29 @@
1
+ module NRSER
2
+
3
+ # Deep merge arrays of data hashes, matching hashes by computing a key with
4
+ # `&merge_key`.
5
+ #
6
+ # Uses {NRSER.deep_merge!} to merge.
7
+ #
8
+ # @param [Array<Hash>] current
9
+ # Current (base) array of hashes to start with (lowest predominance).
10
+ #
11
+ # @param [Array<Hash>] *updates
12
+ # One or more arrays of update hashes to merge over `current` (last is
13
+ # highest predominance).
14
+ #
15
+ # @param [Proc<(Hash)=>Object>] &merge_key
16
+ # Each hash is passed to `&merge_key` and the result is used to match
17
+ # hashes for merge. Must not return equal values for two different hashes
18
+ # in any of the arrays (`current` or any of `*updates`).
19
+ #
20
+ # @return [Array<Hash>]
21
+ # Final array of merged hashes. Don't depend on order.
22
+ #
23
+ def self.merge_by current, *updates, &merge_key
24
+ updates.reduce( to_h_by current, &merge_key ) { |result, update|
25
+ deep_merge! result, to_h_by( update, &merge_key )
26
+ }.values
27
+ end # .merge_by
28
+
29
+ end # module NRSER
File without changes
@@ -1,5 +1,7 @@
1
1
  module NRSER
2
2
 
3
+ # @!group Object Functions
4
+
3
5
  # Return an array given any value in the way that makes most sense:
4
6
  #
5
7
  # 1. If `value` is an array, return it.
@@ -1,5 +1,7 @@
1
1
  module NRSER
2
2
 
3
+ # @!group Object Functions
4
+
3
5
  # Treat the value as the value for `key` in a hash if it's not already a
4
6
  # hash and can't be converted to one:
5
7
  #
@@ -13,7 +15,7 @@ module NRSER
13
15
  # 4. Otherwise, return a new hash where `key` points to the value.
14
16
  # **`key` MUST be provided in this case.**
15
17
  #
16
- # Useful in method overloading and similar situations where you expect a
18
+ # Useful in method overloading and similar situations where you expect a
17
19
  # hash that may specify a host of options, but want to allow the method
18
20
  # to be called with a single value that corresponds to a default key in that
19
21
  # option hash.
@@ -54,9 +56,9 @@ module NRSER
54
56
  # ---------------------
55
57
  #
56
58
  # Right now, {.as_hash} also tests if `value` responds to `#to_h`, and will
57
- # try to call it, using the result if it doesn't raise. This lets it deal
59
+ # try to call it, using the result if it doesn't raise. This lets it deal
58
60
  # with Ruby's "I used to be a Hash until someone mapped me" values like
59
- # `[[:class, 'address']]`. I'm not sure if this is the best approach, but
61
+ # `[[:class, 'address']]`. I'm not sure if this is the best approach, but
60
62
  # I'm going to try it for now and see how it pans out in actual usage.
61
63
  #
62
64
  # @todo
@@ -67,7 +69,7 @@ module NRSER
67
69
  # The value that we want to be a hash.
68
70
  #
69
71
  # @param [Object] key [default nil]
70
- # The key that `value` will be stored under in the result if `value` is
72
+ # The key that `value` will be stored under in the result if `value` is
71
73
  # not a hash or can't be turned into one via `#to_h`. If this happens
72
74
  # this value can **NOT** be `nil` or an `ArgumentError` is raised.
73
75
  #
@@ -75,7 +77,7 @@ module NRSER
75
77
  #
76
78
  # @raise [ArgumentError]
77
79
  # If it comes to constructing a new Hash with `value` as a value and no
78
- # argument was provided
80
+ # argument was provided
79
81
  #
80
82
  def self.as_hash value, key = nil
81
83
  return value if value.is_a? Hash