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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.contexts/code_comment.rb +5 -8
  3. data/.contexts/lib.rb +0 -2
  4. data/CHANGES.md +12 -0
  5. data/README.md +158 -6
  6. data/Rakefile +19 -16
  7. data/examples/let.rb +8 -40
  8. data/examples/mail.rb +0 -1
  9. data/examples/turing.rb +3 -1
  10. data/lib/tins/alias.rb +1 -0
  11. data/lib/tins/annotate.rb +37 -27
  12. data/lib/tins/ask_and_send.rb +41 -0
  13. data/lib/tins/attempt.rb +39 -0
  14. data/lib/tins/bijection.rb +34 -0
  15. data/lib/tins/case_predicate.rb +21 -0
  16. data/lib/tins/complete.rb +16 -0
  17. data/lib/tins/concern.rb +64 -0
  18. data/lib/tins/date_dummy.rb +36 -4
  19. data/lib/tins/date_time_dummy.rb +34 -2
  20. data/lib/tins/deep_dup.rb +9 -2
  21. data/lib/tins/deprecate.rb +12 -0
  22. data/lib/tins/dslkit.rb +559 -83
  23. data/lib/tins/duration.rb +120 -5
  24. data/lib/tins/expose.rb +54 -5
  25. data/lib/tins/extract_last_argument_options.rb +9 -0
  26. data/lib/tins/file_binary.rb +104 -21
  27. data/lib/tins/find.rb +114 -11
  28. data/lib/tins/generator.rb +10 -2
  29. data/lib/tins/go.rb +81 -4
  30. data/lib/tins/hash_bfs.rb +4 -2
  31. data/lib/tins/hash_symbolize_keys_recursive.rb +62 -4
  32. data/lib/tins/hash_union.rb +47 -2
  33. data/lib/tins/if_predicate.rb +31 -0
  34. data/lib/tins/implement.rb +50 -0
  35. data/lib/tins/limited.rb +54 -5
  36. data/lib/tins/lines_file.rb +81 -2
  37. data/lib/tins/lru_cache.rb +54 -17
  38. data/lib/tins/memoize.rb +86 -58
  39. data/lib/tins/method_description.rb +87 -4
  40. data/lib/tins/minimize.rb +39 -11
  41. data/lib/tins/module_group.rb +27 -2
  42. data/lib/tins/named_set.rb +20 -0
  43. data/lib/tins/null.rb +86 -15
  44. data/lib/tins/once.rb +61 -4
  45. data/lib/tins/p.rb +44 -8
  46. data/lib/tins/partial_application.rb +66 -7
  47. data/lib/tins/proc_compose.rb +58 -1
  48. data/lib/tins/proc_prelude.rb +97 -10
  49. data/lib/tins/range_plus.rb +30 -2
  50. data/lib/tins/require_maybe.rb +36 -0
  51. data/lib/tins/responding.rb +39 -0
  52. data/lib/tins/secure_write.rb +24 -4
  53. data/lib/tins/sexy_singleton.rb +45 -48
  54. data/lib/tins/string_byte_order_mark.rb +33 -2
  55. data/lib/tins/string_camelize.rb +31 -2
  56. data/lib/tins/string_underscore.rb +33 -2
  57. data/lib/tins/string_version.rb +179 -10
  58. data/lib/tins/subhash.rb +35 -10
  59. data/lib/tins/temp_io.rb +7 -0
  60. data/lib/tins/temp_io_enum.rb +19 -0
  61. data/lib/tins/terminal.rb +31 -9
  62. data/lib/tins/thread_local.rb +67 -5
  63. data/lib/tins/time_dummy.rb +46 -21
  64. data/lib/tins/to.rb +15 -0
  65. data/lib/tins/to_proc.rb +17 -4
  66. data/lib/tins/token.rb +56 -1
  67. data/lib/tins/unit.rb +288 -149
  68. data/lib/tins/version.rb +1 -1
  69. data/lib/tins/write.rb +14 -3
  70. data/lib/tins/xt/blank.rb +81 -2
  71. data/lib/tins/xt/concern.rb +51 -0
  72. data/lib/tins/xt/full.rb +56 -11
  73. data/lib/tins/xt/irb.rb +46 -2
  74. data/lib/tins/xt/method_description.rb +0 -12
  75. data/lib/tins/xt/minimize.rb +7 -0
  76. data/lib/tins/xt/named.rb +71 -16
  77. data/lib/tins/xt/proc_compose.rb +4 -0
  78. data/lib/tins/xt/subhash.rb +11 -0
  79. data/lib/tins/xt/time_freezer.rb +43 -6
  80. data/lib/tins/xt.rb +1 -3
  81. data/lib/tins.rb +16 -3
  82. data/tests/duration_test.rb +4 -0
  83. data/tests/from_module_test.rb +30 -2
  84. data/tests/implement_test.rb +6 -8
  85. data/tests/lines_file_test.rb +2 -0
  86. data/tests/lru_cache_test.rb +12 -0
  87. data/tests/method_description_test.rb +14 -20
  88. data/tests/partial_application_test.rb +4 -0
  89. data/tests/proc_prelude_test.rb +1 -1
  90. data/tests/scope_test.rb +1 -1
  91. data/tests/string_version_test.rb +2 -0
  92. data/tests/to_test.rb +6 -6
  93. data/tins.gemspec +9 -9
  94. metadata +23 -41
  95. data/lib/tins/count_by.rb +0 -21
  96. data/lib/tins/deep_const_get.rb +0 -64
  97. data/lib/tins/timed_cache.rb +0 -51
  98. data/lib/tins/uniq_by.rb +0 -23
  99. data/lib/tins/xt/count_by.rb +0 -7
  100. data/lib/tins/xt/deep_const_get.rb +0 -7
  101. data/lib/tins/xt/uniq_by.rb +0 -25
  102. data/tests/count_by_test.rb +0 -17
  103. data/tests/deep_const_get_test.rb +0 -37
  104. data/tests/uniq_by_test.rb +0 -31
  105. /data/{COPYING → LICENSE} +0 -0
@@ -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'
@@ -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
- LEVELS = [ :major, :minor, :build, :revision ].each_with_index.
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
- LEVELS.each do |symbol, level|
18
- define_method(symbol) do
19
- self[level]
20
- end
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
- define_method("#{symbol}=") do |new_level|
23
- self[level] = new_level
24
- end
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, that only contains key-value pairs
4
- # matching +patterns+ and return it. +patterns+ can be for example /^foo/
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
- # If a block is given this method yields to it after the first pattern
8
- # matched with a 3-tuple of +(key, value, match_data)+ using the return
9
- # value of the block as the value of the result hash. +match_data+ is a
10
- # MatchData instance if the matching pattern was a regular rexpression
11
- # otherwise it is nil.
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)
@@ -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
- module_function
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
- def lines
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
- def cols
39
- columns
40
- end
60
+ alias cols columns
61
+
62
+ extend self
41
63
  end
42
64
  end
@@ -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 _name_ in this module/class. If the
11
- # value _value_ is given, it is used to initialize the variable.
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 default_falue or default block"
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 _name_.
44
- # If the value _value_ is given, it is used to initialize the variable.
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