tins 1.43.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 +5 -8
- data/.contexts/lib.rb +0 -2
- data/CHANGES.md +12 -0
- data/README.md +158 -6
- data/Rakefile +19 -16
- data/examples/let.rb +8 -40
- data/examples/mail.rb +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 +64 -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 +12 -0
- data/lib/tins/dslkit.rb +559 -83
- data/lib/tins/duration.rb +120 -5
- data/lib/tins/expose.rb +54 -5
- data/lib/tins/extract_last_argument_options.rb +9 -0
- data/lib/tins/file_binary.rb +104 -21
- 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 +4 -2
- 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 +54 -5
- 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 +24 -4
- data/lib/tins/sexy_singleton.rb +45 -48
- data/lib/tins/string_byte_order_mark.rb +33 -2
- data/lib/tins/string_camelize.rb +31 -2
- data/lib/tins/string_underscore.rb +33 -2
- data/lib/tins/string_version.rb +179 -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 +31 -9
- data/lib/tins/thread_local.rb +67 -5
- data/lib/tins/time_dummy.rb +46 -21
- data/lib/tins/to.rb +15 -0
- data/lib/tins/to_proc.rb +17 -4
- data/lib/tins/token.rb +56 -1
- 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/full.rb +56 -11
- 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/subhash.rb +11 -0
- data/lib/tins/xt/time_freezer.rb +43 -6
- data/lib/tins/xt.rb +1 -3
- data/lib/tins.rb +16 -3
- data/tests/duration_test.rb +4 -0
- data/tests/from_module_test.rb +30 -2
- data/tests/implement_test.rb +6 -8
- data/tests/lines_file_test.rb +2 -0
- data/tests/lru_cache_test.rb +12 -0
- data/tests/method_description_test.rb +14 -20
- data/tests/partial_application_test.rb +4 -0
- data/tests/proc_prelude_test.rb +1 -1
- data/tests/scope_test.rb +1 -1
- data/tests/string_version_test.rb +2 -0
- data/tests/to_test.rb +6 -6
- data/tins.gemspec +9 -9
- metadata +23 -41
- data/lib/tins/count_by.rb +0 -21
- data/lib/tins/deep_const_get.rb +0 -64
- data/lib/tins/timed_cache.rb +0 -51
- data/lib/tins/uniq_by.rb +0 -23
- data/lib/tins/xt/count_by.rb +0 -7
- data/lib/tins/xt/deep_const_get.rb +0 -7
- data/lib/tins/xt/uniq_by.rb +0 -25
- 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_camelize.rb
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
module Tins
|
2
|
+
# Tins::StringCamelize provides methods for converting snake_case strings
|
3
|
+
# to camelCase or PascalCase format.
|
4
|
+
#
|
5
|
+
# This module contains the `camelize` method which is commonly used in Ruby
|
6
|
+
# applications, particularly in Rails-style naming conventions where developers
|
7
|
+
# need to convert between different naming styles.
|
8
|
+
#
|
9
|
+
# @example Converting snake_case to PascalCase (default)
|
10
|
+
# "snake_case_string".camelize
|
11
|
+
# # => "SnakeCaseString"
|
12
|
+
#
|
13
|
+
# @example Converting snake_case to camelCase
|
14
|
+
# "snake_case_string".camelize(:lower)
|
15
|
+
# # => "snakeCaseString"
|
16
|
+
#
|
17
|
+
# @example Handling nested module paths
|
18
|
+
# "my/module/class_name".camelize
|
19
|
+
# # => "My::Module::ClassName"
|
2
20
|
module StringCamelize
|
21
|
+
# Convert a snake_case string to camelCase or PascalCase format.
|
22
|
+
#
|
23
|
+
# This method handles various naming conventions:
|
24
|
+
# - Converts snake_case to PascalCase (default) or camelCase
|
25
|
+
# - Handles nested module paths with '/' separators
|
26
|
+
# - Supports different first letter cases
|
27
|
+
#
|
28
|
+
# @param first_letter [Symbol, Boolean] Controls capitalization of first letter
|
29
|
+
# @option first_letter :upper (default) Convert first letter to uppercase (PascalCase)
|
30
|
+
# @option first_letter :lower Convert first letter to lowercase (camelCase)
|
31
|
+
# @option first_letter true Same as :upper
|
32
|
+
# @option first_letter false Same as :lower
|
33
|
+
# @return [String] A new string in camelCase or PascalCase format
|
3
34
|
def camelize(first_letter = :upper)
|
4
35
|
case first_letter
|
5
36
|
when :upper, true
|
@@ -12,5 +43,3 @@ module Tins
|
|
12
43
|
alias camelcase camelize
|
13
44
|
end
|
14
45
|
end
|
15
|
-
|
16
|
-
require 'tins/alias'
|
@@ -1,5 +1,38 @@
|
|
1
1
|
module Tins
|
2
|
+
# Tins::StringUnderscore provides methods for converting camelCase and
|
3
|
+
# PascalCase strings to snake_case format.
|
4
|
+
#
|
5
|
+
# This module contains the `underscore` method which is commonly used in Ruby
|
6
|
+
# applications, particularly in Rails-style naming conventions where developers
|
7
|
+
# need to convert between different naming styles.
|
8
|
+
#
|
9
|
+
# @example Converting camelCase to snake_case
|
10
|
+
# "camelCaseString".underscore
|
11
|
+
# # => "camel_case_string"
|
12
|
+
#
|
13
|
+
# @example Converting PascalCase to snake_case
|
14
|
+
# "PascalCaseString".underscore
|
15
|
+
# # => "pascal_case_string"
|
16
|
+
#
|
17
|
+
# @example Handling nested modules
|
18
|
+
# "My::Module::ClassName".underscore
|
19
|
+
# # => "my/module/class_name"
|
20
|
+
#
|
21
|
+
# @example Mixed case with dashes
|
22
|
+
# "camel-case-string".underscore
|
23
|
+
# # => "camel_case_string"
|
2
24
|
module StringUnderscore
|
25
|
+
# Convert a camelCase or PascalCase string to snake_case format.
|
26
|
+
#
|
27
|
+
# This method handles various naming conventions:
|
28
|
+
# - Converts camelCase to snake_case (e.g., "camelCase" → "camel_case")
|
29
|
+
# - Converts PascalCase to snake_case (e.g., "PascalCase" → "pascal_case")
|
30
|
+
# - Handles consecutive uppercase letters (e.g., "XMLParser" → "xml_parser")
|
31
|
+
# - Replaces dashes with underscores
|
32
|
+
# - Converts to lowercase
|
33
|
+
# - Handles nested module paths with '::' separators
|
34
|
+
#
|
35
|
+
# @return [String] A new string in snake_case format
|
3
36
|
def underscore
|
4
37
|
word = dup
|
5
38
|
word.gsub!(/::/, '/')
|
@@ -11,5 +44,3 @@ module Tins
|
|
11
44
|
end
|
12
45
|
end
|
13
46
|
end
|
14
|
-
|
15
|
-
require 'tins/alias'
|
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]
|
104
|
+
end
|
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
|
25
135
|
end
|
26
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,40 +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
|
101
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
|
102
269
|
def self.compare(version1, operator, version2)
|
103
270
|
Version.new(version1).send(operator, Version.new(version2))
|
104
271
|
end
|
105
272
|
end
|
106
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
|
107
278
|
def self.StringVersion(string)
|
108
279
|
StringVersion::Version.new(string.to_str)
|
109
280
|
end
|
110
281
|
end
|
111
|
-
|
112
|
-
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,10 +4,20 @@ 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
23
|
c = IO.console
|
@@ -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,5 +1,39 @@
|
|
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
|
35
|
+
# Cleanup lambda that removes thread-local data when objects are garbage
|
36
|
+
# collected
|
3
37
|
@@cleanup = lambda do |my_object_id|
|
4
38
|
my_id = "__thread_local_#{my_object_id}__"
|
5
39
|
for t in Thread.list
|
@@ -7,13 +41,28 @@ module Tins
|
|
7
41
|
end
|
8
42
|
end
|
9
43
|
|
10
|
-
# Define a thread local variable named
|
11
|
-
# 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
|
12
61
|
def thread_local(name, default_value = nil, &default)
|
13
62
|
is_a?(Module) or raise TypeError, "receiver has to be a Module"
|
14
63
|
|
15
64
|
default_value && default and raise ArgumentError,
|
16
|
-
"require either
|
65
|
+
"require either default_value or default block"
|
17
66
|
|
18
67
|
if default_value
|
19
68
|
default = -> * { default_value }
|
@@ -40,13 +89,26 @@ module Tins
|
|
40
89
|
self
|
41
90
|
end
|
42
91
|
|
43
|
-
# Define a thread local variable for the current instance with name
|
44
|
-
# 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
|
45
106
|
def instance_thread_local(name, default_value = nil, &default)
|
46
107
|
class << self
|
47
108
|
extend Tins::ThreadLocal
|
48
109
|
self
|
49
110
|
end.thread_local name, default_value, &default
|
111
|
+
|
50
112
|
self
|
51
113
|
end
|
52
114
|
end
|