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/string_version.rb
CHANGED
@@ -1,29 +1,147 @@
|
|
1
1
|
module Tins
|
2
|
+
# Provides version string parsing and comparison functionality.
|
3
|
+
#
|
4
|
+
# This module allows working with semantic version strings in a more intuitive way,
|
5
|
+
# supporting operations like comparison, incrementing, and accessing individual components.
|
6
|
+
#
|
7
|
+
# @example Basic usage
|
8
|
+
# version = "1.2.3".version
|
9
|
+
# puts version.major # => 1
|
10
|
+
# puts version.minor # => 2
|
11
|
+
# puts version.build # => 3
|
12
|
+
#
|
13
|
+
# @example Comparison operations
|
14
|
+
# v1 = "1.2.3".version
|
15
|
+
# v2 = "1.2.4".version
|
16
|
+
# puts v1 < v2 # => true
|
17
|
+
# puts v1 == v2 # => false
|
18
|
+
#
|
19
|
+
# @example Version manipulation
|
20
|
+
# version = "1.2.3".version
|
21
|
+
# version.bump(:minor) # => "1.3.0"
|
22
|
+
# version.succ! # increments last component
|
2
23
|
module StringVersion
|
3
|
-
|
24
|
+
# Map of version level symbols to their numeric indices
|
25
|
+
LEVELS = [ :major, :minor, :build, :revision ].each_with_index.
|
4
26
|
each_with_object({}) { |(k, v), h| h[k] = v }.freeze
|
5
27
|
|
28
|
+
# Inverted map of LEVELS for symbol lookup
|
6
29
|
SYMBOLS = LEVELS.invert.freeze
|
7
30
|
|
31
|
+
# Represents a version string with semantic comparison capabilities
|
32
|
+
#
|
33
|
+
# @example Creating a Version object
|
34
|
+
# version = Tins::StringVersion::Version.new("1.2.3")
|
35
|
+
#
|
36
|
+
# @example Accessing components
|
37
|
+
# v = "1.2.3".version
|
38
|
+
# puts v.major # => 1
|
39
|
+
# puts v.minor # => 2
|
40
|
+
# puts v.build # => 3
|
8
41
|
class Version
|
9
42
|
include Comparable
|
10
43
|
|
44
|
+
# Creates a new version object from a string representation
|
45
|
+
#
|
46
|
+
# @param string [String] The version string (e.g., "1.2.3")
|
47
|
+
# @raise [ArgumentError] If the string doesn't match a valid version pattern
|
48
|
+
# @example
|
49
|
+
# Tins::StringVersion::Version.new("1.2.3")
|
50
|
+
# # => #<Tins::StringVersion::Version:0x00007f8b8c0b0a80 @version="1.2.3">
|
11
51
|
def initialize(string)
|
12
52
|
string =~ /\A\d+(\.\d+)*\z/ or
|
13
53
|
raise ArgumentError, "#{string.inspect} isn't a version number"
|
14
54
|
@version = string.frozen? ? string.dup : string
|
15
55
|
end
|
16
56
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
57
|
+
# Returns the major version component
|
58
|
+
#
|
59
|
+
# @return [Integer] The major version number
|
60
|
+
# @example
|
61
|
+
# "1.2.3".version.major # => 1
|
62
|
+
def major
|
63
|
+
self[0]
|
64
|
+
end
|
21
65
|
|
22
|
-
|
23
|
-
|
24
|
-
|
66
|
+
# Sets the major version component
|
67
|
+
#
|
68
|
+
# @param new_level [Integer] The new major version number
|
69
|
+
# @return [Integer] The updated major version number
|
70
|
+
# @example
|
71
|
+
# v = "1.2.3".version
|
72
|
+
# v.major = 5 # sets major to 5
|
73
|
+
def major=(new_level)
|
74
|
+
self[0] = new_level
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the minor version component
|
78
|
+
#
|
79
|
+
# @return [Integer] The minor version number
|
80
|
+
# @example
|
81
|
+
# "1.2.3".version.minor # => 2
|
82
|
+
def minor
|
83
|
+
self[1]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sets the minor version component
|
87
|
+
#
|
88
|
+
# @param new_level [Integer] The new minor version number
|
89
|
+
# @return [Integer] The updated minor version number
|
90
|
+
# @example
|
91
|
+
# v = "1.2.3".version
|
92
|
+
# v.minor = 5 # sets minor to 5
|
93
|
+
def minor=(new_level)
|
94
|
+
self[1] = new_level
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns the build version component
|
98
|
+
#
|
99
|
+
# @return [Integer] The build version number
|
100
|
+
# @example
|
101
|
+
# "1.2.3".version.build # => 3
|
102
|
+
def build
|
103
|
+
self[2]
|
25
104
|
end
|
26
105
|
|
106
|
+
# Sets the build version component
|
107
|
+
#
|
108
|
+
# @param new_level [Integer] The new build version number
|
109
|
+
# @return [Integer] The updated build version number
|
110
|
+
# @example
|
111
|
+
# v = "1.2.3".version
|
112
|
+
# v.build = 5 # sets build to 5
|
113
|
+
def build=(new_level)
|
114
|
+
self[2] = new_level
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the revision version component
|
118
|
+
#
|
119
|
+
# @return [Integer] The revision version number
|
120
|
+
# @example
|
121
|
+
# "1.2.3".version.revision # => nil (no revision component)
|
122
|
+
def revision
|
123
|
+
self[3]
|
124
|
+
end
|
125
|
+
|
126
|
+
# Sets the revision version component
|
127
|
+
#
|
128
|
+
# @param new_level [Integer] The new revision version number
|
129
|
+
# @return [Integer] The updated revision version number
|
130
|
+
# @example
|
131
|
+
# v = "1.2.3".version
|
132
|
+
# v.revision = 5 # sets revision to 5
|
133
|
+
def revision=(new_level)
|
134
|
+
self[3] = new_level
|
135
|
+
end
|
136
|
+
|
137
|
+
# Increments a specified version component and resets subsequent components
|
138
|
+
#
|
139
|
+
# @param level [Symbol, Integer] The level to bump (default: last component)
|
140
|
+
# @return [self] Returns self for chaining
|
141
|
+
# @example
|
142
|
+
# v = "1.2.3".version
|
143
|
+
# v.bump(:minor) # => "1.3.0"
|
144
|
+
# v.bump # => "1.3.1" (bumps last component)
|
27
145
|
def bump(level = array.size - 1)
|
28
146
|
level = level_of(level)
|
29
147
|
self[level] += 1
|
@@ -33,6 +151,10 @@ module Tins
|
|
33
151
|
self
|
34
152
|
end
|
35
153
|
|
154
|
+
# Converts a symbolic level to its numeric index
|
155
|
+
#
|
156
|
+
# @param specifier [Symbol, Integer] The level specification
|
157
|
+
# @return [Integer] The corresponding numeric index
|
36
158
|
def level_of(specifier)
|
37
159
|
if specifier.respond_to?(:to_sym)
|
38
160
|
LEVELS.fetch(specifier)
|
@@ -41,10 +163,20 @@ module Tins
|
|
41
163
|
end
|
42
164
|
end
|
43
165
|
|
166
|
+
# Gets a version component by index or symbol
|
167
|
+
#
|
168
|
+
# @param level [Symbol, Integer] The level to retrieve
|
169
|
+
# @return [Integer] The version component value
|
44
170
|
def [](level)
|
45
171
|
array[level_of(level)]
|
46
172
|
end
|
47
173
|
|
174
|
+
# Sets a version component by index or symbol
|
175
|
+
#
|
176
|
+
# @param level [Symbol, Integer] The level to set
|
177
|
+
# @param value [Integer] The new value for the component
|
178
|
+
# @return [Integer] The updated value
|
179
|
+
# @raise [ArgumentError] If value is negative
|
48
180
|
def []=(level, value)
|
49
181
|
level = level_of(level)
|
50
182
|
value = value.to_i
|
@@ -56,16 +188,26 @@ module Tins
|
|
56
188
|
@version.replace a * ?.
|
57
189
|
end
|
58
190
|
|
191
|
+
# Increments the last version component
|
192
|
+
#
|
193
|
+
# @return [self] Returns self for chaining
|
59
194
|
def succ!
|
60
195
|
self[-1] += 1
|
61
196
|
self
|
62
197
|
end
|
63
198
|
|
199
|
+
# Decrements the last version component
|
200
|
+
#
|
201
|
+
# @return [self] Returns self for chaining
|
64
202
|
def pred!
|
65
203
|
self[-1] -= 1
|
66
204
|
self
|
67
205
|
end
|
68
206
|
|
207
|
+
# Compares this version with another
|
208
|
+
#
|
209
|
+
# @param other [Tins::StringVersion::Version] The version to compare against
|
210
|
+
# @return [Integer] -1 if self < other, 0 if equal, 1 if self > other
|
69
211
|
def <=>(other)
|
70
212
|
pairs = array.zip(other.array)
|
71
213
|
pairs.map! { |a, b| [ a.to_i, b.to_i ] }
|
@@ -73,36 +215,67 @@ module Tins
|
|
73
215
|
a <=> b
|
74
216
|
end
|
75
217
|
|
218
|
+
# Checks equality with another version
|
219
|
+
#
|
220
|
+
# @param other [Tins::StringVersion::Version] The version to compare against
|
221
|
+
# @return [Boolean] true if versions are equal
|
76
222
|
def ==(other)
|
77
223
|
(self <=> other).zero?
|
78
224
|
end
|
79
225
|
|
226
|
+
# Converts the version to an array of integers
|
227
|
+
#
|
228
|
+
# @return [Array<Integer>] Array representation of the version
|
80
229
|
def array
|
81
230
|
@version.split(?.).map(&:to_i)
|
82
231
|
end
|
83
232
|
|
233
|
+
# Alias for {#array}
|
84
234
|
alias to_a array
|
85
235
|
|
236
|
+
# Returns the string representation of this version
|
237
|
+
#
|
238
|
+
# @return [String] The version string
|
86
239
|
def to_s
|
87
240
|
@version
|
88
241
|
end
|
89
242
|
|
243
|
+
# Creates a copy of this version object
|
244
|
+
#
|
245
|
+
# @param source [Tins::StringVersion::Version] Source version to copy from
|
246
|
+
# @return [void]
|
90
247
|
def initialize_copy(source)
|
91
248
|
super
|
92
249
|
@version = source.instance_variable_get(:@version).dup
|
93
250
|
end
|
94
251
|
|
252
|
+
# Alias for {#to_s}
|
95
253
|
alias inspect to_s
|
96
254
|
end
|
97
255
|
|
256
|
+
# Creates a Version object from this string
|
257
|
+
#
|
258
|
+
# @return [Tins::StringVersion::Version] The version object
|
98
259
|
def version
|
99
260
|
Version.new(self)
|
100
261
|
end
|
262
|
+
|
263
|
+
# Compares two version strings using the specified operator
|
264
|
+
#
|
265
|
+
# @param version1 [String] First version string
|
266
|
+
# @param operator [Symbol] Comparison operator (:<, :<=, :==, :>=, :>)
|
267
|
+
# @param version2 [String] Second version string
|
268
|
+
# @return [Boolean] Result of the comparison
|
269
|
+
def self.compare(version1, operator, version2)
|
270
|
+
Version.new(version1).send(operator, Version.new(version2))
|
271
|
+
end
|
101
272
|
end
|
102
273
|
|
274
|
+
# Creates a Version object directly from a string-like object
|
275
|
+
#
|
276
|
+
# @param string [String] The version string
|
277
|
+
# @return [Tins::StringVersion::Version] The version object
|
103
278
|
def self.StringVersion(string)
|
104
279
|
StringVersion::Version.new(string.to_str)
|
105
280
|
end
|
106
281
|
end
|
107
|
-
|
108
|
-
require 'tins/alias'
|
data/lib/tins/subhash.rb
CHANGED
@@ -1,14 +1,41 @@
|
|
1
1
|
module Tins
|
2
|
+
# Tins::Subhash provides methods for creating filtered subsets of hashes
|
3
|
+
# based on pattern matching against keys.
|
4
|
+
#
|
5
|
+
# The subhash method allows you to extract key-value pairs from a hash
|
6
|
+
# that match specified patterns, making it easy to work with subsets of
|
7
|
+
# hash data based on naming conventions or other criteria.
|
8
|
+
#
|
9
|
+
# @example Basic usage with string patterns
|
10
|
+
# hash = { 'foo' => 1, 'bar' => 2, 'foobar' => 3 }
|
11
|
+
# sub = hash.subhash('foo')
|
12
|
+
# # => { 'foo' => 1, 'foobar' => 3 }
|
13
|
+
#
|
14
|
+
# @example Usage with regular expressions
|
15
|
+
# hash = { 'foo' => 1, 'barfoot' => 2, 'foobar' => 3 }
|
16
|
+
# sub = hash.subhash(/^foo/)
|
17
|
+
# # => { 'foo' => 1, 'foobar' => 3 }
|
18
|
+
#
|
19
|
+
# @example Usage with block for value transformation
|
20
|
+
# hash = { 'foo' => 1, 'bar' => 2, 'foobar' => 3 }
|
21
|
+
# sub = hash.subhash('foo') { |key, value, match_data| value * 2 }
|
22
|
+
# # => { 'foo' => 2, 'foobar' => 6 }
|
2
23
|
module Subhash
|
3
|
-
# Create a subhash from this hash
|
4
|
-
#
|
5
|
-
# to put 'foobar' and 'foobaz' or 'foo'/:foo to put 'foo' into the subhash.
|
24
|
+
# Create a subhash from this hash containing only key-value pairs
|
25
|
+
# where the key matches any of the given patterns.
|
6
26
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
27
|
+
# Patterns can be:
|
28
|
+
# - Regular expressions (matched against key strings)
|
29
|
+
# - Strings (exact string matching)
|
30
|
+
# - Symbols (converted to strings for matching)
|
31
|
+
# - Any object that implements === operator
|
32
|
+
#
|
33
|
+
# @param patterns [Array<Object>] One or more patterns to match against keys
|
34
|
+
# @yield [key, value, match_data] Optional block to transform values
|
35
|
+
# @yieldparam key [String] The original hash key
|
36
|
+
# @yieldparam value [Object] The original hash value
|
37
|
+
# @yieldparam match_data [MatchData, nil] Match data when pattern is regex, nil otherwise
|
38
|
+
# @return [Hash] A new hash containing only matching key-value pairs
|
12
39
|
def subhash(*patterns)
|
13
40
|
patterns.map! do |pat|
|
14
41
|
pat = pat.to_sym.to_s if pat.respond_to?(:to_sym)
|
@@ -38,5 +65,3 @@ module Tins
|
|
38
65
|
end
|
39
66
|
end
|
40
67
|
end
|
41
|
-
|
42
|
-
require 'tins/alias'
|
data/lib/tins/temp_io.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
require 'tmpdir'
|
2
2
|
|
3
3
|
module Tins
|
4
|
+
# A module for creating temporary files and handling their contents securely.
|
4
5
|
module TempIO
|
6
|
+
# Creates a temporary file with the given content and yields it to a block.
|
7
|
+
#
|
8
|
+
# @param content [String, #call] the content to write to the temporary file
|
9
|
+
# @param name [String, Symbol] the base name for the temporary file
|
10
|
+
# @yield [io] yields the temporary file handle to the given block
|
11
|
+
# @return [Object] the return value of the block
|
5
12
|
def temp_io(content: nil, name: __method__)
|
6
13
|
content.nil? and raise ArgumentError, "missing keyword: content"
|
7
14
|
name = File.basename(name.to_s)
|
data/lib/tins/temp_io_enum.rb
CHANGED
@@ -2,9 +2,28 @@ require 'tins/temp_io'
|
|
2
2
|
|
3
3
|
module Tins
|
4
4
|
module TempIO
|
5
|
+
# A streaming enumerator that reads file content in configurable chunks
|
6
|
+
# from temporary files.
|
7
|
+
#
|
8
|
+
# This class provides an efficient way to process large files without
|
9
|
+
# loading them entirely into memory. It creates a temporary file from the
|
10
|
+
# provided content generator and yields data in fixed-size chunks.
|
5
11
|
class Enum < Enumerator
|
6
12
|
include Tins::TempIO
|
7
13
|
|
14
|
+
# This method creates an enumerator that yields chunks of data from a
|
15
|
+
# temporary file generated from the provided content proc. It's designed
|
16
|
+
# for streaming large files efficiently (to a user's web browser for
|
17
|
+
# example) by reading them in fixed-size chunks rather than loading
|
18
|
+
# everything into memory.
|
19
|
+
#
|
20
|
+
# @param chunk_size [ Integer ] the size of each chunk to read from the
|
21
|
+
# file
|
22
|
+
# @param filename [ String, nil ] optional filename to associate with the
|
23
|
+
# enumerator
|
24
|
+
# @param content_proc [ Proc ] a block that generates file content
|
25
|
+
# @return [ Enumerator ] an enumerator that yields file chunks,
|
26
|
+
# eventually having #filename defined as the parameter filename.
|
8
27
|
def initialize(chunk_size: 2 ** 16, filename: nil, &content_proc)
|
9
28
|
content_proc or raise ArgumentError, 'need a content proc as block argument'
|
10
29
|
super() do |y|
|
data/lib/tins/terminal.rb
CHANGED
@@ -4,15 +4,25 @@ rescue LoadError
|
|
4
4
|
end
|
5
5
|
|
6
6
|
module Tins
|
7
|
+
# A module for handling terminal-related functionality and terminal
|
8
|
+
# input/output operations.
|
9
|
+
#
|
10
|
+
# Provides methods for interacting with terminal capabilities, reading from
|
11
|
+
# standard input, and managing terminal sessions.
|
7
12
|
module Terminal
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
# Returns the window size of the console.
|
14
|
+
#
|
15
|
+
# This method attempts to retrieve the terminal window dimensions by
|
16
|
+
# accessing the console object and its winsize method.
|
17
|
+
#
|
18
|
+
# @return [ Array<Integer> ] an array containing the rows and columns
|
19
|
+
# of the terminal window, or an empty array if the console is not
|
20
|
+
# available or does not support winsize querying
|
11
21
|
def winsize
|
12
22
|
if IO.respond_to?(:console)
|
13
|
-
|
14
|
-
if
|
15
|
-
|
23
|
+
c = IO.console
|
24
|
+
if c.respond_to?(:winsize)
|
25
|
+
c.winsize
|
16
26
|
else
|
17
27
|
[]
|
18
28
|
end
|
@@ -21,22 +31,34 @@ module Tins
|
|
21
31
|
end
|
22
32
|
end
|
23
33
|
|
34
|
+
# Returns the number of rows (lines) in the terminal window.
|
35
|
+
#
|
36
|
+
# Attempts to determine the terminal size by checking various sources in
|
37
|
+
# order of preference: window size, stty command output, tput command
|
38
|
+
# output, and defaults to 25 lines if all methods fail.
|
39
|
+
#
|
40
|
+
# @return [ Integer ] the number of terminal rows, or 25 as fallback
|
24
41
|
def rows
|
25
42
|
winsize[0] || `stty size 2>/dev/null`.split[0].to_i.nonzero? ||
|
26
43
|
`tput lines 2>/dev/null`.to_i.nonzero? || 25
|
27
44
|
end
|
28
45
|
|
29
|
-
|
30
|
-
rows
|
31
|
-
end
|
46
|
+
alias lines rows
|
32
47
|
|
48
|
+
# Returns the number of columns in the terminal
|
49
|
+
#
|
50
|
+
# This method attempts to determine the terminal width by checking various sources
|
51
|
+
# in order of preference: system winsize information, stty output, tput output,
|
52
|
+
# and falls back to a default of 80 columns if none are available
|
53
|
+
#
|
54
|
+
# @return [Integer] the number of columns in the terminal or 80 as fallback
|
33
55
|
def columns
|
34
56
|
winsize[1] || `stty size 2>/dev/null`.split[1].to_i.nonzero? ||
|
35
57
|
`tput cols 2>/dev/null`.to_i.nonzero? || 80
|
36
58
|
end
|
37
59
|
|
38
|
-
|
39
|
-
|
40
|
-
|
60
|
+
alias cols columns
|
61
|
+
|
62
|
+
extend self
|
41
63
|
end
|
42
64
|
end
|
data/lib/tins/thread_local.rb
CHANGED
@@ -1,23 +1,68 @@
|
|
1
1
|
module Tins
|
2
|
+
# Provides thread-local storage capabilities for classes and modules.
|
3
|
+
# Thread-local variables are scoped to individual threads, allowing each
|
4
|
+
# thread to maintain its own copy of the variable value.
|
5
|
+
#
|
6
|
+
# @example Basic usage with a class
|
7
|
+
# class MyClass
|
8
|
+
# extend Tins::ThreadLocal
|
9
|
+
#
|
10
|
+
# thread_local :counter, 0
|
11
|
+
# thread_local :name, "default"
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # Each thread gets its own copy of the variables
|
15
|
+
# t1 = Thread.new { puts MyClass.new.counter } # => 0
|
16
|
+
# t2 = Thread.new { puts MyClass.new.counter } # => 0
|
17
|
+
# t1.join; t2.join
|
18
|
+
#
|
19
|
+
# @example Usage with default blocks
|
20
|
+
# class Config
|
21
|
+
# extend Tins::ThreadLocal
|
22
|
+
#
|
23
|
+
# thread_local :database_url do
|
24
|
+
# ENV['DATABASE_URL'] || 'sqlite3://default.db'
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @example Instance-level thread local variables
|
29
|
+
# class MyClass
|
30
|
+
# include Tins::ThreadLocal
|
31
|
+
#
|
32
|
+
# instance_thread_local :user_id, 0
|
33
|
+
# end
|
2
34
|
module ThreadLocal
|
3
|
-
|
4
|
-
|
35
|
+
# Cleanup lambda that removes thread-local data when objects are garbage
|
36
|
+
# collected
|
5
37
|
@@cleanup = lambda do |my_object_id|
|
6
38
|
my_id = "__thread_local_#{my_object_id}__"
|
7
|
-
|
8
|
-
|
9
|
-
t[my_id] = nil if t[my_id]
|
10
|
-
end
|
39
|
+
for t in Thread.list
|
40
|
+
t[my_id] = nil if t[my_id]
|
11
41
|
end
|
12
42
|
end
|
13
43
|
|
14
|
-
# Define a thread local variable named
|
15
|
-
# value
|
44
|
+
# Define a thread local variable named +name+ in this module/class.
|
45
|
+
# If the value +value+ is given, it is used to initialize the variable.
|
46
|
+
#
|
47
|
+
# @param name [Symbol, String] The name of the thread-local variable
|
48
|
+
# @param default_value [Object] Optional default value for the variable
|
49
|
+
# @yield [void] Optional block that returns the default value
|
50
|
+
# @return [self]
|
51
|
+
# @raise [TypeError] If receiver is not a Module
|
52
|
+
# @raise [ArgumentError] If both default_value and default block are provided
|
53
|
+
#
|
54
|
+
# @example With static default value
|
55
|
+
# thread_local :counter, 0
|
56
|
+
#
|
57
|
+
# @example With dynamic default value via block
|
58
|
+
# thread_local :timestamp do
|
59
|
+
# Time.now
|
60
|
+
# end
|
16
61
|
def thread_local(name, default_value = nil, &default)
|
17
62
|
is_a?(Module) or raise TypeError, "receiver has to be a Module"
|
18
63
|
|
19
64
|
default_value && default and raise ArgumentError,
|
20
|
-
"require either
|
65
|
+
"require either default_value or default block"
|
21
66
|
|
22
67
|
if default_value
|
23
68
|
default = -> * { default_value }
|
@@ -44,13 +89,26 @@ module Tins
|
|
44
89
|
self
|
45
90
|
end
|
46
91
|
|
47
|
-
# Define a thread local variable for the current instance with name
|
48
|
-
# If the value
|
92
|
+
# Define a thread local variable for the current instance with name +name+.
|
93
|
+
# If the value +value+ is given, it is used to initialize the variable.
|
94
|
+
#
|
95
|
+
# @param name [Symbol, String] The name of the thread-local variable
|
96
|
+
# @param default_value [Object] Optional default value for the variable
|
97
|
+
# @yield [void] Optional block that returns the default value
|
98
|
+
# @return [self]
|
99
|
+
#
|
100
|
+
# @example Basic usage
|
101
|
+
# class MyClass
|
102
|
+
# include Tins::ThreadLocal
|
103
|
+
#
|
104
|
+
# instance_thread_local :user_id, 0
|
105
|
+
# end
|
49
106
|
def instance_thread_local(name, default_value = nil, &default)
|
50
107
|
class << self
|
51
108
|
extend Tins::ThreadLocal
|
52
109
|
self
|
53
110
|
end.thread_local name, default_value, &default
|
111
|
+
|
54
112
|
self
|
55
113
|
end
|
56
114
|
end
|
data/lib/tins/time_dummy.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
+
require 'tins/string_version'
|
1
2
|
require 'time'
|
2
3
|
|
3
4
|
module Tins
|
5
|
+
# A module that provides time dummy functionality for testing and development
|
6
|
+
# purposes.
|
7
|
+
#
|
8
|
+
# This module allows setting a fake current time that can be used in tests or
|
9
|
+
# development environments where you want to control the time returned by
|
10
|
+
# Time.now.
|
4
11
|
module TimeDummy
|
12
|
+
# The included method is a hook that gets called when this module is
|
13
|
+
# included in another class or module.
|
14
|
+
#
|
15
|
+
# It sets up time freezing functionality by extending the including
|
16
|
+
# class/module with special time handling methods. The method modifies the
|
17
|
+
# including class/module's singleton class to provide dummy time
|
18
|
+
# capabilities.
|
19
|
+
#
|
20
|
+
# @param modul [Object] the class or module that includes this module
|
5
21
|
def self.included(modul)
|
6
22
|
class << modul
|
7
23
|
alias really_new new
|
@@ -10,6 +26,12 @@ module Tins
|
|
10
26
|
remove_method :now rescue nil
|
11
27
|
remove_method :new rescue nil
|
12
28
|
|
29
|
+
# Sets the dummy time value for time freezing functionality.
|
30
|
+
#
|
31
|
+
# This method allows setting a specific time value that will be used
|
32
|
+
# as the frozen time when time freezing is enabled.
|
33
|
+
#
|
34
|
+
# @param value [Time, String] the time value to set as dummy
|
13
35
|
def dummy=(value)
|
14
36
|
if value.respond_to?(:to_str)
|
15
37
|
value = Time.parse(value.to_str)
|
@@ -19,6 +41,12 @@ module Tins
|
|
19
41
|
@dummy = value
|
20
42
|
end
|
21
43
|
|
44
|
+
# The dummy method manages a dummy value for testing purposes.
|
45
|
+
#
|
46
|
+
# @param value [Object] the dummy value to set, or nil to get the current value
|
47
|
+
# @return [Object] the current dummy value when value is nil
|
48
|
+
# @yield [] executes the block with the dummy value set
|
49
|
+
# @return [Object] the return value of the block if a block is given
|
22
50
|
def dummy(value = nil)
|
23
51
|
if value.nil?
|
24
52
|
if defined?(@dummy)
|
@@ -35,28 +63,28 @@ module Tins
|
|
35
63
|
end
|
36
64
|
end
|
37
65
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
else
|
55
|
-
really_new(*a)
|
56
|
-
end
|
66
|
+
# The new method creates a new time instance, either by duplicating
|
67
|
+
# the dummy time or calling the real creation method.
|
68
|
+
#
|
69
|
+
# @param a [ Array ] the arguments to pass to the real creation
|
70
|
+
# method
|
71
|
+
# @param kw [ Hash ] the keyword arguments to pass to the real
|
72
|
+
# creation method
|
73
|
+
#
|
74
|
+
# @return [ Time ] the newly created time instance
|
75
|
+
def new(*a, **kw)
|
76
|
+
if dummy
|
77
|
+
dummy.dup
|
78
|
+
elsif caller.first =~ /`now`/
|
79
|
+
really_now(**kw)
|
80
|
+
else
|
81
|
+
really_new(*a, **kw)
|
57
82
|
end
|
58
83
|
end
|
59
84
|
|
85
|
+
# The now method returns a new instance of the current class.
|
86
|
+
#
|
87
|
+
# @return [Object] a new instance of the class this method is called on
|
60
88
|
def now
|
61
89
|
new
|
62
90
|
end
|
@@ -65,5 +93,3 @@ module Tins
|
|
65
93
|
end
|
66
94
|
end
|
67
95
|
end
|
68
|
-
|
69
|
-
require 'tins/alias'
|
data/lib/tins/to.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
module Tins
|
2
|
+
# Provides a simple way to remove common leading whitespace from multi-line
|
3
|
+
# strings, mostly for to(<<-EOT), if require "tins/xt/to". Today you would
|
4
|
+
# probably use <<~EOT in this case.
|
5
|
+
#
|
6
|
+
# Example usage:
|
7
|
+
# doc = to(<<-EOT)
|
8
|
+
# hello
|
9
|
+
# world
|
10
|
+
# end
|
11
|
+
# EOT
|
12
|
+
# # => "hello\n world\nend"
|
2
13
|
module To
|
14
|
+
# Remove common leading whitespace from multi-line strings
|
15
|
+
#
|
16
|
+
# @param string [String] The multi-line string to deindent
|
17
|
+
# @return [String] String with common leading whitespace removed
|
3
18
|
def to(string)
|
4
19
|
shift_width = (string[/\A\s*/]).size
|
5
20
|
string.gsub(/^[^\S\n]{0,#{shift_width}}/, '')
|