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,12 @@
1
1
  module Tins
2
+ # A hash subclass that ensures bijection between keys and values
2
3
  class Bijection < Hash
4
+ # Creates a new Bijection instance with key-value pairs.
5
+ #
6
+ # @param pairs [Array] an array of key-value pairs to populate the
7
+ # bijection
8
+ #
9
+ # @return [Bijection] a new bijection populated with the provided pairs
3
10
  def self.[](*pairs)
4
11
  pairs.size % 2 == 0 or
5
12
  raise ArgumentError, "odd number of arguments for #{self}"
@@ -15,10 +22,20 @@ module Tins
15
22
  end
16
23
  end
17
24
 
25
+ # The initialize method sets up a new instance with an inverted bijection.
26
+ #
27
+ # @param inverted [ Bijection ] the inverted bijection object, defaults to
28
+ # a new Bijection instance
18
29
  def initialize(inverted = Bijection.new(self))
19
30
  @inverted = inverted
20
31
  end
21
32
 
33
+ # The fill method populates the object with content from a block if it is
34
+ # empty.
35
+ #
36
+ # @return [ self ] returns the object itself after filling or if it was not
37
+ # empty
38
+ # @yield [ self ] yields self to the block for population
22
39
  def fill
23
40
  if empty?
24
41
  yield self
@@ -27,6 +44,9 @@ module Tins
27
44
  self
28
45
  end
29
46
 
47
+ # The freeze method freezes the current object and its inverted attribute.
48
+ #
49
+ # @return [Object] the frozen object
30
50
  def freeze
31
51
  r = super
32
52
  unless @inverted.frozen?
@@ -35,12 +55,26 @@ module Tins
35
55
  r
36
56
  end
37
57
 
58
+ # The []= method assigns a value to a key in the hash and maintains an
59
+ # inverted index.
60
+ #
61
+ # @param key [ Object ] the key to assign
62
+ # @param value [ Object ] the value to assign
63
+ #
64
+ # @return [ Object ] the assigned value
65
+ #
66
+ # @note This method will not overwrite existing keys, it will return early
67
+ # if the key already exists.
38
68
  def []=(key, value)
39
69
  key?(key) and return
40
70
  super
41
71
  @inverted[value] = key
42
72
  end
43
73
 
74
+ # The inverted attribute returns the inverted state of the object.
75
+ # Creates a new Bijection instance filled with key-value pairs.
76
+ #
77
+ # @return [Bijection] a new bijection instance with the specified key-value pairs
44
78
  attr_reader :inverted
45
79
  end
46
80
  end
@@ -1,5 +1,26 @@
1
1
  module Tins
2
+ # A module that provides a predicate method for checking if a value matches
3
+ # any of the given cases.
2
4
  module CasePredicate
5
+ # Checks if the object matches any of the given arguments using the ===
6
+ # operator.
7
+ #
8
+ # This method provides pattern matching functionality similar to Ruby's
9
+ # case/when statements, using the === operator for semantic equality
10
+ # checking.
11
+ #
12
+ # @example Basic type matching
13
+ # "hello".case?(String, Integer) # => String (matches first argument)
14
+ # 42.case?(String, Integer) # => Integer (matches first argument)
15
+ # nil.case?(String, Integer) # => nil (no matches)
16
+ #
17
+ # @example Range and pattern matching
18
+ # 15.case?(1..10, 11..20, 21..30) # => 11..20 (matches range)
19
+ # "hello world".case?(/foo/, /hello/) # => /hello/ (matches regex)
20
+ #
21
+ # @param args [Array] the arguments to check against using === operator
22
+ # @return [Object, nil] the first matching argument, or nil if no match is
23
+ # found
3
24
  def case?(*args)
4
25
  args.find { |a| a === self }
5
26
  end
data/lib/tins/complete.rb CHANGED
@@ -1,11 +1,27 @@
1
1
  require 'readline'
2
2
 
3
3
  module Tins
4
+ # A module that provides completion functionality for objects.
4
5
  module Complete
5
6
  module_function
6
7
 
7
8
  @@sync = Sync.new
8
9
 
10
+ # The complete method reads a line of input from the user with optional
11
+ # prompt and history support.
12
+ #
13
+ # @param prompt [ String ] the prompt string to display to the user
14
+ # @param add_hist [ Boolean ] whether to add the input to the command
15
+ # history
16
+ #
17
+ # @yield [ String ] the completion procedure to use for tab completion
18
+ #
19
+ # @return [ String ] the line of input entered by the user
20
+ #
21
+ # @example Prompt with easy tab completion
22
+ # complete(prompt: 'Pick a ruby file! ') {
23
+ # Dir['**/*.rb'].grep(/#{it}/)
24
+ # }.then { '%u lines' % File.new(it).each_line.count }
9
25
  def complete(prompt: '', add_hist: false, &block)
10
26
  @@sync.synchronize do
11
27
  Readline.completion_proc = block
data/lib/tins/concern.rb CHANGED
@@ -1,9 +1,49 @@
1
1
  module Tins
2
+ # A module concern implementation that supports dependency tracking, class
3
+ # method inclusion, and block execution hooks.
4
+ #
5
+ # This module provides a way to define reusable module functionality with
6
+ # automatic dependency management, class method injection, and lifecycle
7
+ # callbacks similar to ActiveSupport::Concern but with more control.
8
+ #
9
+ # @example Basic usage
10
+ # module MyConcern
11
+ # extend Tins::Concern
12
+ #
13
+ # included do
14
+ # attr_accessor :logger
15
+ # end
16
+ #
17
+ # def log(message)
18
+ # logger&.info message
19
+ # end
20
+ #
21
+ # class_methods do
22
+ # def my_class_method
23
+ # # ...
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ # class MyClass
29
+ # include MyConcern
30
+ # end
2
31
  module Concern
32
+ # Extends the base object with dependency tracking capabilities.
33
+ #
34
+ # This method initializes an instance variable on the base object to store
35
+ # dependency information.
36
+ #
37
+ # @param base [ Object ] the object being extended
3
38
  def self.extended(base)
4
39
  base.instance_variable_set("@_dependencies", [])
5
40
  end
6
41
 
42
+ # The append_features method includes dependencies and class methods in the
43
+ # base class.
44
+ #
45
+ # @param base [ Object ] the base class to include features in
46
+ # @return [ Boolean ] true if features were appended, false otherwise
7
47
  def append_features(base)
8
48
  if base.instance_variable_defined?("@_dependencies")
9
49
  base.instance_variable_get("@_dependencies") << self
@@ -19,12 +59,72 @@ module Tins
19
59
  end
20
60
  end
21
61
 
62
+ # Prepends the features of this module to the base class.
63
+ #
64
+ # This method handles the inclusion of dependencies and class methods when
65
+ # a module is included in a class. It manages dependency tracking and
66
+ # ensures proper extension of the base class with ClassMethods.
67
+ #
68
+ # @param base [Class] the class that includes this module
69
+ # @return [Boolean] true if the module was successfully prepended, false otherwise
70
+ def prepend_features(base)
71
+ if base.instance_variable_defined?("@_dependencies")
72
+ base.instance_variable_get("@_dependencies") << self
73
+ false
74
+ else
75
+ return false if base < self
76
+ @_dependencies.each { |dep| base.send(:include, dep) }
77
+ super
78
+ base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
79
+ base.class_eval(&@_prepended_block) if instance_variable_defined?("@_prepended_block")
80
+ Thread.current[:tin_concern_args] = nil
81
+ true
82
+ end
83
+ end
84
+
85
+ # The included method is called when the module is included in a class or
86
+ # module.
87
+ #
88
+ # @param base [ Object ] the class or module that includes this module
89
+ # @param block [ Proc ] optional block to be executed when included
22
90
  def included(base = nil, &block)
23
91
  if base.nil?
92
+ instance_variable_defined?(:@_included_block) and
93
+ raise StandardError, "included block already defined"
24
94
  @_included_block = block
25
95
  else
26
96
  super
27
97
  end
28
98
  end
99
+
100
+ # The prepended method handles the setup of a block for prepend
101
+ # functionality or delegates to the superclass implementation
102
+ #
103
+ # @param base [ Object ] the base object to prepend to, or nil to set up a block
104
+ # @param block [ Proc ] the block to be used for prepend functionality
105
+ def prepended(base = nil, &block)
106
+ if base.nil?
107
+ instance_variable_defined?(:@_prepended_block) and
108
+ raise StandardError, "prepended block already defined"
109
+ @_prepended_block = block
110
+ else
111
+ super
112
+ end
113
+ end
114
+
115
+ # Defines a ClassMethods module for the current class and evaluates the
116
+ # given block within it.
117
+ #
118
+ # @param block [Proc] The block to be evaluated in the context of the
119
+ # ClassMethods module
120
+ # @return [Module] The ClassMethods module that was defined or retrieved
121
+ def class_methods(&block)
122
+ modul = const_get(:ClassMethods) if const_defined?(:ClassMethods, false)
123
+ unless modul
124
+ modul = Module.new
125
+ const_set(:ClassMethods, modul)
126
+ end
127
+ modul.module_eval(&block)
128
+ end
29
129
  end
30
130
  end
@@ -1,13 +1,34 @@
1
1
  require 'date'
2
2
 
3
3
  module Tins
4
+ # A module that provides dummy date functionality for testing purposes.
5
+ #
6
+ # @example Setting a dummy date
7
+ # Date.dummy = Date.parse('2009-09-09')
8
+ #
9
+ # @example Using a dummy date in a block
10
+ # Date.dummy Date.parse('2009-09-09') do
11
+ # # Your code here
12
+ # end
4
13
  module DateDummy
14
+ # The included method is a hook that gets called when this module is
15
+ # included in another class or module.
16
+ #
17
+ # It sets up date freezing functionality by extending the including
18
+ # class/module with special date handling methods. The method modifies the
19
+ # including class/module's singleton class to provide dummy date
20
+ # capabilities.
21
+ #
22
+ # @param modul [Object] the class or module that includes this module
5
23
  def self.included(modul)
6
24
  class << modul
7
25
  alias really_today today
8
26
 
9
27
  remove_method :today rescue nil
10
28
 
29
+ # Sets the dummy date value for date freezing functionality.
30
+ #
31
+ # @param value [Date, String] the date value to set as dummy
11
32
  def dummy=(value)
12
33
  if value.respond_to?(:to_str)
13
34
  value = Date.parse(value.to_str)
@@ -17,6 +38,16 @@ module Tins
17
38
  @dummy = value
18
39
  end
19
40
 
41
+ # The dummy method provides a way to set and temporarily override a
42
+ # dummy value within a block.
43
+ #
44
+ # @param value [Object] the dummy value to set, or nil to get the
45
+ # current dummy value
46
+ # @yield [] yields control to the block if a value is provided
47
+ # @return [Object] the current dummy value if no value parameter was
48
+ # provided
49
+ # @yieldparam value [Object] the dummy value to set within the block
50
+ # @yieldreturn [Object] the result of the block execution
20
51
  def dummy(value = nil)
21
52
  if value.nil?
22
53
  if defined?(@dummy)
@@ -33,6 +64,11 @@ module Tins
33
64
  end
34
65
  end
35
66
 
67
+ # The today method returns the current date. When a dummy date is set,
68
+ # it returns a duplicate of that date. Otherwise, it delegates to the
69
+ # actual today method implementation.
70
+ #
71
+ # @return [Date] the current date or the dummy date if set
36
72
  def today
37
73
  if dummy
38
74
  dummy.dup
@@ -42,12 +78,8 @@ module Tins
42
78
  really_today
43
79
  end
44
80
  end
45
-
46
81
  end
47
82
  super
48
83
  end
49
84
  end
50
85
  end
51
-
52
- require 'tins/alias'
53
-
@@ -1,13 +1,30 @@
1
1
  require 'date'
2
2
 
3
3
  module Tins
4
+ # A module that provides dummy functionality for DateTime class
5
+ #
6
+ # This module allows setting a fixed date and time that will be returned by
7
+ # DateTime.now instead of the actual current time. This is useful for testing
8
+ # purposes where consistent timestamps are required.
4
9
  module DateTimeDummy
10
+ # The included method is a hook that gets called when this module is
11
+ # included in another class or module.
12
+ #
13
+ # It sets up date time freezing functionality by extending the including
14
+ # class/module with special date time handling methods. The method modifies
15
+ # the including class/module's singleton class to provide dummy date time
16
+ # capabilities.
17
+ #
18
+ # @param modul [Object] the class or module that includes this module
5
19
  def self.included(modul)
6
20
  class << modul
7
21
  alias really_now now
8
22
 
9
23
  remove_method :now rescue nil
10
24
 
25
+ # Sets the dummy value for datetime handling.
26
+ #
27
+ # @param value [DateTime, String] the datetime value to set as dummy
11
28
  def dummy=(value)
12
29
  if value.respond_to?(:to_str)
13
30
  value = DateTime.parse(value.to_str)
@@ -17,6 +34,18 @@ module Tins
17
34
  @dummy = value
18
35
  end
19
36
 
37
+ # The dummy method provides a way to set and restore a dummy value
38
+ # within a block.
39
+ #
40
+ # @param value [Object] the dummy value to set, or nil to get the
41
+ # current dummy value
42
+ #
43
+ # @yield [void] yields control to the block if a value is provided
44
+ #
45
+ # @return [Object] the current dummy value if no value parameter is
46
+ # provided
47
+ # @return [Object] the result of the block if a value parameter is
48
+ # provided
20
49
  def dummy(value = nil)
21
50
  if value.nil?
22
51
  if defined?(@dummy)
@@ -33,6 +62,11 @@ module Tins
33
62
  end
34
63
  end
35
64
 
65
+ # The now method returns the current time, using a dummy time if one
66
+ # has been set. If no dummy time is set, it delegates to the actual
67
+ # time retrieval method.
68
+ #
69
+ # @return [Time] the current time or a mocked time if dummy is set
36
70
  def now
37
71
  if dummy
38
72
  dummy.dup
@@ -47,5 +81,3 @@ module Tins
47
81
  end
48
82
  end
49
83
  end
50
-
51
- require 'tins/alias'
data/lib/tins/deep_dup.rb CHANGED
@@ -1,5 +1,14 @@
1
1
  module Tins
2
+ # DeepDup module provides a method to deeply duplicate objects in Ruby.
3
+ #
4
+ # This module extends the Object class with a deep_dup method that creates a
5
+ # recursive copy of an object and all its nested objects.
2
6
  module DeepDup
7
+ # Duplicates an object deeply by marshaling and unmarshaling it. For
8
+ # objects that can't be marshaled, it returns the object itself.
9
+ #
10
+ # @return [Object] a deep duplicate of the object or the object itself if
11
+ # it can't be marshaled
3
12
  def deep_dup
4
13
  Marshal.load(Marshal.dump(self))
5
14
  rescue TypeError
@@ -7,5 +16,3 @@ module Tins
7
16
  end
8
17
  end
9
18
  end
10
-
11
- require 'tins/alias'
@@ -0,0 +1,27 @@
1
+ module Tins
2
+ # A module for deprecating methods with customizable messages and warnings.
3
+ #
4
+ # @example
5
+ # class MyClass
6
+ # extend Tins::Deprecate
7
+ # deprecate method: :old_method, new_method: :new_method
8
+ # end
9
+ module Deprecate
10
+ # Deprecates a method and issues a warning when called.
11
+ #
12
+ # @param method [ Symbol ] the name of the method to deprecate
13
+ # @param new_method [ Symbol ] the name of the replacement method
14
+ # @param message [ String ] the warning message to display
15
+ def deprecate(method:, new_method: nil, message: nil)
16
+ message ||= '[DEPRECATION] `%{method}` is deprecated. Please use `%{new_method}` instead.'
17
+ message = message % { method: method, new_method: new_method }
18
+ m = Module.new do
19
+ define_method(method) do |*a, **kw, &b|
20
+ warn message
21
+ super(*a, **kw, &b)
22
+ end
23
+ end
24
+ prepend m
25
+ end
26
+ end
27
+ end