tins 1.32.0 → 1.44.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/.contexts/code_comment.rb +23 -0
- data/.contexts/full.rb +31 -0
- data/.contexts/lib.rb +24 -0
- data/.contexts/yard.md +92 -0
- data/.github/workflows/codeql-analysis.yml +72 -0
- data/CHANGES.md +194 -0
- data/README.md +161 -90
- data/Rakefile +23 -19
- data/examples/let.rb +8 -40
- data/examples/mail.rb +0 -1
- data/examples/ones_difference.stm +0 -1
- data/examples/turing.rb +3 -1
- data/lib/tins/alias.rb +1 -0
- data/lib/tins/annotate.rb +37 -27
- data/lib/tins/ask_and_send.rb +41 -0
- data/lib/tins/attempt.rb +39 -0
- data/lib/tins/bijection.rb +34 -0
- data/lib/tins/case_predicate.rb +21 -0
- data/lib/tins/complete.rb +16 -0
- data/lib/tins/concern.rb +100 -0
- data/lib/tins/date_dummy.rb +36 -4
- data/lib/tins/date_time_dummy.rb +34 -2
- data/lib/tins/deep_dup.rb +9 -2
- data/lib/tins/deprecate.rb +27 -0
- data/lib/tins/dslkit.rb +563 -59
- data/lib/tins/duration.rb +160 -3
- data/lib/tins/expose.rb +54 -5
- data/lib/tins/extract_last_argument_options.rb +9 -0
- data/lib/tins/file_binary.rb +108 -25
- data/lib/tins/find.rb +114 -11
- data/lib/tins/generator.rb +10 -2
- data/lib/tins/go.rb +81 -4
- data/lib/tins/hash_bfs.rb +69 -0
- data/lib/tins/hash_symbolize_keys_recursive.rb +62 -4
- data/lib/tins/hash_union.rb +47 -2
- data/lib/tins/if_predicate.rb +31 -0
- data/lib/tins/implement.rb +50 -0
- data/lib/tins/limited.rb +105 -29
- data/lib/tins/lines_file.rb +81 -2
- data/lib/tins/lru_cache.rb +54 -17
- data/lib/tins/memoize.rb +86 -58
- data/lib/tins/method_description.rb +87 -4
- data/lib/tins/minimize.rb +39 -11
- data/lib/tins/module_group.rb +27 -2
- data/lib/tins/named_set.rb +20 -0
- data/lib/tins/null.rb +86 -15
- data/lib/tins/once.rb +61 -4
- data/lib/tins/p.rb +44 -8
- data/lib/tins/partial_application.rb +66 -7
- data/lib/tins/proc_compose.rb +58 -1
- data/lib/tins/proc_prelude.rb +97 -10
- data/lib/tins/range_plus.rb +30 -2
- data/lib/tins/require_maybe.rb +36 -0
- data/lib/tins/responding.rb +39 -0
- data/lib/tins/secure_write.rb +25 -5
- data/lib/tins/sexy_singleton.rb +46 -48
- data/lib/tins/string_byte_order_mark.rb +33 -2
- data/lib/tins/string_camelize.rb +31 -2
- data/lib/tins/string_named_placeholders.rb +70 -0
- data/lib/tins/string_underscore.rb +33 -2
- data/lib/tins/string_version.rb +183 -10
- data/lib/tins/subhash.rb +35 -10
- data/lib/tins/temp_io.rb +7 -0
- data/lib/tins/temp_io_enum.rb +19 -0
- data/lib/tins/terminal.rb +34 -12
- data/lib/tins/thread_local.rb +69 -11
- data/lib/tins/time_dummy.rb +47 -21
- data/lib/tins/to.rb +15 -0
- data/lib/tins/to_proc.rb +17 -4
- data/lib/tins/token.rb +61 -2
- data/lib/tins/unit.rb +288 -149
- data/lib/tins/version.rb +1 -1
- data/lib/tins/write.rb +14 -3
- data/lib/tins/xt/blank.rb +81 -2
- data/lib/tins/xt/concern.rb +51 -0
- data/lib/tins/xt/deep_dup.rb +4 -2
- data/lib/tins/xt/deprecate.rb +5 -0
- data/lib/tins/xt/full.rb +56 -11
- data/lib/tins/xt/hash_bfs.rb +7 -0
- data/lib/tins/xt/irb.rb +46 -2
- data/lib/tins/xt/method_description.rb +0 -12
- data/lib/tins/xt/minimize.rb +7 -0
- data/lib/tins/xt/named.rb +71 -16
- data/lib/tins/xt/proc_compose.rb +4 -0
- data/lib/tins/xt/secure_write.rb +0 -4
- data/lib/tins/xt/string.rb +1 -0
- data/lib/tins/xt/string_camelize.rb +4 -2
- data/lib/tins/xt/string_named_placeholders.rb +7 -0
- data/lib/tins/xt/string_underscore.rb +4 -2
- data/lib/tins/xt/subhash.rb +11 -0
- data/lib/tins/xt/time_freezer.rb +43 -6
- data/lib/tins/xt/write.rb +0 -4
- data/lib/tins/xt.rb +3 -3
- data/lib/tins.rb +19 -3
- data/tests/annotate_test.rb +0 -1
- data/tests/bijection_test.rb +0 -1
- data/tests/concern_test.rb +63 -4
- data/tests/date_dummy_test.rb +0 -1
- data/tests/date_time_dummy_test.rb +0 -1
- data/tests/delegate_test.rb +0 -1
- data/tests/deprecate_test.rb +41 -0
- data/tests/dslkit_test.rb +15 -1
- data/tests/duration_test.rb +23 -2
- data/tests/dynamic_scope_test.rb +0 -1
- data/tests/extract_last_argument_options_test.rb +0 -1
- data/tests/find_test.rb +0 -1
- data/tests/from_module_test.rb +30 -3
- data/tests/generator_test.rb +0 -1
- data/tests/go_test.rb +0 -1
- data/tests/hash_bfs_test.rb +34 -0
- data/tests/hash_symbolize_keys_recursive_test.rb +0 -1
- data/tests/implement_test.rb +6 -9
- data/tests/limited_test.rb +12 -12
- data/tests/lines_file_test.rb +2 -1
- data/tests/lru_cache_test.rb +12 -1
- data/tests/memoize_test.rb +0 -1
- data/tests/method_description_test.rb +14 -20
- data/tests/minimize_test.rb +0 -1
- data/tests/module_group_test.rb +0 -1
- data/tests/named_set_test.rb +0 -1
- data/tests/null_test.rb +0 -1
- data/tests/partial_application_test.rb +4 -0
- data/tests/proc_prelude_test.rb +1 -1
- data/tests/require_maybe_test.rb +0 -1
- data/tests/scope_test.rb +1 -2
- data/tests/secure_write_test.rb +6 -1
- data/tests/sexy_singleton_test.rb +1 -1
- data/tests/string_named_placeholders.rb +109 -0
- data/tests/string_version_test.rb +3 -1
- data/tests/subhash_test.rb +0 -1
- data/tests/test_helper.rb +4 -7
- data/tests/time_dummy_test.rb +0 -1
- data/tests/time_freezer_test.rb +1 -1
- data/tests/to_test.rb +6 -6
- data/tests/token_test.rb +0 -1
- data/tests/unit_test.rb +0 -1
- data/tins.gemspec +18 -30
- metadata +55 -38
- data/lib/tins/count_by.rb +0 -8
- data/lib/tins/deep_const_get.rb +0 -50
- data/lib/tins/timed_cache.rb +0 -51
- data/lib/tins/uniq_by.rb +0 -10
- data/lib/tins/xt/count_by.rb +0 -11
- data/lib/tins/xt/deep_const_get.rb +0 -7
- data/lib/tins/xt/uniq_by.rb +0 -15
- data/tests/count_by_test.rb +0 -17
- data/tests/deep_const_get_test.rb +0 -37
- data/tests/uniq_by_test.rb +0 -31
- /data/{COPYING → LICENSE} +0 -0
data/lib/tins/memoize.rb
CHANGED
@@ -1,91 +1,119 @@
|
|
1
1
|
require 'tins/extract_last_argument_options'
|
2
|
+
require 'mize'
|
2
3
|
|
3
4
|
module Tins
|
5
|
+
# Provides memoization functionality for methods and functions with support
|
6
|
+
# for instance-level and class-level caching respectively.
|
7
|
+
#
|
8
|
+
# @example Basic method memoization
|
9
|
+
# class Calculator
|
10
|
+
# def expensive_calculation(x, y)
|
11
|
+
# # Some expensive computation
|
12
|
+
# x * y
|
13
|
+
# end
|
14
|
+
# memoize_method :expensive_calculation
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @example Function memoization (shared across instances)
|
18
|
+
# class MathUtils
|
19
|
+
# def self.factorial(n)
|
20
|
+
# n <= 1 ? 1 : n * factorial(n - 1)
|
21
|
+
# end
|
22
|
+
# memoize_function :factorial
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @example With freezing results
|
26
|
+
# class DataProcessor
|
27
|
+
# def process_data(input)
|
28
|
+
# # Process data and return result
|
29
|
+
# input.dup
|
30
|
+
# end
|
31
|
+
# memoize_method :process_data, freeze: true
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# @note This module is deprecated in favor of the {https://github.com/flori/mize mize} gem.
|
35
|
+
# Use `memoize method:` or `memoize function:` from the mize gem directly.
|
4
36
|
module Memoize
|
37
|
+
# Provides cache management methods for memoized functions and methods.
|
38
|
+
# This module is included in classes that use memoization functionality.
|
39
|
+
#
|
40
|
+
# @example Using cache methods directly
|
41
|
+
# class Example
|
42
|
+
# include Tins::Memoize::CacheMethods
|
43
|
+
#
|
44
|
+
# def expensive_method
|
45
|
+
# # Some expensive computation
|
46
|
+
# end
|
47
|
+
# memoize_method :expensive_method
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# obj = Example.new
|
51
|
+
# obj.memoize_cache_clear # Clear all cached values
|
52
|
+
# obj.__memoize_cache__ # Access the internal cache object
|
5
53
|
module CacheMethods
|
6
|
-
# Return the cache object.
|
54
|
+
# Return the cache object used for memoization.
|
55
|
+
#
|
56
|
+
# @return [Object] The cache instance
|
7
57
|
def __memoize_cache__
|
8
|
-
@__memoize_cache__
|
58
|
+
if @__memoize_cache__
|
59
|
+
@__memoize_cache__
|
60
|
+
else
|
61
|
+
@__memoize_cache__ = __mize_cache__
|
62
|
+
def @__memoize_cache__.empty?
|
63
|
+
@data.empty?
|
64
|
+
end
|
65
|
+
@__memoize_cache__
|
66
|
+
end
|
9
67
|
end
|
10
68
|
|
11
|
-
# Clear cached values for all methods/functions.
|
69
|
+
# Clear cached values for all methods/functions of this object.
|
70
|
+
#
|
71
|
+
# @return [self] For chaining
|
12
72
|
def memoize_cache_clear
|
13
73
|
__memoize_cache__.clear
|
14
74
|
self
|
15
75
|
end
|
16
|
-
|
17
|
-
def memoize_apply_visibility(id)
|
18
|
-
visibility = instance_eval do
|
19
|
-
case
|
20
|
-
when private_method_defined?(id)
|
21
|
-
:private
|
22
|
-
when protected_method_defined?(id)
|
23
|
-
:protected
|
24
|
-
end
|
25
|
-
end
|
26
|
-
yield
|
27
|
-
ensure
|
28
|
-
visibility and __send__(visibility, id)
|
29
|
-
end
|
30
76
|
end
|
31
77
|
|
32
78
|
class ::Module
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
79
|
+
# Memoize a method so that its return value is cached based on the arguments
|
80
|
+
# and the object instance. Each instance maintains its own cache.
|
81
|
+
#
|
82
|
+
# @param method_ids [Array<Symbol>] One or more method names to memoize
|
83
|
+
# @option opts [Boolean] :freeze (false) Whether to freeze results
|
84
|
+
# @return [Symbol, Array<Symbol>] The memoized method name(s)
|
85
|
+
#
|
86
|
+
# @deprecated Use `memoize method:` from the mize gem instead.
|
36
87
|
def memoize_method(*method_ids)
|
88
|
+
warn "[DEPRECATION] `memoize_method` is deprecated. Please use `memoize method:` from mize gem."
|
37
89
|
method_ids.extend(ExtractLastArgumentOptions)
|
38
90
|
method_ids, opts = method_ids.extract_last_argument_options
|
39
|
-
|
40
|
-
|
41
|
-
method_id = method_id.to_s.to_sym
|
42
|
-
memoize_apply_visibility method_id do
|
43
|
-
orig_method = instance_method(method_id)
|
44
|
-
__send__(:define_method, method_id) do |*args|
|
45
|
-
mc = __memoize_cache__
|
46
|
-
if mc.key?(method_id) and result = mc[method_id][args]
|
47
|
-
result
|
48
|
-
else
|
49
|
-
(mc[method_id] ||= {})[args] = result = orig_method.bind(self).call(*args)
|
50
|
-
$DEBUG and warn "#{self.class} cached method #{method_id}(#{args.inspect unless args.empty?}) = #{result.inspect} [#{__id__}]"
|
51
|
-
opts[:freeze] and result.freeze
|
52
|
-
end
|
53
|
-
result
|
54
|
-
end
|
55
|
-
end
|
91
|
+
method_ids.each do |method|
|
92
|
+
memoize method:, **opts.slice(:freeze)
|
56
93
|
end
|
94
|
+
include CacheMethods
|
57
95
|
method_ids.size == 1 ? method_ids.first : method_ids
|
58
96
|
end
|
59
97
|
|
60
98
|
include CacheMethods
|
61
99
|
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
100
|
+
# Memoize a function (class method) so that its return value is cached
|
101
|
+
# based only on the arguments, shared across all instances of the class.
|
102
|
+
#
|
103
|
+
# @param function_ids [Array<Symbol>] One or more function names to memoize
|
104
|
+
# @option opts [Boolean] :freeze (false) Whether to freeze results
|
105
|
+
# @return [Symbol, Array<Symbol>] The memoized function name(s)
|
106
|
+
#
|
107
|
+
# @deprecated Use `memoize function:` from the mize gem instead.
|
65
108
|
def memoize_function(*function_ids)
|
109
|
+
warn "[DEPRECATION] `memoize_function` is deprecated. Please use `memoize function:` from mize gem."
|
66
110
|
function_ids.extend(ExtractLastArgumentOptions)
|
67
111
|
function_ids, opts = function_ids.extract_last_argument_options
|
68
|
-
|
69
|
-
|
70
|
-
function_id = function_id.to_s.to_sym
|
71
|
-
memoize_apply_visibility function_id do
|
72
|
-
orig_function = instance_method(function_id)
|
73
|
-
__send__(:define_method, function_id) do |*args|
|
74
|
-
if mc.key?(function_id) and result = mc[function_id][args]
|
75
|
-
result
|
76
|
-
else
|
77
|
-
(mc[function_id] ||= {})[args] = result = orig_function.bind(self).call(*args)
|
78
|
-
opts[:freeze] and result.freeze
|
79
|
-
$DEBUG and warn "#{self.class} cached function #{function_id}(#{args.inspect unless args.empty?}) = #{result.inspect}"
|
80
|
-
end
|
81
|
-
result
|
82
|
-
end
|
83
|
-
end
|
112
|
+
function_ids.each do |function|
|
113
|
+
memoize function:, **opts.slice(:freeze)
|
84
114
|
end
|
85
115
|
function_ids.size == 1 ? function_ids.first : function_ids
|
86
116
|
end
|
87
117
|
end
|
88
118
|
end
|
89
119
|
end
|
90
|
-
|
91
|
-
require 'tins/alias'
|
@@ -1,64 +1,115 @@
|
|
1
1
|
module Tins
|
2
|
+
# Provides methods for describing method signatures and parameters.
|
3
|
+
#
|
4
|
+
# This module is designed to be included in method objects to provide
|
5
|
+
# introspection capabilities, particularly useful for generating
|
6
|
+
# documentation or debugging method signatures.
|
2
7
|
module MethodDescription
|
8
|
+
# Represents individual parameters with specific types and names.
|
3
9
|
class Parameters
|
10
|
+
# Base class for all parameter types.
|
4
11
|
class Parameter < Struct.new(:type, :name)
|
12
|
+
# Compares two parameters by their type.
|
13
|
+
#
|
14
|
+
# @param other [Parameter] The other parameter to compare against
|
15
|
+
# @return [Boolean] Whether the types are equal
|
5
16
|
def ==(other)
|
6
17
|
type == other.type
|
7
18
|
end
|
8
19
|
|
20
|
+
# Returns a string representation of the parameter for debugging.
|
21
|
+
#
|
22
|
+
# @return [String] A debug-friendly representation
|
9
23
|
def inspect
|
10
24
|
"#<#{self.class} #{to_s.inspect}>"
|
11
25
|
end
|
12
26
|
end
|
13
27
|
|
28
|
+
# Represents a splat parameter (*args).
|
14
29
|
class RestParameter < Parameter
|
30
|
+
# Converts the parameter to its string form.
|
31
|
+
#
|
32
|
+
# @return [String] Formatted as "*name"
|
15
33
|
def to_s
|
16
34
|
"*#{name}"
|
17
35
|
end
|
18
36
|
end
|
19
37
|
|
38
|
+
# Represents a keyword rest parameter (**kwargs).
|
20
39
|
class KeyrestParameter < Parameter
|
40
|
+
# Converts the parameter to its string form.
|
41
|
+
#
|
42
|
+
# @return [String] Formatted as "**name"
|
21
43
|
def to_s
|
22
44
|
"**#{name}"
|
23
45
|
end
|
24
46
|
end
|
25
47
|
|
48
|
+
# Represents a required parameter.
|
26
49
|
class ReqParameter < Parameter
|
50
|
+
# Converts the parameter to its string form.
|
51
|
+
#
|
52
|
+
# @return [String] The parameter name
|
27
53
|
def to_s
|
28
54
|
name.to_s
|
29
55
|
end
|
30
56
|
end
|
31
57
|
|
58
|
+
# Represents an optional parameter (with default value).
|
32
59
|
class OptParameter < Parameter
|
60
|
+
# Converts the parameter to its string form.
|
61
|
+
#
|
62
|
+
# @return [String] Formatted as "name=..."
|
33
63
|
def to_s
|
34
|
-
"#{name}
|
64
|
+
"#{name}=…"
|
35
65
|
end
|
36
66
|
end
|
37
67
|
|
68
|
+
# Represents a keyword parameter.
|
38
69
|
class KeyParameter < Parameter
|
70
|
+
# Converts the parameter to its string form.
|
71
|
+
#
|
72
|
+
# @return [String] Formatted as "name:..."
|
39
73
|
def to_s
|
40
|
-
"#{name}
|
74
|
+
"#{name}:…"
|
41
75
|
end
|
42
76
|
end
|
43
77
|
|
78
|
+
# Represents a required keyword parameter (without default).
|
44
79
|
class KeyreqParameter < Parameter
|
80
|
+
# Converts the parameter to its string form.
|
81
|
+
#
|
82
|
+
# @return [String] Formatted as "name:"
|
45
83
|
def to_s
|
46
84
|
"#{name}:"
|
47
85
|
end
|
48
86
|
end
|
49
87
|
|
88
|
+
# Represents a block parameter (&block).
|
50
89
|
class BlockParameter < Parameter
|
90
|
+
# Converts the parameter to its string form.
|
91
|
+
#
|
92
|
+
# @return [String] Formatted as "&name"
|
51
93
|
def to_s
|
52
94
|
"&#{name}"
|
53
95
|
end
|
54
96
|
end
|
55
97
|
|
98
|
+
# Represents a generic parameter type not covered by other classes.
|
56
99
|
class GenericParameter < Parameter
|
100
|
+
# Converts the parameter to its string form.
|
101
|
+
#
|
102
|
+
# @return [String] Formatted as "name:type"
|
57
103
|
def to_s
|
58
104
|
[ name, type ] * ?:
|
59
105
|
end
|
60
106
|
end
|
61
107
|
|
108
|
+
# Builds a parameter instance based on type and name.
|
109
|
+
#
|
110
|
+
# @param type [Symbol] The type of parameter (e.g., :req, :opt)
|
111
|
+
# @param name [String, Symbol] The parameter name
|
112
|
+
# @return [Parameter] An appropriate Parameter subclass or GenericParameter
|
62
113
|
def self.build(type, name)
|
63
114
|
parameter_classname = "#{type.to_s.capitalize}Parameter"
|
64
115
|
parameter_class =
|
@@ -71,38 +122,70 @@ module Tins
|
|
71
122
|
end
|
72
123
|
end
|
73
124
|
|
125
|
+
# Represents the signature of a method including all its parameters.
|
74
126
|
class Signature
|
127
|
+
# Initializes a new signature with given parameters.
|
128
|
+
#
|
129
|
+
# @param parameters [Array<Parameters::Parameter>] The list of parameters
|
75
130
|
def initialize(*parameters)
|
76
131
|
@parameters = parameters
|
77
132
|
end
|
78
133
|
|
134
|
+
# Returns the parameters associated with this signature.
|
135
|
+
#
|
136
|
+
# @return [Array<Parameters::Parameter>] The method's parameters
|
79
137
|
attr_reader :parameters
|
80
138
|
|
139
|
+
# Checks if two signatures are equal based on their parameters.
|
140
|
+
#
|
141
|
+
# @param other [Signature] The other signature to compare against
|
142
|
+
# @return [Boolean] Whether the signatures are equal
|
81
143
|
def eql?(other)
|
82
144
|
@parameters.eql? other.parameters
|
83
145
|
end
|
84
146
|
|
147
|
+
# Checks if two signatures are equal (alias for eql?).
|
148
|
+
#
|
149
|
+
# @param other [Signature] The other signature to compare against
|
150
|
+
# @return [Boolean] Whether the signatures are equal
|
85
151
|
def ==(other)
|
86
152
|
@parameters == other.parameters
|
87
153
|
end
|
88
154
|
|
155
|
+
# Pattern matching operator for comparing with a method's signature.
|
156
|
+
#
|
157
|
+
# @param method [Method] The method whose signature we're checking
|
158
|
+
# @return [Boolean] Whether this signature matches the method's signature
|
89
159
|
def ===(method)
|
90
160
|
self == method.signature
|
91
161
|
end
|
92
162
|
|
163
|
+
# Converts the signature to a comma-separated string.
|
164
|
+
#
|
165
|
+
# @return [String] The parameters formatted as a string
|
93
166
|
def to_s
|
94
167
|
@parameters * ?,
|
95
168
|
end
|
96
169
|
|
170
|
+
# Returns a detailed string representation for debugging.
|
171
|
+
#
|
172
|
+
# @return [String] A debug-friendly representation of the signature
|
97
173
|
def inspect
|
98
174
|
"#<#{self.class} (#{to_s})>"
|
99
175
|
end
|
100
176
|
end
|
101
177
|
|
178
|
+
# Retrieves the signature of the method this module is included in.
|
179
|
+
#
|
180
|
+
# @return [Signature] The method's signature
|
102
181
|
def signature
|
103
182
|
description style: :parameters
|
104
183
|
end
|
105
184
|
|
185
|
+
# Generates a human-readable description of the method.
|
186
|
+
#
|
187
|
+
# @param style [:namespace, :name, :parameters] The output format style
|
188
|
+
# @return [String, Signature] The formatted description or signature object
|
106
189
|
def description(style: :namespace)
|
107
190
|
valid_styles = %i[ namespace name parameters ]
|
108
191
|
valid_styles.include?(style) or
|
@@ -122,10 +205,10 @@ module Tins
|
|
122
205
|
return signature
|
123
206
|
end
|
124
207
|
end
|
125
|
-
result = ''
|
208
|
+
result = +''
|
126
209
|
if style == :namespace
|
127
210
|
if owner <= Module
|
128
|
-
result << receiver.to_s << ?.
|
211
|
+
result << receiver.to_s << ?.
|
129
212
|
else
|
130
213
|
result << owner.name.to_s << ?#
|
131
214
|
end
|
data/lib/tins/minimize.rb
CHANGED
@@ -1,19 +1,43 @@
|
|
1
1
|
module Tins
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# Tins::Minimize provides methods for compressing sequential data into ranges
|
3
|
+
# and expanding ranges back into individual elements.
|
4
|
+
#
|
5
|
+
# This module is designed for objects that respond to `[]` and `size` methods,
|
6
|
+
# such as Array, and whose elements respond to the `succ` method (like String
|
7
|
+
# and Numeric types). It's particularly useful for representing sequential data
|
8
|
+
# more compactly and efficiently.
|
9
|
+
#
|
10
|
+
# @example Basic minimization
|
11
|
+
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize
|
12
|
+
# # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
13
|
+
#
|
14
|
+
# @example Numeric minimization
|
15
|
+
# [ 1, 2, 3, 7, 9, 10, 11 ].minimize
|
16
|
+
# # => [ 1..3, 7..7, 9..11 ]
|
17
|
+
#
|
18
|
+
# @example Sorting before minimization
|
19
|
+
# [ 5, 1, 4, 2 ].sort.minimize
|
20
|
+
# # => [ 1..2, 4..5 ]
|
21
|
+
#
|
22
|
+
# @example Unminimization
|
23
|
+
# [ 'A'..'C', 'G'..'G', 'K'..'M' ].unminimize
|
24
|
+
# # => [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ]
|
5
25
|
module Minimize
|
6
26
|
# Returns a minimized version of this object, that is successive elements
|
7
27
|
# are substituted with ranges a..b. In the situation ..., x, y,... and y !=
|
8
28
|
# x.succ a range x..x is created, to make it easier to iterate over all the
|
9
|
-
# ranges in one run.
|
10
|
-
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
29
|
+
# ranges in one run.
|
11
30
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
31
|
+
# @return [Array<Range>] An array of ranges representing the minimized data
|
32
|
+
# @example Sequential string elements
|
33
|
+
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize
|
34
|
+
# # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
35
|
+
#
|
36
|
+
# @example Numeric elements
|
37
|
+
# [ 1, 2, 3, 7, 9, 10, 11 ].minimize
|
38
|
+
# # => [ 1..3, 7..7, 9..11 ]
|
15
39
|
def minimize
|
16
|
-
result
|
40
|
+
result = []
|
17
41
|
last_index = size - 1
|
18
42
|
size.times do |i|
|
19
43
|
result << [ self[0] ] if i == 0
|
@@ -27,6 +51,8 @@ module Tins
|
|
27
51
|
|
28
52
|
# First minimizes this object, then calls the replace method with the
|
29
53
|
# result.
|
54
|
+
#
|
55
|
+
# @return [Object] Returns self (modified in place)
|
30
56
|
def minimize!
|
31
57
|
replace minimize
|
32
58
|
end
|
@@ -35,6 +61,8 @@ module Tins
|
|
35
61
|
# [ 'A'..'C', 'G'..'G', 'K'..'M' ].unminimize # => [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ]
|
36
62
|
# and
|
37
63
|
# [ 1..2, 4..5 ].unminimize # => [ 1, 2, 4, 5 ]
|
64
|
+
#
|
65
|
+
# @return [Array] An array of individual elements expanded from ranges
|
38
66
|
def unminimize
|
39
67
|
result = []
|
40
68
|
for range in self
|
@@ -46,10 +74,10 @@ module Tins
|
|
46
74
|
end
|
47
75
|
|
48
76
|
# Invert a minimized version of this object in place.
|
77
|
+
#
|
78
|
+
# @return [Object] Returns self (modified in place)
|
49
79
|
def unminimize!
|
50
80
|
replace unminimize
|
51
81
|
end
|
52
82
|
end
|
53
83
|
end
|
54
|
-
|
55
|
-
require 'tins/alias'
|
data/lib/tins/module_group.rb
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
module Tins
|
2
|
+
# A module that allows grouping multiple modules together for type checking.
|
3
|
+
#
|
4
|
+
# This implementation creates a shared module that gets included in each of the
|
5
|
+
# specified modules, enabling the use of `===` for type checking across all
|
6
|
+
# grouped modules simultaneously.
|
2
7
|
module ModuleGroup
|
8
|
+
# Creates a new module group from the given modules.
|
9
|
+
#
|
10
|
+
# This method dynamically creates an anonymous module and includes it in
|
11
|
+
# each of the provided modules. The returned module can then be used with
|
12
|
+
# the `===` operator for type checking across all grouped modules.
|
13
|
+
#
|
14
|
+
# @note This modifies the included modules by adding the new module to their
|
15
|
+
# inheritance chain, which can have side effects in complex class hierarchies.
|
16
|
+
#
|
17
|
+
# @param modules [Array<Module>] One or more modules to include in this group
|
18
|
+
# @return [Module] A new anonymous module that is included in all passed modules
|
19
|
+
#
|
20
|
+
# @example Creating a module group
|
21
|
+
# MyGroup = Tins::ModuleGroup[Array, String, Hash]
|
22
|
+
# MyGroup === [] # => true
|
23
|
+
# MyGroup === "" # => true
|
24
|
+
# MyGroup === {} # => true
|
25
|
+
#
|
26
|
+
# case some_value
|
27
|
+
# when MyGroup
|
28
|
+
# handle_collection_or_string(some_value)
|
29
|
+
# end
|
3
30
|
def self.[](*modules)
|
4
31
|
modul = Module.new
|
5
32
|
modules.each do |m|
|
@@ -9,5 +36,3 @@ module Tins
|
|
9
36
|
end
|
10
37
|
end
|
11
38
|
end
|
12
|
-
|
13
|
-
require 'tins/alias'
|
data/lib/tins/named_set.rb
CHANGED
@@ -1,12 +1,32 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
3
|
module Tins
|
4
|
+
# NamedSet extends Ruby's Set class to include a descriptive name.
|
5
|
+
#
|
6
|
+
# This class provides all the functionality of Ruby's standard Set while
|
7
|
+
# adding a named identifier that can be useful for debugging, logging, and
|
8
|
+
# identification purposes. The name is stored as an instance variable and can
|
9
|
+
# be accessed or modified
|
10
|
+
# through the `name` accessor.
|
11
|
+
#
|
12
|
+
# @example Basic usage
|
13
|
+
# set = Tins::NamedSet.new("user_roles")
|
14
|
+
# set.add(:admin)
|
15
|
+
# set.add(:user)
|
16
|
+
# puts set.name # => "user_roles"
|
17
|
+
# puts set.to_a # => [:admin, :user]
|
4
18
|
class NamedSet < Set
|
19
|
+
# Initializes a new NamedSet with the given name.
|
20
|
+
#
|
21
|
+
# @param name [String] A descriptive name for this set
|
5
22
|
def initialize(name)
|
6
23
|
@name = name
|
7
24
|
super()
|
8
25
|
end
|
9
26
|
|
27
|
+
# Gets or sets the name of this NamedSet.
|
28
|
+
#
|
29
|
+
# @return [String] The current name of the set
|
10
30
|
attr_accessor :name
|
11
31
|
end
|
12
32
|
end
|