nrser 0.1.3 → 0.1.4
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 +10 -12
- data/lib/nrser/ext.rb +1 -0
- data/lib/nrser/ext/enumerable.rb +6 -0
- data/lib/nrser/ext/string.rb +62 -0
- data/lib/nrser/functions/enumerable.rb +1 -0
- data/lib/nrser/functions/enumerable/include_slice.rb +84 -0
- data/lib/nrser/functions/enumerable/include_slice/array_include_slice.rb +80 -0
- data/lib/nrser/functions/text.rb +1 -0
- data/lib/nrser/functions/text/words.rb +23 -0
- data/lib/nrser/labs.rb +13 -0
- data/lib/nrser/labs/globlin.rb +106 -0
- data/lib/nrser/labs/index.rb +89 -0
- data/lib/nrser/{temp → labs}/unicode_math.rb +2 -2
- data/lib/nrser/{temp → labs}/where.rb +2 -4
- data/lib/nrser/meta/props.rb +13 -7
- data/lib/nrser/meta/props/prop.rb +144 -86
- data/lib/nrser/refinements/exception.rb +2 -2
- data/lib/nrser/refinements/string.rb +1 -51
- data/lib/nrser/rspex/example_group/describe_spec_file.rb +20 -0
- data/lib/nrser/version.rb +21 -1
- data/spec/nrser/functions/enumerable/include_slice_spec.rb +28 -0
- data/spec/nrser/functions/text/words_spec.rb +17 -0
- data/spec/nrser/labs/globlin_spec.rb +61 -0
- data/spec/nrser/labs/index_spec.rb +87 -0
- data/spec/spec_helper.rb +16 -0
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10930bb8b3998cc81f95333c8eb86a102262d84e
|
4
|
+
data.tar.gz: ff5057aead5992fe1d053e814afd120278b80a4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f64f47ddbbb195eb13f072aec1f0beb8ff8299dbf9bc40838a2dcb078fd401fc03509c6566f497041ca0b3d5fc95f4b0baee0e7cb390e7238182a0e14e51ee50
|
7
|
+
data.tar.gz: ba2da8fe57e5d3e9f2c1439fa978957327982624c1a536b1bbf950901783d66808c44042d0f01f4ce7c8ec4333a93fb29904878c3165bc010dd099eddd864265
|
data/lib/nrser.rb
CHANGED
@@ -17,10 +17,11 @@ require 'yaml'
|
|
17
17
|
require 'logger'
|
18
18
|
require 'singleton'
|
19
19
|
|
20
|
-
|
21
20
|
# Deps
|
22
21
|
# -----------------------------------------------------------------------
|
23
22
|
require 'hamster'
|
23
|
+
require 'semantic_logger'
|
24
|
+
|
24
25
|
|
25
26
|
# Hi there!
|
26
27
|
#
|
@@ -47,28 +48,25 @@ require 'hamster'
|
|
47
48
|
# Enjoy!
|
48
49
|
#
|
49
50
|
module NRSER
|
50
|
-
|
51
|
-
# Absolute, expanded path to the gem's root directory.
|
52
|
-
#
|
53
|
-
# @return [Pathname]
|
54
|
-
#
|
55
|
-
ROOT = ( Pathname.new(__FILE__).dirname / '..' ).expand_path
|
56
|
-
|
51
|
+
include SemanticLogger::Loggable
|
57
52
|
end
|
58
53
|
|
59
|
-
# 1. Load up
|
54
|
+
# 1. Load up version, which has {NRSER::ROOT} in it and depends on nothing
|
55
|
+
# else
|
56
|
+
require_relative './nrser/version'
|
57
|
+
|
58
|
+
# 2. Load up extension mixins first - they don't invoke anything, just define
|
60
59
|
# methods
|
61
60
|
require_relative './nrser/ext'
|
62
61
|
|
63
|
-
#
|
62
|
+
# 3. Then load up the refinements, which either include the extension mixins
|
64
63
|
# or directly define proxies and methods (but don't execute them).
|
65
64
|
#
|
66
65
|
# This way everything else should be able to use them.
|
67
66
|
#
|
68
67
|
require_relative './nrser/refinements'
|
69
68
|
|
70
|
-
#
|
71
|
-
require_relative './nrser/version'
|
69
|
+
# 4. Then everything else...
|
72
70
|
require_relative './nrser/char'
|
73
71
|
require_relative './nrser/errors'
|
74
72
|
require_relative './nrser/no_arg'
|
data/lib/nrser/ext.rb
CHANGED
data/lib/nrser/ext/enumerable.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Extension methods for {String}
|
2
|
+
#
|
3
|
+
module NRSER::Ext::String
|
4
|
+
|
5
|
+
def squish
|
6
|
+
NRSER.squish self
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def unblock
|
11
|
+
NRSER.unblock self
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def dedent
|
16
|
+
NRSER.dedent self
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def indent *args
|
21
|
+
NRSER.indent self, *args
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def truncate *args
|
26
|
+
NRSER.truncate self, *args
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# See {NRSER.constantize}
|
31
|
+
def constantize
|
32
|
+
NRSER.constantize self
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :to_const, :constantize
|
36
|
+
|
37
|
+
|
38
|
+
# @return [Pathname]
|
39
|
+
# Convert self into a {Pathname}
|
40
|
+
#
|
41
|
+
def to_pn
|
42
|
+
Pathname.new self
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def whitespace?
|
47
|
+
NRSER.whitespace? self
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Calls {NRSER.ellipsis} on `self`.
|
52
|
+
def ellipsis *args
|
53
|
+
NRSER.ellipsis self, *args
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Calls {NRSER.words} on `self`
|
58
|
+
def words *args, &block
|
59
|
+
NRSER::words self, *args, &block
|
60
|
+
end
|
61
|
+
|
62
|
+
end # module NRSER::Ext::String
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative './include_slice/array_include_slice'
|
2
|
+
|
3
|
+
using NRSER
|
4
|
+
|
5
|
+
module NRSER
|
6
|
+
|
7
|
+
# @!group Enumerable Functions
|
8
|
+
|
9
|
+
# See if an `enum` includes a `slice`, using an optional block to do custom
|
10
|
+
# matching.
|
11
|
+
#
|
12
|
+
# @example Order matters
|
13
|
+
# NRSER.slice? [1, 2, 3], [2, 3]
|
14
|
+
# # => true
|
15
|
+
#
|
16
|
+
# NRSER.slice? [1, 2, 3], [3, 2]
|
17
|
+
# # => false
|
18
|
+
#
|
19
|
+
# @example The empty slice is always present
|
20
|
+
# NRSER.slice? [1, 2, 3], []
|
21
|
+
# # => true
|
22
|
+
#
|
23
|
+
# NRSER.slice? [], []
|
24
|
+
# # => true
|
25
|
+
#
|
26
|
+
# @example Custom `&is_match` block to prefix-match
|
27
|
+
# NRSER.slice?(
|
28
|
+
# ['neil', 'mica', 'hudie'],
|
29
|
+
# ['m', 'h']
|
30
|
+
# ) { |enum_entry, slice_entry|
|
31
|
+
# enum_entry.start_with? slice_entry
|
32
|
+
# }
|
33
|
+
# # => true
|
34
|
+
#
|
35
|
+
# @note
|
36
|
+
# Right now, just forwards to {NRSER.array_include_slice?}, which requires
|
37
|
+
# that the {Enumerable}s support {Array}-like `#length` and `#slice`. I
|
38
|
+
# took a swing at the general case but it came out messy and only partially
|
39
|
+
# correct.
|
40
|
+
#
|
41
|
+
# @param [Enumerable] enum
|
42
|
+
# Sequence to search in.
|
43
|
+
#
|
44
|
+
# @param [Enumerable] slice
|
45
|
+
# Slice to search for.
|
46
|
+
#
|
47
|
+
# @return [Boolean]
|
48
|
+
# `true` if `enum` has a slice matching `slice`.
|
49
|
+
#
|
50
|
+
def self.include_slice? enum, slice, &is_match
|
51
|
+
# Check that both args are {Enumerable}
|
52
|
+
unless Enumerable === enum &&
|
53
|
+
Enumerable === slice
|
54
|
+
raise TypeError.new binding.erb <<-END
|
55
|
+
Both `enum` and `slice` must be {Enumerable}
|
56
|
+
|
57
|
+
enum (<%= enum.class.name %>):
|
58
|
+
|
59
|
+
<%= enum.pretty_inspect %>
|
60
|
+
|
61
|
+
slice (<%= slice.class.name %>):
|
62
|
+
|
63
|
+
<%= slice.pretty_inspect %>
|
64
|
+
|
65
|
+
END
|
66
|
+
end
|
67
|
+
|
68
|
+
if [enum, slice].all? { |e|
|
69
|
+
e.respond_to?( :length ) && e.respond_to?( :slice )
|
70
|
+
}
|
71
|
+
return array_include_slice? enum, slice, &is_match
|
72
|
+
end
|
73
|
+
|
74
|
+
raise NotImplementedError.new binding.erb <<-END
|
75
|
+
Sorry, but general {Enumerable} slice include has not been implemented
|
76
|
+
|
77
|
+
It's kinda complicated, or at least seems that way at first, so I'm
|
78
|
+
going to punt for now...
|
79
|
+
END
|
80
|
+
end
|
81
|
+
|
82
|
+
singleton_class.send :alias_method, :slice?, :include_slice?
|
83
|
+
|
84
|
+
end # module NRSER
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
module NRSER
|
3
|
+
|
4
|
+
# @!group Enumerable Functions
|
5
|
+
|
6
|
+
# Test slice inclusion when both the `slice` and the `enum` that we're
|
7
|
+
# going to look for it in support `#length` and `#slice`
|
8
|
+
# in the same manner that {Array} does (hence the name).
|
9
|
+
#
|
10
|
+
# This is much simpler and more efficient than the "general" {Enumerable}
|
11
|
+
# case where we can't necessarily find out how many entries are in the
|
12
|
+
# enumerables or really do much of anything with them except iterate through
|
13
|
+
# (at least, with my current grasp of {Enumerable} and {Enumerator} it
|
14
|
+
# seems painfully complex... in fact it may never terminate for infinite
|
15
|
+
# enumerables).
|
16
|
+
#
|
17
|
+
# @param [Enumerable<E> & #length & #slice] enum
|
18
|
+
# The {Enumerable} that we want test for `slice` inclusion. Must
|
19
|
+
# support `#length` and `#slice` like {Array} does.
|
20
|
+
#
|
21
|
+
# @param [Enumerable<S> & #length & #slice] slice
|
22
|
+
# The {Enumerable} slice that we want to see if `enum` includes. Must
|
23
|
+
# support `#length` and `#slice` like {Array} does.
|
24
|
+
#
|
25
|
+
# @param [Proc<(E, S)=>Boolean>] &is_match
|
26
|
+
# Optional {Proc} that accepts an entry from `enum` and an entry from
|
27
|
+
# `slice` and returns if they match.
|
28
|
+
#
|
29
|
+
# @return [Boolean]
|
30
|
+
# `true` if there is a slice of `enum` for which each entry matches the
|
31
|
+
# corresponding entry in `slice` according to `&is_match`.
|
32
|
+
#
|
33
|
+
def self.array_include_slice? enum, slice, &is_match
|
34
|
+
slice_length = slice.length
|
35
|
+
|
36
|
+
# Short-circuit on empty slice - it's *always* present
|
37
|
+
return true if slice_length == 0
|
38
|
+
|
39
|
+
enum_length = enum.length
|
40
|
+
|
41
|
+
# Short-circuit if slice is longer than enum since we can't possibly
|
42
|
+
# match
|
43
|
+
return false if slice_length > enum_length
|
44
|
+
|
45
|
+
# Create a default `#==` matcher if we weren't provided one.
|
46
|
+
if is_match.nil?
|
47
|
+
is_match = ->(enum_entry, slice_entry) {
|
48
|
+
enum_entry == slice_entry
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
enum.each_with_index do |enum_entry, enum_start_index|
|
53
|
+
# Compute index in `enum` that we would need to match up to
|
54
|
+
enum_end_index = enum_start_index + slice_length - 1
|
55
|
+
|
56
|
+
# Short-circuit if can't match (more slice entries than enum ones left)
|
57
|
+
return false if enum_end_index >= enum_length
|
58
|
+
|
59
|
+
# Create the slice to test against
|
60
|
+
enum_slice = enum[enum_start_index..enum_end_index]
|
61
|
+
|
62
|
+
# See if every entry in the slice from `enum` matches the corresponding
|
63
|
+
# one in `slice`
|
64
|
+
return true if enum_slice.zip( slice ).all?( &is_match )
|
65
|
+
|
66
|
+
# Otherwise, just continue on through `enum` looking for that first
|
67
|
+
# match until the number of `enum` entries left is too few for `slice`
|
68
|
+
# to possibly match
|
69
|
+
end
|
70
|
+
|
71
|
+
# We never matched the first `slice` entry to a `enum` entry (and `slice`
|
72
|
+
# had to be of length 1 so that the "too long" short-circuit never fired).
|
73
|
+
#
|
74
|
+
# So, we don't have a match.
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
singleton_class.send :alias_method, :array_slice?, :array_include_slice?
|
79
|
+
|
80
|
+
end # module NRSER
|
data/lib/nrser/functions/text.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
module NRSER
|
2
|
+
|
3
|
+
# Regexp {NRSER.words} uses to split strings. Probably not great but it's
|
4
|
+
# what I have for the moment.
|
5
|
+
#
|
6
|
+
# @return {Regexp}
|
7
|
+
#
|
8
|
+
SPLIT_WORDS_RE = /[\W_\-\/]+/
|
9
|
+
|
10
|
+
|
11
|
+
# Split a string into 'words' for word-based matching.
|
12
|
+
#
|
13
|
+
# @param [String] string
|
14
|
+
# Input string.
|
15
|
+
#
|
16
|
+
# @return [Array<String>]
|
17
|
+
# Array of non-empty words in `string`.
|
18
|
+
#
|
19
|
+
def self.words string
|
20
|
+
string.split(/[\W_\-\/]+/).reject { |w| w.empty? }
|
21
|
+
end # .words
|
22
|
+
|
23
|
+
end # module NRSER
|
data/lib/nrser/labs.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Namespace for new ideas I'm trying out. Totally unstable. May be gone
|
2
|
+
# completely tomorrow, etc.
|
3
|
+
#
|
4
|
+
# While, yes, the entire package is alpha, and there is no guarantee against
|
5
|
+
# change and breakage, I'm generally at a point where I'm trying to keep
|
6
|
+
# things that work mostly working.
|
7
|
+
#
|
8
|
+
# Stuff in here has no such consideration.
|
9
|
+
#
|
10
|
+
# There may be some tests for it, but they are going to be skipped by default
|
11
|
+
# and certainly won't prevent releases, if they're even run at all.
|
12
|
+
#
|
13
|
+
module NRSER::Labs; end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Requirements
|
4
|
+
# =======================================================================
|
5
|
+
|
6
|
+
# Stdlib
|
7
|
+
# -----------------------------------------------------------------------
|
8
|
+
|
9
|
+
# Deps
|
10
|
+
# -----------------------------------------------------------------------
|
11
|
+
|
12
|
+
# Project / Package
|
13
|
+
# -----------------------------------------------------------------------
|
14
|
+
|
15
|
+
|
16
|
+
# Refinements
|
17
|
+
# =======================================================================
|
18
|
+
|
19
|
+
|
20
|
+
# Declarations
|
21
|
+
# =======================================================================
|
22
|
+
|
23
|
+
module NRSER::Labs; end
|
24
|
+
|
25
|
+
|
26
|
+
# Definitions
|
27
|
+
# =======================================================================
|
28
|
+
|
29
|
+
|
30
|
+
# @todo document NRSER::Labs::Globlin class.
|
31
|
+
class NRSER::Labs::Globlin
|
32
|
+
|
33
|
+
|
34
|
+
# @todo document Matcher class.
|
35
|
+
class Matcher
|
36
|
+
def match entry
|
37
|
+
if entry.is_a?( NRSER::Globlin )
|
38
|
+
|
39
|
+
else
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end # class Matcher
|
44
|
+
|
45
|
+
|
46
|
+
# Constants
|
47
|
+
# ======================================================================
|
48
|
+
|
49
|
+
|
50
|
+
# Class Methods
|
51
|
+
# ======================================================================
|
52
|
+
|
53
|
+
|
54
|
+
# Attributes
|
55
|
+
# ======================================================================
|
56
|
+
|
57
|
+
|
58
|
+
# Constructor
|
59
|
+
# ======================================================================
|
60
|
+
|
61
|
+
# Instantiate a new `NRSER:Globlin`.
|
62
|
+
def initialize split:, ignore: nil
|
63
|
+
@split = Array split
|
64
|
+
|
65
|
+
|
66
|
+
end # #initialize
|
67
|
+
|
68
|
+
|
69
|
+
# Instance Methods
|
70
|
+
# ======================================================================
|
71
|
+
|
72
|
+
def matcher_for search_string
|
73
|
+
@split.each
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def find_only! search_string
|
78
|
+
matcher = matcher_for search_string
|
79
|
+
|
80
|
+
# Search entries that have the same seg count first
|
81
|
+
found = @seg_count_index[matcher.seg_count].find_all_map { |entry|
|
82
|
+
matcher.match entry
|
83
|
+
}
|
84
|
+
|
85
|
+
case found.length
|
86
|
+
when 0
|
87
|
+
# move on..
|
88
|
+
when 1
|
89
|
+
return found[0]
|
90
|
+
else
|
91
|
+
raise TooManyError.new found
|
92
|
+
end
|
93
|
+
|
94
|
+
# Ok, try slice matches for entries with *more* segments only
|
95
|
+
slice_matches = @seg_count_index.keys.
|
96
|
+
select { |count| count > matcher.seg_count }.
|
97
|
+
map { |key| @seg_count_index[key] }.
|
98
|
+
reduce( :+ ).
|
99
|
+
select { |entry|
|
100
|
+
matcher.match_slice entry
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end # class NRSER::Labs::Globlin
|