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.
- checksums.yaml +4 -4
- data/lib/nrser/char/alpha_numeric_sub.rb +9 -19
- data/lib/nrser/char/special.rb +5 -5
- data/lib/nrser/core_ext/array.rb +36 -13
- data/lib/nrser/core_ext/enumerable.rb +1 -0
- data/lib/nrser/core_ext/enumerable/find_map.rb +1 -1
- data/lib/nrser/core_ext/hash/bury.rb +3 -0
- data/lib/nrser/core_ext/hash/extract_values_at.rb +2 -2
- data/lib/nrser/core_ext/method/full_name.rb +1 -1
- data/lib/nrser/core_ext/module/method_objects.rb +1 -1
- data/lib/nrser/core_ext/module/source_locations.rb +27 -15
- data/lib/nrser/core_ext/object/lazy_var.rb +1 -1
- data/lib/nrser/core_ext/pathname.rb +67 -12
- data/lib/nrser/core_ext/pathname/subpath.rb +86 -0
- data/lib/nrser/core_ext/string.rb +28 -1
- data/lib/nrser/core_ext/symbol.rb +11 -12
- data/lib/nrser/errors/README.md +154 -0
- data/lib/nrser/errors/attr_error.rb +146 -53
- data/lib/nrser/errors/count_error.rb +61 -12
- data/lib/nrser/errors/nicer_error.rb +42 -71
- data/lib/nrser/errors/value_error.rb +53 -58
- data/lib/nrser/functions.rb +0 -2
- data/lib/nrser/functions/enumerable.rb +5 -17
- data/lib/nrser/functions/enumerable/associate.rb +14 -5
- data/lib/nrser/functions/enumerable/find_all_map.rb +1 -1
- data/lib/nrser/functions/enumerable/include_slice/array_include_slice.rb +1 -1
- data/lib/nrser/functions/hash/bury.rb +2 -12
- data/lib/nrser/functions/merge_by.rb +2 -2
- data/lib/nrser/functions/module/method_objects.rb +2 -2
- data/lib/nrser/functions/path.rb +185 -165
- data/lib/nrser/functions/path/normalized.rb +84 -0
- data/lib/nrser/functions/string.rb +4 -4
- data/lib/nrser/functions/text/README.md +4 -0
- data/lib/nrser/functions/text/format.rb +53 -0
- data/lib/nrser/functions/text/indentation.rb +6 -6
- data/lib/nrser/functions/text/word_wrap.rb +2 -2
- data/lib/nrser/functions/tree/map_leaves.rb +3 -3
- data/lib/nrser/functions/tree/map_tree.rb +2 -2
- data/lib/nrser/functions/tree/transform.rb +1 -18
- data/lib/nrser/gem_ext/README.md +4 -0
- data/lib/nrser/labs/README.md +8 -0
- data/lib/nrser/labs/config.rb +163 -0
- data/lib/nrser/labs/i8.rb +49 -159
- data/lib/nrser/labs/i8/struct.rb +167 -0
- data/lib/nrser/labs/i8/struct/hash.rb +140 -0
- data/lib/nrser/labs/i8/struct/vector.rb +149 -0
- data/lib/nrser/labs/i8/surjection.rb +211 -0
- data/lib/nrser/labs/lots/consumer.rb +19 -0
- data/lib/nrser/labs/lots/parser.rb +21 -1
- data/lib/nrser/labs/stash.rb +4 -4
- data/lib/nrser/log.rb +25 -21
- data/lib/nrser/log/appender/sync.rb +15 -11
- data/lib/nrser/log/formatters/color.rb +0 -3
- data/lib/nrser/log/formatters/mixin.rb +4 -4
- data/lib/nrser/log/logger.rb +54 -6
- data/lib/nrser/log/mixin.rb +2 -1
- data/lib/nrser/log/plugin.rb +6 -6
- data/lib/nrser/log/types.rb +46 -29
- data/lib/nrser/mean_streak.rb +0 -8
- data/lib/nrser/mean_streak/document.rb +1 -4
- data/lib/nrser/message.rb +3 -3
- data/lib/nrser/meta/README.md +4 -0
- data/lib/nrser/meta/lazy_attr.rb +2 -2
- data/lib/nrser/meta/source/location.rb +1 -1
- data/lib/nrser/props.rb +34 -3
- data/lib/nrser/props/class_methods.rb +2 -1
- data/lib/nrser/props/instance_methods.rb +9 -9
- data/lib/nrser/props/metadata.rb +4 -12
- data/lib/nrser/props/mutable/stash.rb +5 -2
- data/lib/nrser/props/prop.rb +10 -19
- data/lib/nrser/rspex.rb +1 -20
- data/lib/nrser/rspex/example_group/describe_attribute.rb +3 -0
- data/lib/nrser/rspex/example_group/describe_called_with.rb +9 -4
- data/lib/nrser/rspex/example_group/describe_case.rb +1 -0
- data/lib/nrser/rspex/example_group/describe_class.rb +2 -0
- data/lib/nrser/rspex/example_group/describe_group.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_instance.rb +3 -1
- data/lib/nrser/rspex/example_group/describe_message.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_method.rb +64 -30
- data/lib/nrser/rspex/example_group/describe_response_to.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_section.rb +4 -1
- data/lib/nrser/rspex/example_group/describe_sent_to.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_setup.rb +1 -0
- data/lib/nrser/rspex/example_group/describe_source_file.rb +1 -1
- data/lib/nrser/rspex/example_group/describe_spec_file.rb +4 -2
- data/lib/nrser/rspex/example_group/describe_when.rb +2 -1
- data/lib/nrser/rspex/example_group/describe_x.rb +5 -5
- data/lib/nrser/rspex/format.rb +0 -15
- data/lib/nrser/sugar/method_missing_forwarder.rb +3 -3
- data/lib/nrser/sys/env/path.rb +2 -28
- data/lib/nrser/types.rb +63 -12
- data/lib/nrser/types/README.md +76 -0
- data/lib/nrser/types/arrays.rb +192 -137
- data/lib/nrser/types/attributes.rb +269 -0
- data/lib/nrser/types/booleans.rb +134 -83
- data/lib/nrser/types/bounded.rb +110 -47
- data/lib/nrser/types/collections.rb +119 -0
- data/lib/nrser/types/combinators.rb +283 -196
- data/lib/nrser/types/doc/display_table.md +66 -0
- data/lib/nrser/types/eqiuvalent.rb +91 -0
- data/lib/nrser/types/errors/check_error.rb +5 -11
- data/lib/nrser/types/errors/from_string_error.rb +3 -3
- data/lib/nrser/types/factory.rb +287 -20
- data/lib/nrser/types/hashes.rb +227 -179
- data/lib/nrser/types/in.rb +73 -36
- data/lib/nrser/types/is.rb +67 -60
- data/lib/nrser/types/is_a.rb +141 -84
- data/lib/nrser/types/labels.rb +45 -16
- data/lib/nrser/types/maybe.rb +6 -3
- data/lib/nrser/types/nil.rb +64 -27
- data/lib/nrser/types/not.rb +92 -34
- data/lib/nrser/types/numbers.rb +224 -169
- data/lib/nrser/types/pairs.rb +113 -89
- data/lib/nrser/types/paths.rb +250 -137
- data/lib/nrser/types/responds.rb +167 -89
- data/lib/nrser/types/selector.rb +234 -0
- data/lib/nrser/types/shape.rb +136 -65
- data/lib/nrser/types/strings.rb +189 -63
- data/lib/nrser/types/symbols.rb +83 -33
- data/lib/nrser/types/top.rb +89 -0
- data/lib/nrser/types/tuples.rb +134 -98
- data/lib/nrser/types/type.rb +617 -505
- data/lib/nrser/types/when.rb +123 -98
- data/lib/nrser/types/where.rb +182 -91
- data/lib/nrser/version.rb +1 -1
- data/spec/lib/nrser/core_ext/pathname/subpath_spec.rb +22 -0
- data/spec/lib/nrser/errors/attr_error_spec.rb +68 -0
- data/spec/lib/nrser/errors/count_error_spec.rb +69 -0
- data/spec/lib/nrser/functions/path/normalize_path_spec.rb +35 -0
- data/spec/lib/nrser/functions/tree/map_tree_spec.rb +74 -96
- data/spec/lib/nrser/functions/tree/transform_spec.rb +11 -11
- data/spec/lib/nrser/labs/config_spec.rb +22 -0
- data/spec/lib/nrser/labs/i8/struct_spec.rb +39 -0
- data/spec/lib/nrser/types/display_spec.rb +50 -0
- data/spec/lib/nrser/types/paths_spec.rb +16 -10
- data/spec/lib/nrser/types/selector_spec.rb +125 -0
- data/spec/spec_helper.rb +4 -5
- metadata +105 -22
- data/lib/nrser/types/any.rb +0 -41
- data/lib/nrser/types/attrs.rb +0 -213
- data/lib/nrser/types/trees.rb +0 -42
@@ -16,10 +16,10 @@ module NRSER
|
|
16
16
|
# @param [Boolean] include_super
|
17
17
|
# When `true`, includes inherited class methods.
|
18
18
|
#
|
19
|
-
# @param [:class | :instance] type
|
19
|
+
# @param [:class | :instance] type
|
20
20
|
# Get class or instance methods.
|
21
21
|
#
|
22
|
-
# @param [Boolean] sort
|
22
|
+
# @param [Boolean] sort
|
23
23
|
# If `true`, will sort the methods by name, which is usually
|
24
24
|
# the useful way to look at and use them.
|
25
25
|
#
|
data/lib/nrser/functions/path.rb
CHANGED
@@ -1,175 +1,195 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
5
|
+
# =======================================================================
|
6
|
+
|
7
|
+
# Stdlib
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
# Deps
|
12
|
+
# -----------------------------------------------------------------------
|
13
|
+
|
14
|
+
# Project / Package
|
15
|
+
# -----------------------------------------------------------------------
|
16
|
+
|
17
|
+
require_relative './path/normalized'
|
18
|
+
|
19
|
+
# Namespace
|
20
|
+
# =======================================================================
|
21
|
+
|
22
|
+
module NRSER
|
23
|
+
|
24
|
+
|
25
|
+
# Definitions
|
26
|
+
# =======================================================================
|
27
|
+
|
28
|
+
# @!group Path Functions
|
29
|
+
|
30
|
+
# @return [Pathname]
|
31
|
+
#
|
32
|
+
def self.pn_from path
|
33
|
+
if path.is_a? Pathname
|
34
|
+
path
|
35
|
+
else
|
36
|
+
Pathname.new path
|
12
37
|
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Get the directory for a path - if the path is a directory, it's returned
|
42
|
+
# (converted to a {Pathname}). It's not a directory, it's {Pathname#dirname}
|
43
|
+
# will be returned.
|
44
|
+
#
|
45
|
+
# Expands the path (so that `~` paths work).
|
46
|
+
#
|
47
|
+
# @param [String | Pathname] path
|
48
|
+
# File or directory path.
|
49
|
+
#
|
50
|
+
# @return [Pathname]
|
51
|
+
# Absolute directory path.
|
52
|
+
#
|
53
|
+
def self.dir_from path
|
54
|
+
pn = pn_from( path ).expand_path
|
13
55
|
|
56
|
+
if pn.directory?
|
57
|
+
pn
|
58
|
+
else
|
59
|
+
pn.dirname
|
60
|
+
end
|
61
|
+
end # .dir_from
|
62
|
+
|
63
|
+
|
64
|
+
def self.looks_globish? path
|
65
|
+
%w|* ? [ {|.any? &path.to_s.method( :include? )
|
66
|
+
end # .glob?
|
67
|
+
|
68
|
+
|
69
|
+
# Ascend the directory tree starting at `from` (defaults to working
|
70
|
+
# directory) looking for a relative path.
|
71
|
+
#
|
72
|
+
# How it works and what it returns is dependent on the sent options.
|
73
|
+
#
|
74
|
+
# In the simplest / default case:
|
75
|
+
#
|
76
|
+
# 1.
|
77
|
+
#
|
78
|
+
# @param [String | Pathname] rel_path
|
79
|
+
# Relative path to search for. Can contains glob patterns; see the `glob`
|
80
|
+
# keyword.
|
81
|
+
#
|
82
|
+
# @param [String | Pathname] from
|
83
|
+
# Where to start the search. This is the first directory checked.
|
84
|
+
#
|
85
|
+
# @param [Boolean | :guess] glob
|
86
|
+
# Controls file-glob behavior with respect to `rel_path`:
|
87
|
+
#
|
88
|
+
# - `:guess` (default) - boolean value is computed by passing `rel_path`
|
89
|
+
# to {.looks_globish?}.
|
90
|
+
#
|
91
|
+
# - `true` - {Pathname.glob} is used to search for `rel_path` in each
|
92
|
+
# directory, and the first glob result that passes the test is
|
93
|
+
# considered the match.
|
94
|
+
#
|
95
|
+
# - `false` - `rel_path` is used as a literal file path (if it has a `*`
|
96
|
+
# character it will only match paths with a literal `*` character,
|
97
|
+
# etc.)
|
98
|
+
#
|
99
|
+
# **Be mindful that glob searches can easily consume significant resources
|
100
|
+
# when using broad patterns and/or large file trees.**
|
101
|
+
#
|
102
|
+
# Basically, you probably don't *ever* want to use `**` - we walk all the
|
103
|
+
# way up to the file system root, so it would be equivalent to searching
|
104
|
+
# *the entire filesystem*.
|
105
|
+
#
|
106
|
+
# @todo
|
107
|
+
# There should be a way to cut the search off early or detect `**` in
|
108
|
+
# the `rel_path` and error out or something to prevent full FS search.
|
109
|
+
#
|
110
|
+
# @param [Symbol] test
|
111
|
+
# The test to perform on pathnames to see if they match. Defaults to
|
112
|
+
# `:exist?` - which calls {Pathname#exist?} - but could be `:directory?`
|
113
|
+
# or anything else that makes sense.
|
114
|
+
#
|
115
|
+
# @param [Symbol] result
|
116
|
+
# What information to return:
|
117
|
+
#
|
118
|
+
# - `:common_root` (default) - return the directory that the match was
|
119
|
+
# relative to, so the return value is `from` or a ancestor of it.
|
120
|
+
#
|
121
|
+
# - `:path` - return the full path that was matched.
|
122
|
+
#
|
123
|
+
# - `:pair` - return the `:common_root` value followed by the `:path`
|
124
|
+
# value in a two-element {Array}.
|
125
|
+
#
|
126
|
+
# @return [nil]
|
127
|
+
# When no match is found.
|
128
|
+
#
|
129
|
+
# @return [Pathname]
|
130
|
+
# When a match is found and `result` keyword is
|
131
|
+
#
|
132
|
+
# - `:common_root` - the directory in `from.ascend` the match was made
|
133
|
+
# from.
|
134
|
+
#
|
135
|
+
# - `:path` - the path to the matched file.
|
136
|
+
#
|
137
|
+
# @return [Array<(Pathname, Pathname)>]
|
138
|
+
# When a match is found and `result` keyword is `:pair`, the directory
|
139
|
+
# the match was relative to followed by the matched path.
|
140
|
+
#
|
141
|
+
def self.find_up(
|
142
|
+
rel_path,
|
143
|
+
from: Pathname.pwd,
|
144
|
+
glob: :guess,
|
145
|
+
test: :exist?,
|
146
|
+
result: :common_root
|
147
|
+
)
|
148
|
+
# If `glob` is `:guess`, override `glob` with the result of
|
149
|
+
# {.looks_globish?}
|
150
|
+
#
|
151
|
+
glob = looks_globish?( rel_path ) if glob == :guess
|
14
152
|
|
15
|
-
|
16
|
-
|
17
|
-
# will be returned.
|
18
|
-
#
|
19
|
-
# Expands the path (so that `~` paths work).
|
20
|
-
#
|
21
|
-
# @param [String | Pathname] path
|
22
|
-
# File or directory path.
|
23
|
-
#
|
24
|
-
# @return [Pathname]
|
25
|
-
# Absolute directory path.
|
26
|
-
#
|
27
|
-
def self.dir_from path
|
28
|
-
pn = pn_from( path ).expand_path
|
153
|
+
found = pn_from( from ).ascend.find_map { |dir|
|
154
|
+
path = dir / rel_path
|
29
155
|
|
30
|
-
if
|
31
|
-
|
156
|
+
found_path = if glob
|
157
|
+
Pathname.glob( path ).find { |match_path|
|
158
|
+
match_path.public_send test
|
159
|
+
}
|
160
|
+
elsif path.public_send( test )
|
161
|
+
path
|
32
162
|
else
|
33
|
-
|
163
|
+
nil
|
34
164
|
end
|
35
|
-
end # .dir_from
|
36
|
-
|
37
|
-
|
38
|
-
# @todo Document glob? method.
|
39
|
-
#
|
40
|
-
# @param [type] arg_name
|
41
|
-
# @todo Add name param description.
|
42
|
-
#
|
43
|
-
# @return [return_type]
|
44
|
-
# @todo Document return value.
|
45
|
-
#
|
46
|
-
def self.looks_globish? path
|
47
|
-
%w|* ? [ {|.any? &path.to_s.method( :include? )
|
48
|
-
end # .glob?
|
49
|
-
|
50
|
-
|
51
|
-
# Ascend the directory tree starting at `from` (defaults to working
|
52
|
-
# directory) looking for a relative path.
|
53
|
-
#
|
54
|
-
# How it works and what it returns is dependent on the sent options.
|
55
|
-
#
|
56
|
-
# In the simplest / default case:
|
57
|
-
#
|
58
|
-
# 1.
|
59
|
-
#
|
60
|
-
# @param [String | Pathname] rel_path
|
61
|
-
# Relative path to search for. Can contains glob patterns; see the `glob`
|
62
|
-
# keyword.
|
63
|
-
#
|
64
|
-
# @param [String | Pathname] from:
|
65
|
-
# Where to start the search. This is the first directory checked.
|
66
|
-
#
|
67
|
-
# @param [Boolean | :guess] glob:
|
68
|
-
# Controls file-glob behavior with respect to `rel_path`:
|
69
|
-
#
|
70
|
-
# - `:guess` (default) - boolean value is computed by passing `rel_path`
|
71
|
-
# to {.looks_globish?}.
|
72
|
-
#
|
73
|
-
# - `true` - {Pathname.glob} is used to search for `rel_path` in each
|
74
|
-
# directory, and the first glob result that passes the test is
|
75
|
-
# considered the match.
|
76
|
-
#
|
77
|
-
# - `false` - `rel_path` is used as a literal file path (if it has a `*`
|
78
|
-
# character it will only match paths with a literal `*` character,
|
79
|
-
# etc.)
|
80
|
-
#
|
81
|
-
# **Be mindful that glob searches can easily consume significant resources
|
82
|
-
# when using broad patterns and/or large file trees.**
|
83
|
-
#
|
84
|
-
# Basically, you probably don't *ever* want to use `**` - we walk all the
|
85
|
-
# way up to the file system root, so it would be equivalent to searching
|
86
|
-
# *the entire filesystem*.
|
87
|
-
#
|
88
|
-
# @todo
|
89
|
-
# There should be a way to cut the search off early or detect `**` in
|
90
|
-
# the `rel_path` and error out or something to prevent full FS search.
|
91
|
-
#
|
92
|
-
# @param [Symbol] test:
|
93
|
-
# The test to perform on pathnames to see if they match. Defaults to
|
94
|
-
# `:exist?` - which calls {Pathname#exist?} - but could be `:directory?`
|
95
|
-
# or anything else that makes sense.
|
96
|
-
#
|
97
|
-
# @param [Symbol] result:
|
98
|
-
# What information to return:
|
99
|
-
#
|
100
|
-
# - `:common_root` (default) - return the directory that the match was
|
101
|
-
# relative to, so the return value is `from` or a ancestor of it.
|
102
|
-
#
|
103
|
-
# - `:path` - return the full path that was matched.
|
104
|
-
#
|
105
|
-
# - `:pair` - return the `:common_root` value followed by the `:path`
|
106
|
-
# value in a two-element {Array}.
|
107
|
-
#
|
108
|
-
# @return [nil]
|
109
|
-
# When no match is found.
|
110
|
-
#
|
111
|
-
# @return [Pathname]
|
112
|
-
# When a match is found and `result` keyword is
|
113
|
-
#
|
114
|
-
# - `:common_root` - the directory in `from.ascend` the match was made
|
115
|
-
# from.
|
116
|
-
#
|
117
|
-
# - `:path` - the path to the matched file.
|
118
|
-
#
|
119
|
-
# @return [Array<(Pathname, Pathname)>]
|
120
|
-
# When a match is found and `result` keyword is `:pair`, the directory
|
121
|
-
# the match was relative to followed by the matched path.
|
122
|
-
#
|
123
|
-
def self.find_up(
|
124
|
-
rel_path,
|
125
|
-
from: Pathname.pwd,
|
126
|
-
glob: :guess,
|
127
|
-
test: :exist?,
|
128
|
-
result: :common_root
|
129
|
-
)
|
130
|
-
# If `glob` is `:guess`, override `glob` with the result of
|
131
|
-
# {.looks_globish?}
|
132
|
-
#
|
133
|
-
glob = looks_globish?( rel_path ) if glob == :guess
|
134
|
-
|
135
|
-
found = pn_from( from ).ascend.find_map { |dir|
|
136
|
-
path = dir / rel_path
|
137
|
-
|
138
|
-
found_path = if glob
|
139
|
-
Pathname.glob( path ).find { |match_path|
|
140
|
-
match_path.public_send test
|
141
|
-
}
|
142
|
-
elsif path.public_send( test )
|
143
|
-
path
|
144
|
-
else
|
145
|
-
nil
|
146
|
-
end
|
147
|
-
|
148
|
-
unless found_path.nil?
|
149
|
-
[dir, found_path]
|
150
|
-
end
|
151
|
-
}
|
152
|
-
|
153
|
-
return nil if found.nil?
|
154
165
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
:pair, found,
|
160
|
-
:path, path
|
161
|
-
end
|
166
|
+
unless found_path.nil?
|
167
|
+
[dir, found_path]
|
168
|
+
end
|
169
|
+
}
|
162
170
|
|
171
|
+
return nil if found.nil?
|
163
172
|
|
164
|
-
|
165
|
-
#
|
166
|
-
def self.find_up! *args
|
167
|
-
find_up( *args ).tap { |result|
|
168
|
-
if result.nil?
|
169
|
-
raise "HERE! #{ args.inspect }"
|
170
|
-
end
|
171
|
-
}
|
172
|
-
end
|
173
|
+
dir, path = found
|
173
174
|
|
174
|
-
|
175
|
-
|
175
|
+
Types.match result,
|
176
|
+
:common_root, dir,
|
177
|
+
:pair, found,
|
178
|
+
:path, path
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# Exactly like {NRSER.find_up} but raises if nothing is found.
|
183
|
+
#
|
184
|
+
def self.find_up! *args
|
185
|
+
find_up( *args ).tap { |result|
|
186
|
+
if result.nil?
|
187
|
+
raise "HERE! #{ args.inspect }"
|
188
|
+
end
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
# /Namespace
|
193
|
+
# =======================================================================
|
194
|
+
|
195
|
+
end # module NRSER
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
5
|
+
# =======================================================================
|
6
|
+
|
7
|
+
# Stdlib
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
# Project / Package
|
12
|
+
# -----------------------------------------------------------------------
|
13
|
+
require 'nrser/errors/type_error'
|
14
|
+
|
15
|
+
|
16
|
+
# Namespace
|
17
|
+
# =======================================================================
|
18
|
+
|
19
|
+
module NRSER
|
20
|
+
|
21
|
+
|
22
|
+
# Definitions
|
23
|
+
# =======================================================================
|
24
|
+
|
25
|
+
# Test if a path is what I'm calling "normalized" - generally free of any
|
26
|
+
# `.`, `..` or empty segments, with specific exceptions for `'/'` and `'.'`.
|
27
|
+
#
|
28
|
+
# @param [String | Pathname] path
|
29
|
+
# Path to test.
|
30
|
+
#
|
31
|
+
# @return [Boolean]
|
32
|
+
# `true` if we consider the path "normalized".
|
33
|
+
#
|
34
|
+
# @raise [NRSER::TypeError]
|
35
|
+
# If `path` is not a {String} or {Pathname}.
|
36
|
+
#
|
37
|
+
def self.normalized_path? path
|
38
|
+
string = case path
|
39
|
+
when String
|
40
|
+
path
|
41
|
+
when Pathname
|
42
|
+
path.to_s
|
43
|
+
else
|
44
|
+
raise NRSER::TypeError.new \
|
45
|
+
"path must be String or Pathname, found", path,
|
46
|
+
expected: [ String, Pathname ],
|
47
|
+
found: path
|
48
|
+
end
|
49
|
+
|
50
|
+
# Examine each segment
|
51
|
+
|
52
|
+
# NOTE The `-1` is *extremely* important - it stops suppression of empty
|
53
|
+
# entries in the result, and we need them!
|
54
|
+
segments = string.split File::SEPARATOR, -1
|
55
|
+
|
56
|
+
segments.
|
57
|
+
# We need the indexes, since the first and last segments can be empty,
|
58
|
+
# corresponding to `/...` and `.../` paths, respectively.
|
59
|
+
each_with_index.
|
60
|
+
# See if they all meet the requirements
|
61
|
+
all? { |segment, index|
|
62
|
+
(
|
63
|
+
segment != '.' || # Can't have any `.../x/./y/...` business
|
64
|
+
index == 0 # But we can have `./x/y/` and such
|
65
|
+
) &&
|
66
|
+
segment != '..' && # Can't have any `.../x/../y/...` crap either
|
67
|
+
(
|
68
|
+
# and, finally, the segment can't be empty
|
69
|
+
segment != '' ||
|
70
|
+
# unless it's the first (`/x/...` case)
|
71
|
+
index == 0 ||
|
72
|
+
# or the last segment (`.../z/` case)
|
73
|
+
index == segments.length - 1
|
74
|
+
)
|
75
|
+
}
|
76
|
+
end # .normalized_path?
|
77
|
+
|
78
|
+
singleton_class.send :alias_method, :norm_path?, :normalized_path?
|
79
|
+
|
80
|
+
|
81
|
+
# /Namespace
|
82
|
+
# =======================================================================
|
83
|
+
|
84
|
+
end # module NRSER
|