nrser 0.0.30 → 0.1.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.
- checksums.yaml +4 -4
- data/lib/nrser.rb +56 -12
- data/lib/nrser/collection.rb +4 -7
- data/lib/nrser/ext.rb +5 -0
- data/lib/nrser/{refinements → ext}/enumerable.rb +11 -9
- data/lib/nrser/ext/pathname.rb +74 -0
- data/lib/nrser/{refinements → ext}/tree.rb +2 -26
- data/lib/nrser/functions.rb +18 -0
- data/lib/nrser/{array.rb → functions/array.rb} +2 -3
- data/lib/nrser/{binding.rb → functions/binding.rb} +0 -2
- data/lib/nrser/functions/enumerable.rb +355 -0
- data/lib/nrser/functions/enumerable/find_all_map.rb +33 -0
- data/lib/nrser/functions/enumerable/find_map.rb +53 -0
- data/lib/nrser/functions/exception.rb +17 -0
- data/lib/nrser/{hash.rb → functions/hash.rb} +0 -0
- data/lib/nrser/functions/hash/bury.rb +147 -0
- data/lib/nrser/{hash → functions/hash}/deep_merge.rb +5 -5
- data/lib/nrser/{hash → functions/hash}/except_keys.rb +2 -0
- data/lib/nrser/{hash → functions/hash}/guess_label_key_type.rb +3 -1
- data/lib/nrser/{hash → functions/hash}/slice_keys.rb +3 -1
- data/lib/nrser/{hash → functions/hash}/stringify_keys.rb +2 -0
- data/lib/nrser/{hash → functions/hash}/symbolize_keys.rb +3 -1
- data/lib/nrser/{hash → functions/hash}/transform_keys.rb +3 -1
- data/lib/nrser/functions/merge_by.rb +29 -0
- data/lib/nrser/{object.rb → functions/object.rb} +0 -0
- data/lib/nrser/{object → functions/object}/as_array.rb +2 -0
- data/lib/nrser/{object → functions/object}/as_hash.rb +7 -5
- data/lib/nrser/{object → functions/object}/truthy.rb +46 -7
- data/lib/nrser/{open_struct.rb → functions/open_struct.rb} +0 -0
- data/lib/nrser/functions/path.rb +150 -0
- data/lib/nrser/{proc.rb → functions/proc.rb} +1 -22
- data/lib/nrser/functions/string.rb +297 -0
- data/lib/nrser/functions/string/looks_like.rb +44 -0
- data/lib/nrser/{text.rb → functions/text.rb} +0 -0
- data/lib/nrser/{text → functions/text}/indentation.rb +2 -16
- data/lib/nrser/{text → functions/text}/lines.rb +1 -2
- data/lib/nrser/{text → functions/text}/word_wrap.rb +2 -4
- data/lib/nrser/{tree.rb → functions/tree.rb} +0 -0
- data/lib/nrser/{tree → functions/tree}/each_branch.rb +6 -7
- data/lib/nrser/functions/tree/leaves.rb +92 -0
- data/lib/nrser/{tree → functions/tree}/map_branches.rb +31 -32
- data/lib/nrser/functions/tree/map_leaves.rb +56 -0
- data/lib/nrser/{tree → functions/tree}/map_tree.rb +9 -20
- data/lib/nrser/{tree → functions/tree}/transform.rb +0 -10
- data/lib/nrser/logger.rb +9 -10
- data/lib/nrser/message.rb +3 -7
- data/lib/nrser/meta.rb +2 -0
- data/lib/nrser/meta/class_attrs.rb +3 -9
- data/lib/nrser/meta/props.rb +19 -19
- data/lib/nrser/meta/props/base.rb +4 -10
- data/lib/nrser/meta/props/prop.rb +12 -28
- data/lib/nrser/no_arg.rb +1 -3
- data/lib/nrser/refinements.rb +5 -0
- data/lib/nrser/refinements/array.rb +5 -17
- data/lib/nrser/refinements/enumerator.rb +1 -3
- data/lib/nrser/refinements/hash.rb +3 -15
- data/lib/nrser/refinements/object.rb +2 -2
- data/lib/nrser/refinements/open_struct.rb +0 -2
- data/lib/nrser/refinements/pathname.rb +3 -46
- data/lib/nrser/refinements/set.rb +2 -6
- data/lib/nrser/refinements/string.rb +2 -2
- data/lib/nrser/rspex.rb +16 -13
- data/lib/nrser/types.rb +6 -20
- data/lib/nrser/types/any.rb +0 -1
- data/lib/nrser/types/booleans.rb +1 -1
- data/lib/nrser/types/combinators.rb +5 -5
- data/lib/nrser/types/in.rb +0 -21
- data/lib/nrser/types/responds.rb +1 -0
- data/lib/nrser/types/trees.rb +1 -0
- data/lib/nrser/version.rb +2 -3
- data/spec/nrser/{template_spec.rb → functions/binding/template_spec.rb} +0 -0
- data/spec/nrser/functions/enumerable/find_all_map_spec.rb +28 -0
- data/spec/nrser/functions/enumerable/find_bounded_spec.rb +70 -0
- data/spec/nrser/functions/enumerable/find_map_spec.rb +38 -0
- data/spec/nrser/functions/enumerable/find_only_spec.rb +25 -0
- data/spec/nrser/functions/enumerable/to_h_by_spec.rb +28 -0
- data/spec/nrser/{format_exception_spec.rb → functions/exception/format_exception_spec.rb} +0 -0
- data/spec/nrser/{hash → functions/hash}/bury_spec.rb +0 -0
- data/spec/nrser/{hash → functions/hash}/guess_label_key_type_spec.rb +0 -0
- data/spec/nrser/{hash_spec.rb → functions/hash_spec.rb} +0 -7
- data/spec/nrser/{merge_by_spec.rb → functions/merge_by_spec.rb} +0 -0
- data/spec/nrser/{truthy_spec.rb → functions/object/truthy_spec.rb} +0 -0
- data/spec/nrser/{open_struct_spec.rb → functions/open_struct_spec.rb} +0 -0
- data/spec/nrser/{string → functions/string}/common_prefix_spec.rb +0 -0
- data/spec/nrser/{string → functions/string}/looks_like_spec.rb +0 -0
- data/spec/nrser/{truncate_spec.rb → functions/string/truncate_spec.rb} +0 -0
- data/spec/nrser/{text → functions/text}/dedent/gotchas_spec.rb +0 -0
- data/spec/nrser/{text → functions/text}/dedent_spec.rb +0 -0
- data/spec/nrser/{indent_spec.rb → functions/text/indent_spec.rb} +0 -0
- data/spec/nrser/{tree → functions/tree}/each_branch_spec.rb +0 -0
- data/spec/nrser/{tree → functions/tree}/leaves_spec.rb +0 -0
- data/spec/nrser/{tree → functions/tree}/map_branch_spec.rb +0 -0
- data/spec/nrser/{tree → functions/tree}/map_tree_spec.rb +0 -0
- data/spec/nrser/{tree → functions/tree}/transform_spec.rb +0 -0
- data/spec/nrser/{tree → functions/tree}/transformer_spec.rb +0 -0
- data/spec/nrser/meta/class_attrs_spec.rb +12 -14
- data/spec/spec_helper.rb +2 -3
- metadata +136 -110
- data/lib/nrser/enumerable.rb +0 -288
- data/lib/nrser/exception.rb +0 -7
- data/lib/nrser/hash/bury.rb +0 -154
- data/lib/nrser/merge_by.rb +0 -26
- data/lib/nrser/string.rb +0 -294
- data/lib/nrser/string/looks_like.rb +0 -51
- data/lib/nrser/tree/leaves.rb +0 -92
- data/lib/nrser/tree/map_leaves.rb +0 -63
- data/spec/nrser/enumerable_spec.rb +0 -111
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b087fa91e207a8859fdcf2335e50bc2dc8661fb8
|
|
4
|
+
data.tar.gz: 6a0edb30a2236bde01044c9de2189edc7f42bac3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8f5e75698179ea8beac5a4e40fe6f52f006817646fc6c47cc04c6b505e144e0b07066023ad547c49d0d9c3eea52bda2520816b9b9ed2bd15373cd55cd92aad0
|
|
7
|
+
data.tar.gz: 8a2e9a4b5deee46c2c9e5f34142618b1148ed5ae23ba1cdceb8e573e7ec5575c7e96c24ba62fe2ad3188be3618aa0922cbebb4810c2344dd9ce99c619a72ed8b
|
data/lib/nrser.rb
CHANGED
|
@@ -1,24 +1,68 @@
|
|
|
1
|
+
# Requirements
|
|
2
|
+
# =======================================================================
|
|
3
|
+
#
|
|
4
|
+
# I'm moving to "just require everything" after abandoning the idea of
|
|
5
|
+
# supporting old rubies (2.3 is now min), so all the require expressions
|
|
6
|
+
# should just go here, making life much simpler.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
# Stdlib
|
|
10
|
+
# -----------------------------------------------------------------------
|
|
1
11
|
require 'pathname'
|
|
12
|
+
require 'set'
|
|
13
|
+
require 'pp'
|
|
14
|
+
require 'ostruct'
|
|
15
|
+
require 'json'
|
|
16
|
+
require 'yaml'
|
|
17
|
+
require 'logger'
|
|
18
|
+
require 'singleton'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Deps
|
|
22
|
+
# -----------------------------------------------------------------------
|
|
23
|
+
|
|
2
24
|
|
|
25
|
+
# Hi there!
|
|
26
|
+
#
|
|
27
|
+
# This is [my][me] Ruby stuff.
|
|
28
|
+
#
|
|
29
|
+
# [me]: https://github.com/nrser
|
|
30
|
+
#
|
|
31
|
+
# This module has a lot of static methods (`class` or `self.` methods), which
|
|
32
|
+
# are functional (pure).
|
|
33
|
+
#
|
|
34
|
+
# They're all just directly attached to the {NRSER} object to make them
|
|
35
|
+
# convenient to call, but they're grouped here by what they operate on.
|
|
36
|
+
#
|
|
37
|
+
# You can go ahead and use them like that, but many (most?) of them are also
|
|
38
|
+
# refined in to the classes of their first arguments in `nrser/refinements`,
|
|
39
|
+
# which is how I mostly use them, unless I'm targeting an older Ruby that
|
|
40
|
+
# doesn't support refinements.
|
|
41
|
+
#
|
|
42
|
+
# There are also various classes and sub-modules as well. Most of the
|
|
43
|
+
# sub-modules require refinements because though it's nice to have the core
|
|
44
|
+
# functionality available in a pinch for earlier Rubies, I'm not targeting
|
|
45
|
+
# versions that far back in any serious or complicated code.
|
|
46
|
+
#
|
|
47
|
+
# Enjoy!
|
|
48
|
+
#
|
|
3
49
|
module NRSER
|
|
50
|
+
|
|
51
|
+
# Absolute, expanded path to the gem's root directory.
|
|
52
|
+
#
|
|
53
|
+
# @return [Pathname]
|
|
54
|
+
#
|
|
4
55
|
ROOT = ( Pathname.new(__FILE__).dirname / '..' ).expand_path
|
|
56
|
+
|
|
5
57
|
end
|
|
6
58
|
|
|
59
|
+
require_relative './nrser/ext'
|
|
60
|
+
require_relative './nrser/errors'
|
|
7
61
|
require_relative './nrser/version'
|
|
8
62
|
require_relative './nrser/no_arg'
|
|
9
63
|
require_relative './nrser/message'
|
|
10
|
-
require_relative './nrser/proc'
|
|
11
64
|
require_relative './nrser/collection'
|
|
12
|
-
require_relative './nrser/
|
|
13
|
-
require_relative './nrser/string'
|
|
14
|
-
require_relative './nrser/text'
|
|
15
|
-
require_relative './nrser/binding'
|
|
16
|
-
require_relative './nrser/exception'
|
|
17
|
-
require_relative './nrser/enumerable'
|
|
18
|
-
require_relative './nrser/hash'
|
|
19
|
-
require_relative './nrser/array'
|
|
65
|
+
require_relative './nrser/functions'
|
|
20
66
|
require_relative './nrser/types'
|
|
67
|
+
require_relative './nrser/refinements'
|
|
21
68
|
require_relative './nrser/meta'
|
|
22
|
-
require_relative './nrser/open_struct'
|
|
23
|
-
require_relative './nrser/merge_by'
|
|
24
|
-
require_relative './nrser/tree'
|
data/lib/nrser/collection.rb
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
require 'set'
|
|
2
|
-
require 'ostruct'
|
|
3
|
-
|
|
4
1
|
module NRSER
|
|
5
2
|
# include this module in any custom classes to have them treated as
|
|
6
3
|
# collections instead of individual objects by the methods in this file
|
|
@@ -36,7 +33,7 @@ module NRSER
|
|
|
36
33
|
#
|
|
37
34
|
# **NOTE** Implemented for our idea of a collection instead of testing
|
|
38
35
|
# for response to `#each` (or similar) to avoid catching things
|
|
39
|
-
# like {IO} instances, which include {Enumerable} but are
|
|
36
|
+
# like {IO} instances, which include {Enumerable} but are
|
|
40
37
|
# probably not what is desired when using {NRSER.each}
|
|
41
38
|
# (more likely that you mean "I expect one or more files" than
|
|
42
39
|
# "I expect one or more strings which may be represented by
|
|
@@ -54,9 +51,9 @@ module NRSER
|
|
|
54
51
|
def each object, &block
|
|
55
52
|
if collection? object
|
|
56
53
|
# We need to test for response because {OpenStruct} *will* respond to
|
|
57
|
-
# #each because *it will respond to anything* (which sucks), but it
|
|
54
|
+
# #each because *it will respond to anything* (which sucks), but it
|
|
58
55
|
# will return `false` for `respond_to? :each` and the like, and this
|
|
59
|
-
# behavior could be shared by other collection objects, so it seems
|
|
56
|
+
# behavior could be shared by other collection objects, so it seems
|
|
60
57
|
# like a decent idea.
|
|
61
58
|
if object.respond_to? :each_pair
|
|
62
59
|
object.each_pair &block
|
|
@@ -98,4 +95,4 @@ module NRSER
|
|
|
98
95
|
end # #map
|
|
99
96
|
|
|
100
97
|
end # class << self
|
|
101
|
-
end # module NRSER
|
|
98
|
+
end # module NRSER
|
data/lib/nrser/ext.rb
ADDED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
module NRSER::Refinements; end
|
|
3
|
-
|
|
4
|
-
# Instance methods that are mixed in to the refinements of many classes that
|
|
5
|
-
# include {Enumerable}, including {Array}, {Set}, {Hash} and {OpenStruct}.
|
|
1
|
+
# Instance methods to extend {Enumerable} objects.
|
|
6
2
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
3
|
+
# Refined into many of them, including {Array}, {Set}, {Hash} and {OpenStruct},
|
|
4
|
+
# and may be independently used as well.
|
|
9
5
|
#
|
|
10
|
-
module NRSER::
|
|
6
|
+
module NRSER::Ext::Enumerable
|
|
11
7
|
|
|
12
8
|
# See {NRSER.map_values}
|
|
13
9
|
def map_values &block
|
|
@@ -62,4 +58,10 @@ module NRSER::Refinements::Enumerable
|
|
|
62
58
|
NRSER.try_find self, &block
|
|
63
59
|
end
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
|
|
62
|
+
# See {NRSER.find_map}
|
|
63
|
+
def find_map *args, &block
|
|
64
|
+
NRSER.find_map self, *args, &block
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end # module NRSER::Ext::Enumerable
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
# @todo document NRSER::Ext::Pathname module.
|
|
3
|
+
module NRSER::Ext::Pathname
|
|
4
|
+
|
|
5
|
+
# override to accept Pathname instances.
|
|
6
|
+
#
|
|
7
|
+
# @param [String] *prefixes
|
|
8
|
+
# the prefixes to see if the Pathname starts with.
|
|
9
|
+
#
|
|
10
|
+
# @return [Boolean]
|
|
11
|
+
# true if the Pathname starts with any of the prefixes.
|
|
12
|
+
#
|
|
13
|
+
def start_with? *prefixes
|
|
14
|
+
to_s.start_with? *prefixes.map(&:to_s)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# override sub to support Pathname instances as patterns.
|
|
19
|
+
#
|
|
20
|
+
# @param [String | Regexp | Pathname] pattern
|
|
21
|
+
# thing to replace.
|
|
22
|
+
#
|
|
23
|
+
# @param [String | Hash] replacement
|
|
24
|
+
# thing to replace it with.
|
|
25
|
+
#
|
|
26
|
+
# @return [Pathname]
|
|
27
|
+
# new Pathname.
|
|
28
|
+
#
|
|
29
|
+
def sub pattern, replacement
|
|
30
|
+
case pattern
|
|
31
|
+
when Pathname
|
|
32
|
+
super pattern.to_s, replacement
|
|
33
|
+
else
|
|
34
|
+
super pattern, replacement
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Just returns `self`. Implemented to match the {String#to_pn} API so it
|
|
40
|
+
# can be called on an argument that may be either one.
|
|
41
|
+
#
|
|
42
|
+
# @return [Pathname]
|
|
43
|
+
#
|
|
44
|
+
def to_pn
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# @todo Document find_root method.
|
|
50
|
+
#
|
|
51
|
+
# @param [type] arg_name
|
|
52
|
+
# @todo Add name param description.
|
|
53
|
+
#
|
|
54
|
+
# @return [return_type]
|
|
55
|
+
# @todo Document return value.
|
|
56
|
+
#
|
|
57
|
+
def find_up rel_path, **kwds
|
|
58
|
+
NRSER.find_up rel_path, **kwds, from: self
|
|
59
|
+
end # #find_root
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# @todo Document find_root method.
|
|
63
|
+
#
|
|
64
|
+
# @param [type] arg_name
|
|
65
|
+
# @todo Add name param description.
|
|
66
|
+
#
|
|
67
|
+
# @return [return_type]
|
|
68
|
+
# @todo Document return value.
|
|
69
|
+
#
|
|
70
|
+
def find_up! rel_path, **kwds
|
|
71
|
+
NRSER.find_up! rel_path, **kwds, from: self
|
|
72
|
+
end # #find_root
|
|
73
|
+
|
|
74
|
+
end # module NRSER::Ext::Pathname
|
|
@@ -1,30 +1,7 @@
|
|
|
1
|
-
# Requirements
|
|
2
|
-
# =======================================================================
|
|
3
|
-
|
|
4
|
-
# Stdlib
|
|
5
|
-
# -----------------------------------------------------------------------
|
|
6
|
-
|
|
7
|
-
# Deps
|
|
8
|
-
# -----------------------------------------------------------------------
|
|
9
|
-
|
|
10
|
-
# Project / Package
|
|
11
|
-
# -----------------------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Declarations
|
|
15
|
-
# =======================================================================
|
|
16
|
-
|
|
17
|
-
module NRSER; end
|
|
18
|
-
module NRSER::Refinements; end
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Definitions
|
|
22
|
-
# =======================================================================
|
|
23
|
-
|
|
24
1
|
# Instance methods that are refined in to the Ruby built-ins that we consider
|
|
25
2
|
# trees: {Array}, {Hash} and {OpenStruct}.
|
|
26
3
|
#
|
|
27
|
-
module NRSER::
|
|
4
|
+
module NRSER::Ext::Tree
|
|
28
5
|
|
|
29
6
|
# Sends `self` to {NRSER.leaves}.
|
|
30
7
|
def leaves
|
|
@@ -59,5 +36,4 @@ module NRSER::Refinements::Tree
|
|
|
59
36
|
NRSER.map_tree self, **options, &block
|
|
60
37
|
end
|
|
61
38
|
|
|
62
|
-
end # module NRSER::
|
|
63
|
-
|
|
39
|
+
end # module NRSER::Ext::Tree
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Require everything in `//lib/nrser/functions` - the core functional
|
|
3
|
+
# interfaces that underlie all the refinements.
|
|
4
|
+
##
|
|
5
|
+
|
|
6
|
+
require_relative './functions/proc'
|
|
7
|
+
require_relative './functions/object'
|
|
8
|
+
require_relative './functions/string'
|
|
9
|
+
require_relative './functions/text'
|
|
10
|
+
require_relative './functions/binding'
|
|
11
|
+
require_relative './functions/exception'
|
|
12
|
+
require_relative './functions/enumerable'
|
|
13
|
+
require_relative './functions/hash'
|
|
14
|
+
require_relative './functions/array'
|
|
15
|
+
require_relative './functions/open_struct'
|
|
16
|
+
require_relative './functions/merge_by'
|
|
17
|
+
require_relative './functions/tree'
|
|
18
|
+
require_relative './functions/path'
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
require_relative './enumerable/find_map'
|
|
2
|
+
require_relative './enumerable/find_all_map'
|
|
3
|
+
|
|
4
|
+
module NRSER
|
|
5
|
+
|
|
6
|
+
# @!group Enumerable Functions
|
|
7
|
+
|
|
8
|
+
# Test if an object is "array-like" - is it an Enumerable and does it respond
|
|
9
|
+
# to `#each_index`?
|
|
10
|
+
#
|
|
11
|
+
# @param [Object] object
|
|
12
|
+
# Any old thing.
|
|
13
|
+
#
|
|
14
|
+
# @return [Boolean]
|
|
15
|
+
# `true` if `object` is "array-like" for our purposes.
|
|
16
|
+
#
|
|
17
|
+
def self.array_like? object
|
|
18
|
+
object.is_a?( ::Enumerable ) &&
|
|
19
|
+
object.respond_to?( :each_index )
|
|
20
|
+
end # .array_like?
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Test if an object is "hash-like" - is it an Enumerable and does it respond
|
|
24
|
+
# to `#each_pair`?
|
|
25
|
+
#
|
|
26
|
+
# @param [Object] object
|
|
27
|
+
# Any old thing.
|
|
28
|
+
#
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
# `true` if `object` is "hash-like" for our purposes.
|
|
31
|
+
#
|
|
32
|
+
def self.hash_like? object
|
|
33
|
+
object.is_a?( ::Enumerable ) &&
|
|
34
|
+
object.respond_to?( :each_pair )
|
|
35
|
+
end # .hash_like?
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Maps an enumerable object to a *new* hash with the same keys and values
|
|
39
|
+
# obtained by calling `block` with the current key and value.
|
|
40
|
+
#
|
|
41
|
+
# If `enumerable` *does not* respond to `#to_pairs` then it's
|
|
42
|
+
# treated as a hash where the elements iterated by `#each` are it's keys
|
|
43
|
+
# and all it's values are `nil`.
|
|
44
|
+
#
|
|
45
|
+
# In this way, {NRSER.map_values} handles Hash, Array, Set, OpenStruct,
|
|
46
|
+
# and probably pretty much anything else reasonable you may throw at it.
|
|
47
|
+
#
|
|
48
|
+
# @param [#each_pair, #each] enum
|
|
49
|
+
#
|
|
50
|
+
# @yieldparam [Object] key
|
|
51
|
+
# The key that will be used for whatever value the block returns in the
|
|
52
|
+
# new hash.
|
|
53
|
+
#
|
|
54
|
+
# @yieldparam [nil, Object] value
|
|
55
|
+
# If `enumerable` responds to `#each_pair`, the second parameter it yielded
|
|
56
|
+
# along with `key`. Otherwise `nil`.
|
|
57
|
+
#
|
|
58
|
+
# @yieldreturn [Object]
|
|
59
|
+
# Value for the new hash.
|
|
60
|
+
#
|
|
61
|
+
# @return [Hash]
|
|
62
|
+
#
|
|
63
|
+
# @raise [TypeError]
|
|
64
|
+
# If `enumerable` does not respond to `#each_pair` or `#each`.
|
|
65
|
+
#
|
|
66
|
+
def self.map_values enum, &block
|
|
67
|
+
result = {}
|
|
68
|
+
|
|
69
|
+
if enum.respond_to? :each_pair
|
|
70
|
+
enum.each_pair { |key, value|
|
|
71
|
+
result[key] = block.call key, value
|
|
72
|
+
}
|
|
73
|
+
elsif enum.respond_to? :each
|
|
74
|
+
enum.each { |key|
|
|
75
|
+
result[key] = block.call key, nil
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
raise ArgumentError.new erb binding, <<-END
|
|
79
|
+
First argument to {NRSER.map_values} must respond to #each_pair or #each
|
|
80
|
+
|
|
81
|
+
Received
|
|
82
|
+
|
|
83
|
+
<%= enum.pretty_inspect %>
|
|
84
|
+
|
|
85
|
+
of class <%= enum.class %>
|
|
86
|
+
END
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
result
|
|
90
|
+
end # .map_values
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Find all entries in an {Enumerable} for which `&block` returns a truthy
|
|
94
|
+
# value, then check the amount of results found against the
|
|
95
|
+
# {NRSER::Types.length} created from `bounds`, raising a {TypeError} if
|
|
96
|
+
# the results' length doesn't satisfy the bounds type.
|
|
97
|
+
#
|
|
98
|
+
# @param [Enumerable<E>] enum
|
|
99
|
+
# The entries to search and check.
|
|
100
|
+
#
|
|
101
|
+
# @param [Integer | Hash] bounds
|
|
102
|
+
# Passed as only argument to {NRSER::Types.length} to create the length
|
|
103
|
+
# type the results are checked against.
|
|
104
|
+
#
|
|
105
|
+
# @param [Proc] &block
|
|
106
|
+
# `#find`/`#find_all`-style block that will be called with each entry
|
|
107
|
+
# from `enum`. Truthy responses mean the entry matched.
|
|
108
|
+
#
|
|
109
|
+
# @return [Array<E>]
|
|
110
|
+
# Found entries from `enum`.
|
|
111
|
+
#
|
|
112
|
+
# @raise [TypeError]
|
|
113
|
+
# If the results of `enum.find_all &block` don't satisfy `bounds`.
|
|
114
|
+
#
|
|
115
|
+
def self.find_bounded enum, bounds, &block
|
|
116
|
+
NRSER::Types.
|
|
117
|
+
length(bounds).
|
|
118
|
+
check(enum.find_all &block) { |type:, value:|
|
|
119
|
+
erb binding, <<-END
|
|
120
|
+
|
|
121
|
+
Length of found elements (<%= value.length %>) FAILED to
|
|
122
|
+
satisfy <%= type.to_s %>.
|
|
123
|
+
|
|
124
|
+
Found entries:
|
|
125
|
+
|
|
126
|
+
<%= value.pretty_inspect %>
|
|
127
|
+
|
|
128
|
+
from enumerable:
|
|
129
|
+
|
|
130
|
+
<%= enum.pretty_inspect %>
|
|
131
|
+
|
|
132
|
+
END
|
|
133
|
+
}
|
|
134
|
+
end # .find_bounded
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# Find the only entry in `enum` for which `&block` responds truthy, raising
|
|
138
|
+
# if either no entries or more than one are found.
|
|
139
|
+
#
|
|
140
|
+
# Returns the entry itself, not an array of length 1.
|
|
141
|
+
#
|
|
142
|
+
# Just calls {NRSER.find_bounded} with `bounds = 1`.
|
|
143
|
+
#
|
|
144
|
+
# @param enum (see NRSER.find_bounded)
|
|
145
|
+
# @param &block (see NRSER.find_bounded)
|
|
146
|
+
#
|
|
147
|
+
# @return [E]
|
|
148
|
+
# Only entry in `enum` that `&block` matched.
|
|
149
|
+
#
|
|
150
|
+
# @raise [TypeError]
|
|
151
|
+
# If `&block` matched more or less than one entry.
|
|
152
|
+
#
|
|
153
|
+
def self.find_only enum, &block
|
|
154
|
+
find_bounded(enum, 1, &block).first
|
|
155
|
+
end # .find_only
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# Return the first entry if the enumerable has `#count` one.
|
|
159
|
+
#
|
|
160
|
+
# Otherwise, return `default` (which defaults to `nil`).
|
|
161
|
+
#
|
|
162
|
+
# @param [Enumerable<E>] enum
|
|
163
|
+
# Enumerable in question (really, anything that responds to `#first` and
|
|
164
|
+
# `#count`).
|
|
165
|
+
#
|
|
166
|
+
# @param [D] default:
|
|
167
|
+
# Value to return if `enum` does not have only one entry.
|
|
168
|
+
#
|
|
169
|
+
# @return [E]
|
|
170
|
+
# When `enum` has `#count == 1`.
|
|
171
|
+
#
|
|
172
|
+
# @return [D]
|
|
173
|
+
# When `enum` does not have `#count == 1`.
|
|
174
|
+
#
|
|
175
|
+
def self.only enum, default: nil
|
|
176
|
+
if enum.count == 1
|
|
177
|
+
enum.first
|
|
178
|
+
else
|
|
179
|
+
default
|
|
180
|
+
end
|
|
181
|
+
end # .only
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Return the only entry if the enumerable has `#count` one. Otherwise
|
|
185
|
+
# raise an error.
|
|
186
|
+
#
|
|
187
|
+
# @param enum (see NRSER.only)
|
|
188
|
+
#
|
|
189
|
+
# @return [E]
|
|
190
|
+
# First element of `enum`.
|
|
191
|
+
#
|
|
192
|
+
# @raise [ArgumentError]
|
|
193
|
+
# If `enum` does not have `#count == 1`.
|
|
194
|
+
#
|
|
195
|
+
def self.only! enum
|
|
196
|
+
unless enum.count == 1
|
|
197
|
+
raise ArgumentError.new erb binding, <<-END
|
|
198
|
+
Expected enumerable to have #count == 1 but it has
|
|
199
|
+
|
|
200
|
+
#count = <%= enum.count %>
|
|
201
|
+
|
|
202
|
+
Enumerable (class: <%= enum.class %>):
|
|
203
|
+
|
|
204
|
+
<%= enum.pretty_inspect %>
|
|
205
|
+
|
|
206
|
+
END
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
enum.first
|
|
210
|
+
end # .only!
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# Convert an enumerable to a hash by passing each entry through `&block` to
|
|
214
|
+
# get it's key, raising an error if multiple entries map to the same key.
|
|
215
|
+
#
|
|
216
|
+
# @param [Enumerable<V>] enum
|
|
217
|
+
# Enumerable containing the values for the hash.
|
|
218
|
+
#
|
|
219
|
+
# @param [Proc<(V)=>K>] &block
|
|
220
|
+
# Block that maps `enum` values to their hash keys.
|
|
221
|
+
#
|
|
222
|
+
# @return [Hash<K, V>]
|
|
223
|
+
#
|
|
224
|
+
# @raise [NRSER::ConflictError]
|
|
225
|
+
# If two values map to the same key.
|
|
226
|
+
#
|
|
227
|
+
def self.to_h_by enum, &block
|
|
228
|
+
enum.each_with_object( {} ) { |element, result|
|
|
229
|
+
key = block.call element
|
|
230
|
+
|
|
231
|
+
if result.key? key
|
|
232
|
+
raise NRSER::ConflictError.new erb binding, <<-END
|
|
233
|
+
Key <%= key.inspect %> is already in results with value:
|
|
234
|
+
|
|
235
|
+
<%= result[key].pretty_inspect %>
|
|
236
|
+
|
|
237
|
+
END
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
result[key] = element
|
|
241
|
+
}
|
|
242
|
+
end # .to_h_by
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# Create an {Enumerator} that iterates over the "values" of an
|
|
246
|
+
# {Enumerable} `enum`. If `enum` responds to `#each_value` than we return
|
|
247
|
+
# that. Otherwise, we return `#each_entry`.
|
|
248
|
+
#
|
|
249
|
+
# @param [Enumerable] enum
|
|
250
|
+
#
|
|
251
|
+
# @return [Enumerator]
|
|
252
|
+
#
|
|
253
|
+
# @raise [ArgumentError]
|
|
254
|
+
# If `enum` doesn't respond to `#each_value` or `#each_entry`.
|
|
255
|
+
#
|
|
256
|
+
def self.enumerate_as_values enum
|
|
257
|
+
# NRSER.match enum,
|
|
258
|
+
# t.respond_to(:each_value), :each_value.to_proc,
|
|
259
|
+
# t.respond_to(:each_entry), :each_entry.to_proc
|
|
260
|
+
#
|
|
261
|
+
if enum.respond_to? :each_value
|
|
262
|
+
enum.each_value
|
|
263
|
+
elsif enum.respond_to? :each_entry
|
|
264
|
+
enum.each_entry
|
|
265
|
+
else
|
|
266
|
+
raise ArgumentError.new erb binding, <<-END
|
|
267
|
+
Expected `enum` arg to respond to :each_value or :each_entry, found:
|
|
268
|
+
|
|
269
|
+
<%= enum.inspect %>
|
|
270
|
+
|
|
271
|
+
END
|
|
272
|
+
end
|
|
273
|
+
end # .enumerate_as_values
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# Count entries in an {Enumerable} by the value returned when they are
|
|
277
|
+
# passed to the block.
|
|
278
|
+
#
|
|
279
|
+
# @example Count array entries by class
|
|
280
|
+
#
|
|
281
|
+
# [1, 2, :three, 'four', 5, :six].count_by &:class
|
|
282
|
+
# # => {Fixnum=>3, Symbol=>2, String=>1}
|
|
283
|
+
#
|
|
284
|
+
# @param [Enumerable<E>] enum
|
|
285
|
+
# {Enumerable} (or other object with compatible `#each_with_object` and
|
|
286
|
+
# `#to_enum` methods) you want to count.
|
|
287
|
+
#
|
|
288
|
+
# @param [Proc<(E)=>C>] &block
|
|
289
|
+
# Block mapping entries in `enum` to the group to count them in.
|
|
290
|
+
#
|
|
291
|
+
# @return [Hash{C=>Integer}]
|
|
292
|
+
# Hash mapping groups to positive integer counts.
|
|
293
|
+
#
|
|
294
|
+
def self.count_by enum, &block
|
|
295
|
+
enum.each_with_object( Hash.new 0 ) do |entry, hash|
|
|
296
|
+
hash[block.call entry] += 1
|
|
297
|
+
end
|
|
298
|
+
end # .count_by
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# Like `Enumerable#find`, but wraps each call to `&block` in a
|
|
302
|
+
# `begin` / `rescue`, returning the result of the first call that doesn't
|
|
303
|
+
# raise an error.
|
|
304
|
+
#
|
|
305
|
+
# If no calls succeed, raises a {NRSER::MultipleErrors} containing the
|
|
306
|
+
# errors from the block calls.
|
|
307
|
+
#
|
|
308
|
+
# @param [Enumerable<E>] enum
|
|
309
|
+
# Values to call `&block` with.
|
|
310
|
+
#
|
|
311
|
+
# @param [Proc<E=>V>] &block
|
|
312
|
+
# Block to call, which is expected to raise an error if it fails.
|
|
313
|
+
#
|
|
314
|
+
# @return [V]
|
|
315
|
+
# Result of first call to `&block` that doesn't raise.
|
|
316
|
+
#
|
|
317
|
+
# @raise [ArgumentError]
|
|
318
|
+
# If `enum` was empty (`enum#each` never yielded).
|
|
319
|
+
#
|
|
320
|
+
# @raise [NRSER::MultipleErrors]
|
|
321
|
+
# If all calls to `&block` failed.
|
|
322
|
+
#
|
|
323
|
+
def self.try_find enum, &block
|
|
324
|
+
errors = []
|
|
325
|
+
|
|
326
|
+
enum.each do |*args|
|
|
327
|
+
begin
|
|
328
|
+
result = block.call *args
|
|
329
|
+
rescue Exception => error
|
|
330
|
+
errors << error
|
|
331
|
+
else
|
|
332
|
+
return result
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
if errors.empty?
|
|
337
|
+
raise ArgumentError,
|
|
338
|
+
"Appears that enumerable was empty: #{ enum.inspect }"
|
|
339
|
+
else
|
|
340
|
+
raise NRSER::MultipleErrors.new errors
|
|
341
|
+
end
|
|
342
|
+
end # .try_find
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# TODO It would be nice for this to work...
|
|
346
|
+
#
|
|
347
|
+
# def to_enum object, meth, *args
|
|
348
|
+
# unless object.respond_to?( meth )
|
|
349
|
+
# object = NRSER::Enumerable.new object
|
|
350
|
+
# end
|
|
351
|
+
#
|
|
352
|
+
# object.to_enum meth, *args
|
|
353
|
+
# end
|
|
354
|
+
|
|
355
|
+
end # module NRSER
|