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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.contexts/code_comment.rb +23 -0
  3. data/.contexts/full.rb +31 -0
  4. data/.contexts/lib.rb +24 -0
  5. data/.contexts/yard.md +92 -0
  6. data/.github/workflows/codeql-analysis.yml +72 -0
  7. data/CHANGES.md +194 -0
  8. data/README.md +161 -90
  9. data/Rakefile +23 -19
  10. data/examples/let.rb +8 -40
  11. data/examples/mail.rb +0 -1
  12. data/examples/ones_difference.stm +0 -1
  13. data/examples/turing.rb +3 -1
  14. data/lib/tins/alias.rb +1 -0
  15. data/lib/tins/annotate.rb +37 -27
  16. data/lib/tins/ask_and_send.rb +41 -0
  17. data/lib/tins/attempt.rb +39 -0
  18. data/lib/tins/bijection.rb +34 -0
  19. data/lib/tins/case_predicate.rb +21 -0
  20. data/lib/tins/complete.rb +16 -0
  21. data/lib/tins/concern.rb +100 -0
  22. data/lib/tins/date_dummy.rb +36 -4
  23. data/lib/tins/date_time_dummy.rb +34 -2
  24. data/lib/tins/deep_dup.rb +9 -2
  25. data/lib/tins/deprecate.rb +27 -0
  26. data/lib/tins/dslkit.rb +563 -59
  27. data/lib/tins/duration.rb +160 -3
  28. data/lib/tins/expose.rb +54 -5
  29. data/lib/tins/extract_last_argument_options.rb +9 -0
  30. data/lib/tins/file_binary.rb +108 -25
  31. data/lib/tins/find.rb +114 -11
  32. data/lib/tins/generator.rb +10 -2
  33. data/lib/tins/go.rb +81 -4
  34. data/lib/tins/hash_bfs.rb +69 -0
  35. data/lib/tins/hash_symbolize_keys_recursive.rb +62 -4
  36. data/lib/tins/hash_union.rb +47 -2
  37. data/lib/tins/if_predicate.rb +31 -0
  38. data/lib/tins/implement.rb +50 -0
  39. data/lib/tins/limited.rb +105 -29
  40. data/lib/tins/lines_file.rb +81 -2
  41. data/lib/tins/lru_cache.rb +54 -17
  42. data/lib/tins/memoize.rb +86 -58
  43. data/lib/tins/method_description.rb +87 -4
  44. data/lib/tins/minimize.rb +39 -11
  45. data/lib/tins/module_group.rb +27 -2
  46. data/lib/tins/named_set.rb +20 -0
  47. data/lib/tins/null.rb +86 -15
  48. data/lib/tins/once.rb +61 -4
  49. data/lib/tins/p.rb +44 -8
  50. data/lib/tins/partial_application.rb +66 -7
  51. data/lib/tins/proc_compose.rb +58 -1
  52. data/lib/tins/proc_prelude.rb +97 -10
  53. data/lib/tins/range_plus.rb +30 -2
  54. data/lib/tins/require_maybe.rb +36 -0
  55. data/lib/tins/responding.rb +39 -0
  56. data/lib/tins/secure_write.rb +25 -5
  57. data/lib/tins/sexy_singleton.rb +46 -48
  58. data/lib/tins/string_byte_order_mark.rb +33 -2
  59. data/lib/tins/string_camelize.rb +31 -2
  60. data/lib/tins/string_named_placeholders.rb +70 -0
  61. data/lib/tins/string_underscore.rb +33 -2
  62. data/lib/tins/string_version.rb +183 -10
  63. data/lib/tins/subhash.rb +35 -10
  64. data/lib/tins/temp_io.rb +7 -0
  65. data/lib/tins/temp_io_enum.rb +19 -0
  66. data/lib/tins/terminal.rb +34 -12
  67. data/lib/tins/thread_local.rb +69 -11
  68. data/lib/tins/time_dummy.rb +47 -21
  69. data/lib/tins/to.rb +15 -0
  70. data/lib/tins/to_proc.rb +17 -4
  71. data/lib/tins/token.rb +61 -2
  72. data/lib/tins/unit.rb +288 -149
  73. data/lib/tins/version.rb +1 -1
  74. data/lib/tins/write.rb +14 -3
  75. data/lib/tins/xt/blank.rb +81 -2
  76. data/lib/tins/xt/concern.rb +51 -0
  77. data/lib/tins/xt/deep_dup.rb +4 -2
  78. data/lib/tins/xt/deprecate.rb +5 -0
  79. data/lib/tins/xt/full.rb +56 -11
  80. data/lib/tins/xt/hash_bfs.rb +7 -0
  81. data/lib/tins/xt/irb.rb +46 -2
  82. data/lib/tins/xt/method_description.rb +0 -12
  83. data/lib/tins/xt/minimize.rb +7 -0
  84. data/lib/tins/xt/named.rb +71 -16
  85. data/lib/tins/xt/proc_compose.rb +4 -0
  86. data/lib/tins/xt/secure_write.rb +0 -4
  87. data/lib/tins/xt/string.rb +1 -0
  88. data/lib/tins/xt/string_camelize.rb +4 -2
  89. data/lib/tins/xt/string_named_placeholders.rb +7 -0
  90. data/lib/tins/xt/string_underscore.rb +4 -2
  91. data/lib/tins/xt/subhash.rb +11 -0
  92. data/lib/tins/xt/time_freezer.rb +43 -6
  93. data/lib/tins/xt/write.rb +0 -4
  94. data/lib/tins/xt.rb +3 -3
  95. data/lib/tins.rb +19 -3
  96. data/tests/annotate_test.rb +0 -1
  97. data/tests/bijection_test.rb +0 -1
  98. data/tests/concern_test.rb +63 -4
  99. data/tests/date_dummy_test.rb +0 -1
  100. data/tests/date_time_dummy_test.rb +0 -1
  101. data/tests/delegate_test.rb +0 -1
  102. data/tests/deprecate_test.rb +41 -0
  103. data/tests/dslkit_test.rb +15 -1
  104. data/tests/duration_test.rb +23 -2
  105. data/tests/dynamic_scope_test.rb +0 -1
  106. data/tests/extract_last_argument_options_test.rb +0 -1
  107. data/tests/find_test.rb +0 -1
  108. data/tests/from_module_test.rb +30 -3
  109. data/tests/generator_test.rb +0 -1
  110. data/tests/go_test.rb +0 -1
  111. data/tests/hash_bfs_test.rb +34 -0
  112. data/tests/hash_symbolize_keys_recursive_test.rb +0 -1
  113. data/tests/implement_test.rb +6 -9
  114. data/tests/limited_test.rb +12 -12
  115. data/tests/lines_file_test.rb +2 -1
  116. data/tests/lru_cache_test.rb +12 -1
  117. data/tests/memoize_test.rb +0 -1
  118. data/tests/method_description_test.rb +14 -20
  119. data/tests/minimize_test.rb +0 -1
  120. data/tests/module_group_test.rb +0 -1
  121. data/tests/named_set_test.rb +0 -1
  122. data/tests/null_test.rb +0 -1
  123. data/tests/partial_application_test.rb +4 -0
  124. data/tests/proc_prelude_test.rb +1 -1
  125. data/tests/require_maybe_test.rb +0 -1
  126. data/tests/scope_test.rb +1 -2
  127. data/tests/secure_write_test.rb +6 -1
  128. data/tests/sexy_singleton_test.rb +1 -1
  129. data/tests/string_named_placeholders.rb +109 -0
  130. data/tests/string_version_test.rb +3 -1
  131. data/tests/subhash_test.rb +0 -1
  132. data/tests/test_helper.rb +4 -7
  133. data/tests/time_dummy_test.rb +0 -1
  134. data/tests/time_freezer_test.rb +1 -1
  135. data/tests/to_test.rb +6 -6
  136. data/tests/token_test.rb +0 -1
  137. data/tests/unit_test.rb +0 -1
  138. data/tins.gemspec +18 -30
  139. metadata +55 -38
  140. data/lib/tins/count_by.rb +0 -8
  141. data/lib/tins/deep_const_get.rb +0 -50
  142. data/lib/tins/timed_cache.rb +0 -51
  143. data/lib/tins/uniq_by.rb +0 -10
  144. data/lib/tins/xt/count_by.rb +0 -11
  145. data/lib/tins/xt/deep_const_get.rb +0 -7
  146. data/lib/tins/xt/uniq_by.rb +0 -15
  147. data/tests/count_by_test.rb +0 -17
  148. data/tests/deep_const_get_test.rb +0 -37
  149. data/tests/uniq_by_test.rb +0 -31
  150. /data/{COPYING → LICENSE} +0 -0
@@ -1,5 +1,32 @@
1
1
  module Tins
2
+ # Provides methods for defining abstract method implementations that raise
3
+ # NotImplementedError. Useful for creating interface-like modules or base
4
+ # classes where certain methods must be implemented by subclasses.
5
+ #
6
+ # @example Basic usage
7
+ # module MyInterface
8
+ # include Tins::Implement
9
+ # implement :process
10
+ # end
11
+ #
12
+ # class MyClass
13
+ # include MyInterface
14
+ # # Must implement process method or it will raise NotImplementedError
15
+ # end
16
+ #
17
+ # @example With custom messages
18
+ # module ApiInterface
19
+ # include Tins::Implement
20
+ # implement :get_data, :subclass
21
+ # implement :post_data, :submodule
22
+ # end
23
+ #
24
+ # @see Tins::MethodDescription For method description integration
2
25
  module Implement
26
+ # Predefined error message templates for different implementation contexts.
27
+ #
28
+ # These templates provide context-specific error messages that help
29
+ # developers understand where and how methods should be implemented.
3
30
  MESSAGES = {
4
31
  default: 'method %{method_name} not implemented in module %{module}',
5
32
  subclass: 'method %{method_name} has to be implemented in '\
@@ -8,6 +35,21 @@ module Tins
8
35
  'submodules of %{module}',
9
36
  }
10
37
 
38
+ # Defines an implementation for a method that raises NotImplementedError.
39
+ #
40
+ # @param method_name [Symbol, String] The name of the method to implement
41
+ # @param msg [Symbol, String, Hash] The error message template or options
42
+ # @option msg [Symbol] :in When msg is a Hash, specifies which message key to use
43
+ #
44
+ # @example Basic usage
45
+ # implement :process
46
+ # # => Defines process method that raises NotImplementedError
47
+ #
48
+ # @example Custom message
49
+ # implement :calculate, 'Custom error message'
50
+ #
51
+ # @example Using predefined message template
52
+ # implement :validate, :subclass
11
53
  def implement(method_name, msg = :default)
12
54
  method_name.nil? and return
13
55
  case msg
@@ -30,6 +72,14 @@ module Tins
30
72
  end
31
73
  end
32
74
 
75
+ # Defines an implementation for a method that raises NotImplementedError
76
+ # specifically for submodule implementations.
77
+ #
78
+ # @param method_name [Symbol, String] The name of the method to implement
79
+ #
80
+ # @example Usage
81
+ # implement_in_submodule :render
82
+ # # => Defines render method with submodule-specific error message
33
83
  def implement_in_submodule(method_name)
34
84
  implement method_name,
35
85
  'method %{method_name} has to be implemented in submodules of'\
data/lib/tins/limited.rb CHANGED
@@ -1,50 +1,126 @@
1
1
  require 'thread'
2
2
 
3
3
  module Tins
4
+ # Tins::Limited provides a thread pool implementation that limits the number
5
+ # of concurrent threads running simultaneously.
6
+ #
7
+ # This class implements a producer-consumer pattern where you can submit
8
+ # tasks that will be executed by a fixed number of worker threads, preventing
9
+ # resource exhaustion from too many concurrent operations.
10
+ #
11
+ # @example Basic usage
12
+ # limited = Tins::Limited.new(3) # Limit to 3 concurrent threads
13
+ #
14
+ # limited.process do |l|
15
+ # 10.times do
16
+ # l.execute { puts "Task #{Thread.current.object_id}" }
17
+ # end
18
+ # l.stop # Stop processing new tasks
19
+ # end
20
+ #
21
+ # @example With named thread group
22
+ # limited = Tins::Limited.new(5, name: 'worker_pool')
23
+ # # Threads will be named 'worker_pool'
4
24
  class Limited
5
- # Create a Limited instance, that runs _maximum_ threads at most.
6
- def initialize(maximum)
7
- @mutex = Mutex.new
8
- @continue = ConditionVariable.new
9
- @maximum = Integer(maximum)
25
+ # Create a Limited instance that runs at most _maximum_ threads
26
+ # simultaneously.
27
+ #
28
+ # @param maximum [Integer] The maximum number of concurrent worker threads
29
+ # @param name [String, nil] Optional name for the thread group
30
+ # @raise [ArgumentError] if maximum is less than 1
31
+ # @raise [TypeError] if maximum cannot be converted to Integer
32
+ def initialize(maximum, name: nil)
33
+ @maximum = Integer(maximum)
10
34
  raise ArgumentError, "maximum < 1" if @maximum < 1
11
- @count = 0
12
- @tg = ThreadGroup.new
35
+ @mutex = Mutex.new
36
+ @continue = ConditionVariable.new
37
+ @name = name
38
+ @count = 0
39
+ @tg = ThreadGroup.new
13
40
  end
14
41
 
15
- # The maximum number of worker threads.
42
+ # The maximum number of worker threads that can run concurrently.
43
+ #
44
+ # @return [Integer] The maximum concurrent thread limit
16
45
  attr_reader :maximum
17
46
 
18
- # Execute _maximum_ number of threads in parallel.
19
- def execute
20
- @mutex.synchronize do
47
+ # Submit a task to be executed by the thread pool.
48
+ #
49
+ # @yield [Thread] The block to execute as a task
50
+ # @raise [ArgumentError] if called before process has been started
51
+ def execute(&block)
52
+ @tasks or raise ArgumentError, "start processing first"
53
+ @tasks << block
54
+ end
55
+
56
+ # Start processing tasks with the configured thread pool.
57
+ #
58
+ # This method blocks until all tasks are completed and the processing is
59
+ # stopped. The provided block is called repeatedly to submit tasks via
60
+ # execute().
61
+ #
62
+ # @yield [Limited] The limited instance for submitting tasks
63
+ # @return [void]
64
+ def process
65
+ @tasks = Queue.new
66
+ @executor = create_executor
67
+ @executor.name = @name if @name
68
+ catch :stop do
21
69
  loop do
22
- if @count < @maximum
23
- @count += 1
24
- Thread.new do
25
- @tg.add Thread.current
26
- yield
27
- @mutex.synchronize { @count -= 1 }
28
- @continue.signal
29
- end
30
- return
31
- else
32
- @continue.wait(@mutex)
33
- end
70
+ yield self
34
71
  end
72
+ ensure
73
+ wait until done?
74
+ @executor.kill
35
75
  end
36
76
  end
37
77
 
78
+ # Stop processing new tasks and wait for existing tasks to complete.
79
+ #
80
+ # @return [void]
81
+ def stop
82
+ throw :stop
83
+ end
84
+
85
+ private
86
+
87
+ # Check if all tasks and threads have completed.
88
+ #
89
+ # @return [Boolean] true if no tasks remain and no threads are running
90
+ def done?
91
+ @tasks.empty? && @tg.list.empty?
92
+ end
93
+
94
+ # Wait for all threads in the thread group to complete.
95
+ #
96
+ # @return [void]
38
97
  def wait
39
98
  @tg.list.each(&:join)
40
99
  end
41
100
 
42
- def process
43
- yield self
44
- ensure
45
- wait
101
+ # Create and start the executor thread that manages the worker pool.
102
+ #
103
+ # @return [Thread] The executor thread
104
+ def create_executor
105
+ Thread.new do
106
+ @mutex.synchronize do
107
+ loop do
108
+ if @count < @maximum
109
+ task = @tasks.pop
110
+ @count += 1
111
+ Thread.new do
112
+ @tg.add Thread.current
113
+ task.(Thread.current)
114
+ ensure
115
+ @count -= 1
116
+ @continue.signal
117
+ end
118
+ else
119
+ @continue.wait(@mutex)
120
+ end
121
+ end
122
+ end
123
+ end
46
124
  end
47
125
  end
48
126
  end
49
-
50
- require 'tins/alias'
@@ -1,29 +1,74 @@
1
1
  module Tins
2
+ # Tins::LinesFile provides enhanced file line processing capabilities.
3
+ #
4
+ # This class wraps file content in a way that allows for rich line-level
5
+ # operations, including tracking line numbers, filenames, and providing
6
+ # convenient navigation and matching methods. It's particularly useful for
7
+ # log processing, configuration files, or any scenario where you need to
8
+ # work with structured text data while maintaining context about line
9
+ # positions.
10
+ #
11
+ # @example Basic usage
12
+ # lines_file = Tins::LinesFile.for_filename('example.txt')
13
+ # lines_file.each do |line|
14
+ # puts line.file_linenumber # => "example.txt:1"
15
+ # puts line.line_number # => 1
16
+ # end
17
+ #
18
+ # @example Line navigation and matching
19
+ # lines_file = Tins::LinesFile.for_filename('example.txt')
20
+ # lines_file.next! # Move to next line
21
+ # lines_file.match_forward(/pattern/) # Match forward from current position
2
22
  class LinesFile
23
+ # Extension module that adds line metadata to individual lines.
24
+ #
25
+ # This module is automatically mixed into each line when a LinesFile is created,
26
+ # providing access to line number and filename information directly from the line objects.
3
27
  module LineExtension
28
+ # @return [Integer] The line number (1-based) of this line
4
29
  attr_reader :line_number
5
30
 
31
+ # @return [String] The filename associated with this line's source
6
32
  def filename
7
33
  lines_file.filename.dup
8
34
  end
9
35
  end
10
36
 
37
+ # Create a LinesFile instance from a filename.
38
+ #
39
+ # @param filename [String] Path to the file to read
40
+ # @param line_number [Integer] Starting line number (default: 1)
41
+ # @return [LinesFile] A new LinesFile instance
11
42
  def self.for_filename(filename, line_number = nil)
12
43
  obj = new(File.readlines(filename), line_number)
13
44
  obj.filename = filename
14
45
  obj
15
46
  end
16
47
 
48
+ # Create a LinesFile instance from an already opened file.
49
+ #
50
+ # @param file [File] An open File object to read from
51
+ # @param line_number [Integer] Starting line number (default: 1)
52
+ # @return [LinesFile] A new LinesFile instance
17
53
  def self.for_file(file, line_number = nil)
18
54
  obj = new(file.readlines, line_number)
19
55
  obj.filename = file.path
20
56
  obj
21
57
  end
22
58
 
59
+ # Create a LinesFile instance from an array of lines.
60
+ #
61
+ # @param lines [Array<String>] Array of line strings
62
+ # @param line_number [Integer] Starting line number (default: 1)
63
+ # @return [LinesFile] A new LinesFile instance
23
64
  def self.for_lines(lines, line_number = nil)
24
65
  new(lines, line_number)
25
66
  end
26
67
 
68
+ # Initialize a LinesFile with lines and optional starting line number.
69
+ #
70
+ # @param lines [Array<String>] Array of line strings to process
71
+ # @param line_number [Integer] Starting line number (default: 1)
27
72
  def initialize(lines, line_number = nil)
28
73
  @lines = lines
29
74
  @lines.each_with_index do |line, i|
@@ -34,27 +79,42 @@ module Tins
34
79
  instance_variable_set :@line_number, line_number || (@lines.empty? ? 0 : 1)
35
80
  end
36
81
 
82
+ # @return [String] The filename associated with this LinesFile
37
83
  attr_accessor :filename
38
84
 
85
+ # @return [Integer] The current line number (1-based)
39
86
  attr_reader :line_number
40
87
 
88
+ # Reset the current line number to the beginning.
89
+ #
90
+ # @return [LinesFile] Returns self for chaining
41
91
  def rewind
42
92
  self.line_number = 1
43
93
  self
44
94
  end
45
95
 
96
+ # Move to the next line.
97
+ #
98
+ # @return [LinesFile, nil] Returns self if successful, nil if at end of file
46
99
  def next!
47
100
  old = line_number
48
101
  self.line_number += 1
49
102
  line_number > old ? self : nil
50
103
  end
51
104
 
105
+ # Move to the previous line.
106
+ #
107
+ # @return [LinesFile, nil] Returns self if successful, nil if at beginning
52
108
  def previous!
53
109
  old = line_number
54
110
  self.line_number -= 1
55
111
  line_number < old ? self : nil
56
112
  end
57
113
 
114
+ # Set the current line number.
115
+ #
116
+ # @param number [Integer] The new line number to set
117
+ # @return [void]
58
118
  def line_number=(number)
59
119
  number = number.to_i
60
120
  if number > 0 && number <= last_line_number
@@ -62,14 +122,21 @@ module Tins
62
122
  end
63
123
  end
64
124
 
125
+ # @return [Integer] The total number of lines in this file
65
126
  def last_line_number
66
127
  @lines.size
67
128
  end
68
129
 
130
+ # @return [Boolean] True if the file has no lines
69
131
  def empty?
70
132
  @lines.empty?
71
133
  end
72
134
 
135
+ # Iterate through all lines, setting the current line number for each.
136
+ #
137
+ # @yield [line] Each line in the file
138
+ # @yieldparam line [String] The current line object with line metadata
139
+ # @return [LinesFile] Returns self for chaining
73
140
  def each(&block)
74
141
  empty? and return self
75
142
  old_line_number = line_number
@@ -83,15 +150,22 @@ module Tins
83
150
  end
84
151
  include Enumerable
85
152
 
153
+ # @return [String, nil] The current line content or nil if out of bounds
86
154
  def line
87
155
  index = line_number - 1
88
156
  @lines[index] if index >= 0
89
157
  end
90
158
 
159
+ # @return [String] Formatted filename and line number (e.g., "file.txt:5")
91
160
  def file_linenumber
92
161
  "#{filename}:#{line_number}"
93
162
  end
94
163
 
164
+ # Match a regular expression backward from current position.
165
+ #
166
+ # @param regexp [Regexp] The regular expression to match
167
+ # @param previous_after_match [Boolean] Whether to move back one line after match
168
+ # @return [Array<String>, nil] Captured groups or nil if no match
95
169
  def match_backward(regexp, previous_after_match = false)
96
170
  begin
97
171
  if line =~ regexp
@@ -101,6 +175,11 @@ module Tins
101
175
  end while previous!
102
176
  end
103
177
 
178
+ # Match a regular expression forward from current position.
179
+ #
180
+ # @param regexp [Regexp] The regular expression to match
181
+ # @param next_after_match [Boolean] Whether to move forward one line after match
182
+ # @return [Array<String>, nil] Captured groups or nil if no match
104
183
  def match_forward(regexp, next_after_match = false)
105
184
  begin
106
185
  if line =~ regexp
@@ -110,14 +189,14 @@ module Tins
110
189
  end while next!
111
190
  end
112
191
 
192
+ # @return [String] String representation including line number and content
113
193
  def to_s
114
194
  "#{line_number} #{line.chomp}"
115
195
  end
116
196
 
197
+ # @return [String] Detailed inspection string
117
198
  def inspect
118
199
  "#<#{self.class}: #{to_s.inspect}>"
119
200
  end
120
201
  end
121
202
  end
122
-
123
- require 'tins/alias'
@@ -1,33 +1,57 @@
1
1
  module Tins
2
+ # An LRU (Least Recently Used) cache implementation.
3
+ #
4
+ # This cache maintains a fixed-size collection of key-value pairs,
5
+ # automatically removing the least recently accessed item when the capacity
6
+ # is exceeded.
2
7
  class LRUCache
3
8
  include Enumerable
4
9
 
5
- class << self
6
- private
10
+ # Our "nil" value if it wasn' set by clients
11
+ NOT_EXIST = Object.new.freeze
7
12
 
8
- my_nil = Object.new.freeze
9
-
10
- define_method(:not_exist) do
11
- my_nil
12
- end
13
- end
13
+ private_constant :NOT_EXIST
14
14
 
15
+ # Initializes a new LRU cache with the specified capacity.
16
+ #
17
+ # @param capacity [Integer] maximum number of items the cache can hold
15
18
  def initialize(capacity)
16
- @capacity = capacity
19
+ @capacity = Integer(capacity)
20
+ @capacity >= 1 or
21
+ raise ArgumentError, "capacity should be >= 1, was #@capacity"
17
22
  @data = {}
18
23
  end
19
24
 
25
+ # Returns the maximum capacity of the cache.
26
+ #
27
+ # @return [Integer] the cache capacity
20
28
  attr_reader :capacity
21
29
 
30
+ # Retrieves the value associated with the given key.
31
+ #
32
+ # If the key exists, it is moved to the most recently used position.
33
+ # Returns nil if the key does not exist.
34
+ #
35
+ # @param key [Object] the key to look up
36
+ # @return [Object, nil] the value for the key or nil if not found
22
37
  def [](key)
23
- case value = @data.delete(key){ not_exist }
24
- when not_exist
38
+ case value = @data.delete(key) { NOT_EXIST }
39
+ when NOT_EXIST
25
40
  nil
26
41
  else
27
42
  @data[key] = value
28
43
  end
29
44
  end
30
45
 
46
+ # Associates a value with a key in the cache.
47
+ #
48
+ # If the key already exists, its position is updated to most recently used.
49
+ # If adding this item exceeds the capacity, the least recently used item is
50
+ # removed.
51
+ #
52
+ # @param key [Object] the key to set
53
+ # @param value [Object] the value to associate with the key
54
+ # @return [Object] the assigned value
31
55
  def []=(key, value)
32
56
  @data.delete(key)
33
57
  @data[key] = value
@@ -37,26 +61,39 @@ module Tins
37
61
  value
38
62
  end
39
63
 
64
+ # Iterates over all key-value pairs in the cache.
65
+ #
66
+ # Items are yielded in order from most recently used to least recently used.
67
+ #
68
+ # @yield [key, value] yields each key-value pair
69
+ # @yieldparam key [Object] the key
70
+ # @yieldparam value [Object] the value
71
+ # @return [Enumerator] if no block is given
40
72
  def each(&block)
41
73
  @data.reverse_each(&block)
42
74
  end
43
75
 
76
+ # Removes and returns the value associated with the given key.
77
+ #
78
+ # @param key [Object] the key to delete
79
+ # @return [Object, nil] the removed value or nil if not found
44
80
  def delete(key)
45
81
  @data.delete(key)
46
82
  end
47
83
 
84
+ # Removes all items from the cache.
85
+ #
86
+ # @return [Tins::LRUCache] self
48
87
  def clear
49
88
  @data.clear
89
+ self
50
90
  end
51
91
 
92
+ # Returns the number of items currently in the cache.
93
+ #
94
+ # @return [Integer] the current size of the cache
52
95
  def size
53
96
  @data.size
54
97
  end
55
-
56
- private
57
-
58
- def not_exist
59
- self.class.send(:not_exist)
60
- end
61
98
  end
62
99
  end