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
data/lib/tins/xt/blank.rb CHANGED
@@ -1,34 +1,83 @@
1
1
  module Tins
2
+ # The Tins::Blank module provides a consistent way to check if objects are
3
+ # "blank" (empty, nil, or contain only whitespace). This follows Rails'
4
+ # convention.
5
+ #
6
+ # @example Basic usage
7
+ # "".blank? # => true
8
+ # " ".blank? # => true
9
+ # "foo".blank? # => false
10
+ # [].blank? # => true
11
+ # [1, 2].blank? # => false
12
+ # nil.blank? # => true
13
+ # false.blank? # => true
14
+ # true.blank? # => false
15
+ #
16
+ # @example Using present?
17
+ # "".present? # => false
18
+ # "foo".present? # => true
2
19
  module Blank
20
+ # Blank behavior for Object instances
3
21
  module Object
22
+ # Provides a fallback implementation that checks for `empty?` method,
23
+ # falling back to negation of truthiness if not defined.
24
+ #
25
+ # @return [Boolean] true if the object is considered blank, false otherwise
4
26
  def blank?
5
27
  respond_to?(:empty?) ? empty? : !self
6
28
  end
7
29
 
30
+ # Checks if the object is not blank
31
+ #
32
+ # @return [Boolean] true if the object is present, false otherwise
8
33
  def present?
9
34
  !blank?
10
35
  end
11
36
  end
12
37
 
38
+ # Blank behavior for NilClass instances
13
39
  module NilClass
40
+ #
41
+ # Nil values are always considered blank.
42
+ #
43
+ # @return [Boolean] true (always)
14
44
  def blank?
15
45
  true
16
46
  end
17
47
  end
18
48
 
49
+ # Blank behavior for FalseClass instances
19
50
  module FalseClass
51
+ # False values are always considered blank.
52
+ #
53
+ # @return [Boolean] true (always)
20
54
  def blank?
21
55
  true
22
56
  end
23
57
  end
24
58
 
59
+ # Blank behavior for TrueClass instances
25
60
  module TrueClass
61
+ # True values are never considered blank.
62
+ #
63
+ # @return [Boolean] false (always)
26
64
  def blank?
27
65
  false
28
66
  end
29
67
  end
30
68
 
69
+ # Blank behavior for Array instances
70
+ #
71
+ # Arrays are blank if they are empty.
72
+ # This implementation aliases the `empty?` method to `blank?`.
31
73
  module Array
74
+ # The included method is a hook that gets called when this module is
75
+ # included in another class or module.
76
+ #
77
+ # It sets up blank? behavior by aliasing the empty? method to blank? in
78
+ # the including class/module.
79
+ #
80
+ # @param modul [Object] the class or module that includes this module
32
81
  def self.included(modul)
33
82
  modul.module_eval do
34
83
  alias_method :blank?, :empty?
@@ -36,7 +85,18 @@ module Tins
36
85
  end
37
86
  end
38
87
 
88
+ # Blank behavior for Hash instances
89
+ #
90
+ # Hashes are blank if they are empty.
91
+ # This implementation aliases the `empty?` method to `blank?`.
39
92
  module Hash
93
+ # The included method is a hook that gets called when this module is
94
+ # included in another class or module.
95
+ #
96
+ # It sets up blank? behavior by aliasing the empty? method to blank? in
97
+ # the including class/module.
98
+ #
99
+ # @param modul [Object] the class or module that includes this module
40
100
  def self.included(modul)
41
101
  modul.module_eval do
42
102
  alias_method :blank?, :empty?
@@ -44,20 +104,39 @@ module Tins
44
104
  end
45
105
  end
46
106
 
107
+ # Blank behavior for String instances
47
108
  module String
109
+ # Strings are blank if they contain only whitespace characters.
110
+ # This uses a regex match against non-whitespace characters.
111
+ #
112
+ # @return [Boolean] true if the string contains only whitespace, false otherwise
48
113
  def blank?
49
114
  self !~ /\S/
50
115
  end
51
116
  end
52
117
 
118
+ # Blank behavior for Numeric instances
119
+ #
120
+ # Numbers are considered blank only if they are zero.
121
+ # This implementation aliases the `zero?` method to `blank?`.
53
122
  module Numeric
54
- def blank?
55
- false
123
+ # The included method is a hook that gets called when this module is
124
+ # included in another class or module.
125
+ #
126
+ # It sets up blank? behavior by aliasing the zero? method to blank? in
127
+ # the including class/module.
128
+ #
129
+ # @param modul [Object] the class or module that includes this module
130
+ def self.included(modul)
131
+ modul.module_eval do
132
+ alias_method :blank?, :zero?
133
+ end
56
134
  end
57
135
  end
58
136
  end
59
137
  end
60
138
 
139
+ # Extend constant classes with Blank behavior unless blank? is already defined
61
140
  unless Object.respond_to?(:blank?)
62
141
  for k in Tins::Blank.constants
63
142
  Object.const_get(k).class_eval do
@@ -1,19 +1,70 @@
1
1
  require 'tins/concern'
2
2
 
3
3
  module Tins
4
+ # Concern provides a mechanism for module configuration that persists across
5
+ # inheritance and inclusion boundaries using thread-local storage.
6
+ #
7
+ # This module implements a pattern where modules can be configured with
8
+ # arguments that are then available throughout the module's lifecycle in
9
+ # a thread-safe manner. It's particularly useful for implementing
10
+ # configuration-based concerns that need to maintain state across
11
+ # different scopes.
12
+ #
13
+ # @example Configured concern usage
14
+ # module MyConcern
15
+ # extend Tins::Concern
16
+ # end
17
+ #
18
+ # # Configure the concern with parameters
19
+ # include MyConcern.tins_concern_configure(:option1, :option2)
4
20
  module Concern
21
+ # ModuleMixin provides thread-local storage for concern configuration.
22
+ #
23
+ # This mixin adds methods to any module that includes it, allowing for
24
+ # configuration of concerns through thread-local storage. The configuration
25
+ # is stored in the current thread's context and persists during the
26
+ # execution of code that uses this concern.
27
+ #
28
+ # @note This implementation relies on Thread.current which makes it
29
+ # thread-safe but scoped to individual threads.
5
30
  module ModuleMixin
31
+ # Configures the module with the given arguments and returns self.
32
+ #
33
+ # This method stores the provided arguments in thread-local storage,
34
+ # making them available via {tins_concern_args}. It's designed to be
35
+ # chainable (returns self).
36
+ #
37
+ # @param args [Array] Arguments to configure this concern with
38
+ # @return [Module] The module itself, for chaining
39
+ # @example
40
+ # MyConcern.tins_concern_configure(:option1, :option2)
6
41
  def tins_concern_configure(*args)
7
42
  Thread.current[:tin_concern_args] = args
8
43
  self
9
44
  end
10
45
 
46
+ # Retrieves the current concern configuration arguments.
47
+ #
48
+ # This method fetches the arguments that were previously set using
49
+ # {tins_concern_configure}. If no configuration has been set, it returns
50
+ # nil.
51
+ #
52
+ # @return [Array, nil] The stored configuration arguments or nil
53
+ # @example
54
+ # MyConcern.tins_concern_configure(:option1, :option2)
55
+ # puts MyConcern.tins_concern_args # => [:option1, :option2]
11
56
  def tins_concern_args
12
57
  Thread.current[:tin_concern_args]
13
58
  end
14
59
  end
15
60
  end
16
61
 
62
+ # Extends the core Module class with the concern functionality.
63
+ #
64
+ # This line makes the concern configuration methods available to all modules
65
+ # in the system, allowing any module to be configured as a concern.
66
+ #
67
+ # @see Tins::Concern::ModuleMixin
17
68
  class ::Module
18
69
  include Tins::Concern::ModuleMixin
19
70
  end
@@ -1,7 +1,9 @@
1
1
  require 'tins/deep_dup'
2
2
 
3
3
  module Tins
4
- class ::Object
5
- include Tins::DeepDup
4
+ unless Object.respond_to?(:deep_dup)
5
+ class ::Object
6
+ include Tins::DeepDup
7
+ end
6
8
  end
7
9
  end
@@ -0,0 +1,5 @@
1
+ require 'tins/deprecate'
2
+
3
+ class Module
4
+ include Tins::Deprecate
5
+ end
data/lib/tins/xt/full.rb CHANGED
@@ -1,18 +1,51 @@
1
1
  require 'tins/xt/blank'
2
2
 
3
3
  module Tins
4
+ # Provides methods for checking if objects are "full" (non-blank) and safely
5
+ # processing them in conditional contexts.
6
+ #
7
+ # This module adds the `full?` and `all_full?` methods to all objects, enabling
8
+ # clean, readable patterns for validation and conditional processing.
9
+ #
10
+ # @example Basic usage
11
+ # "hello".full? # => "hello"
12
+ # "".full? # => nil
13
+ #
14
+ # @example Method dispatch with block
15
+ # user.full?(:name) { |name| "Hello #{name}" } # Returns "Hello John" if name is full
16
+ #
17
+ # @example Safe assignment and processing
18
+ # if name = user.full?(:name)
19
+ # puts "Hello #{name}"
20
+ # end
4
21
  module Full
5
- # Returns the object if it isn't blank (as in Object#blank?), otherwise it
6
- # returns nil. If a block was given as an argument and the object isn't
7
- # blank, the block is executed with the object as its first argument. If an
8
- # argument +dispatch+ was given and the object wasn't blank the method
9
- # given by dispatch is called on the object. This is the same as
10
- # foo.full?(&:bar) in the previous block form.
22
+ # Checks if the object is not blank, returning the object itself if it's
23
+ # full, or nil if it's blank. If a method name is provided as +dispatch+,
24
+ # that method is called on the object and the result is checked for being
25
+ # full.
26
+ #
27
+ # @param dispatch [Symbol, nil] The method to call on the object (optional)
28
+ # @param args [Array] Arguments to pass to the dispatched method (optional)
29
+ # @yield [Object] Optional block to execute with the result if result or
30
+ # dispatched result not nil
31
+ # @return [Object, nil] The object itself if not blank, or the result of
32
+ # dispatching +dispatch+ if provided and valid, or nil if the object is blank
33
+ #
34
+ # @example Basic usage
35
+ # "hello".full? # => "hello"
36
+ # "".full? # => nil
37
+ #
38
+ # @example Method dispatch
39
+ # user.full?(:name) # Returns user.name if not blank, nil otherwise
40
+ #
41
+ # @example Method dispatch with arguments
42
+ # user.full?(:method_with_args, arg1, arg2)
43
+ #
44
+ # @example With block execution
45
+ # user.full?(:name) { |name| "Hello #{name}" }
11
46
  def full?(dispatch = nil, *args)
12
47
  if blank?
13
48
  obj = nil
14
- #elsif Module === dispatch # TODO
15
- # dispatch.found?(self)
16
49
  elsif dispatch
17
50
  obj = __send__(dispatch, *args)
18
51
  obj = nil if obj.blank?
@@ -26,14 +59,26 @@ module Tins
26
59
  end
27
60
  end
28
61
 
62
+ # Checks if all elements in a collection are "full" (not blank). If the
63
+ # object responds to +all?+ and all elements pass the +full?+ test, then
64
+ # the block is executed with the collection itself or the collection is returned.
65
+ #
66
+ # @return [Object, nil] The collection if all elements are full, otherwise nil
67
+ #
68
+ # @example Basic usage
69
+ # [1,2,3].all_full? # => [1,2,3]
70
+ # [1,nil,3].all_full? # => nil
71
+ #
72
+ # @example With block execution
73
+ # [1,2,3].all_full? { |array| array.sum }
29
74
  def all_full?
30
75
  if respond_to?(:all?) && all?(&:full?)
31
76
  block_given? ? yield(self) : self
32
77
  end
33
78
  end
34
- end
35
79
 
36
- class ::Object
37
- include Full
80
+ class ::Object
81
+ include Full
82
+ end
38
83
  end
39
84
  end
@@ -0,0 +1,7 @@
1
+ require 'tins/hash_bfs'
2
+
3
+ module Tins
4
+ class ::Hash
5
+ include HashBFS
6
+ end
7
+ end
data/lib/tins/xt/irb.rb CHANGED
@@ -1,9 +1,42 @@
1
1
  require 'irb'
2
2
 
3
+ # Provides interactive debugging capabilities through the IRB console.
4
+ #
5
+ # This module adds an `examine` method to all objects, allowing developers to
6
+ # quickly drop into an interactive IRB session with the current binding or
7
+ # examine specific objects. It's particularly useful for debugging and exploring
8
+ # data structures during development.
9
+ #
10
+ # @example Basic usage
11
+ # # Drop into IRB with current context
12
+ # examine
13
+ #
14
+ # @example Examine a specific object
15
+ # data = [1, 2, 3]
16
+ # examine data # Inspects just the 'data' variable
17
+ #
18
+ # @example Use from within methods
19
+ # def process_data
20
+ # result = expensive_operation
21
+ # examine result # Debug the result immediately
22
+ # end
3
23
  module Tins
24
+ # We have our own IRB as well.
4
25
  IRB = ::IRB
5
26
 
27
+ # We extend the top level IRB module
6
28
  module ::IRB
29
+ # Starts an interactive IRB session with the given binding context. This
30
+ # method creates a new IRB instance and evaluates input from it, allowing for
31
+ # interactive exploration of variables and objects.
32
+ #
33
+ # @param binding [Binding, nil] The binding context to examine (defaults to TOPLEVEL_BINDING)
34
+ #
35
+ # @example Start IRB with current context
36
+ # examine
37
+ #
38
+ # @example Examine specific binding
39
+ # examine some_binding
7
40
  def self.examine(binding = TOPLEVEL_BINDING)
8
41
  setup nil
9
42
  workspace = WorkSpace.new binding
@@ -13,9 +46,20 @@ module Tins
13
46
  rescue Interrupt
14
47
  exit
15
48
  end
16
- end
17
49
 
18
- class ::Object
50
+ # Starts an interactive IRB session examining the current object and its context.
51
+ # This instance method provides a convenient way to debug objects without
52
+ # explicitly passing bindings.
53
+ #
54
+ # @param binding [Binding, nil] The binding context to examine (defaults to TOPLEVEL_BINDING)
55
+ # @return [void]
56
+ #
57
+ # @example Examine the current object
58
+ # my_object.examine
59
+ #
60
+ # @example Examine a specific variable
61
+ # data = [1, 2, 3]
62
+ # data.examine # Inspects just the 'data' variable
19
63
  def examine(binding = TOPLEVEL_BINDING)
20
64
  IRB.examine(binding)
21
65
  end
@@ -3,21 +3,9 @@ require 'tins/method_description'
3
3
  module Tins
4
4
  class ::UnboundMethod
5
5
  include MethodDescription
6
-
7
- alias to_s description
8
-
9
- def inspect
10
- "#<#{self.class}: #{description}>"
11
- end
12
6
  end
13
7
 
14
8
  class ::Method
15
9
  include MethodDescription
16
-
17
- alias to_s description
18
-
19
- def inspect
20
- "#<#{self.class}: #{description}>"
21
- end
22
10
  end
23
11
  end
@@ -0,0 +1,7 @@
1
+ require 'tins/minimize'
2
+
3
+ module Tins
4
+ class ::Array
5
+ include Tins::Minimize
6
+ end
7
+ end
data/lib/tins/xt/named.rb CHANGED
@@ -1,23 +1,78 @@
1
- require 'tins/xt/string_version'
1
+ module Tins
2
+ # A dynamically created module class used internally by the
3
+ # {Tins::Object#named} and {Tins::Module#named} methods to create dynamic
4
+ # methods.
5
+ #
6
+ # This class inherits from Module and serves as a template for creating
7
+ # dynamically scoped methods that can be extended or included into objects
8
+ # or classes.
9
+ Named = ::Class.new(::Module)
2
10
 
3
- class Object
4
- def named(name, method, *args, &named_block)
5
- extend Module.new {
6
- define_method(name) do |*rest, &block|
7
- block = named_block if named_block
8
- __send__(method, *(args + rest), &block)
11
+ class ::Object
12
+ # Adds a dynamically created method to the object instance. The method will
13
+ # call the specified +method+ with optional +args+ and combine any provided
14
+ # +named_block+ with runtime blocks.
15
+ #
16
+ # @param name [Symbol] The name of the method to create
17
+ # @param method [Symbol] The existing method to delegate to
18
+ # @param args [Array] Optional arguments to pre-bind to the delegated method
19
+ # @yield [Object] Optional block to be used as the method's block
20
+ # @return [Object] self
21
+ # @example Create a method that maps elements
22
+ # a = [1, 2, 3]
23
+ # a.named(:double, :map) { |x| x * 2 }
24
+ # a.double # => [2, 4, 6]
25
+ #
26
+ # @example Pre-bind arguments to a method
27
+ # def process(data, multiplier, &block)
28
+ # data.map { |x| block.call(x * multiplier) }
29
+ # end
30
+ #
31
+ # Object.named(:process_by_10, :process, 10) do |result|
32
+ # result + 1
33
+ # end
34
+ # process_by_10([1, 2, 3]) { |x| x * 2 } # => [21, 41, 61]
35
+ def named(name, method, *args, &named_block)
36
+ name = name.to_sym
37
+ m = Tins::Named.new {
38
+ define_method(name) do |*rest, &block|
39
+ block = named_block if named_block
40
+ __send__(method, *(args + rest), &block)
41
+ end
42
+ }
43
+ if m.respond_to?(:set_temporary_name)
44
+ m.set_temporary_name "#{m.class} for method #{name.inspect}"
9
45
  end
10
- }
46
+ extend m
47
+ end
11
48
  end
12
- end
13
49
 
14
- class Module
15
- def named(name, method, *args, &named_block)
16
- include Module.new {
17
- define_method(name) do |*rest, &block|
18
- block = named_block if named_block
19
- __send__(method, *(args + rest), &block)
50
+ class ::Module
51
+ # Adds a dynamically created method to all instances of the class. The
52
+ # method will call the specified +method+ with optional +args+ and combine
53
+ # any provided +named_block+ with runtime blocks.
54
+ #
55
+ # @param name [Symbol] The name of the method to create
56
+ # @param method [Symbol] The existing method to delegate to
57
+ # @param args [Array] Optional arguments to pre-bind to the delegated method
58
+ # @yield [Object] Optional block to be used as the method's block
59
+ # @return [Module] self
60
+ #
61
+ # @example Create a class-level method
62
+ # Array.named(:sum_all, :reduce) { |acc, x| acc + x }
63
+ # [1, 2, 3].sum_all # => 6
64
+ def named(name, method, *args, &named_block)
65
+ name = name.to_sym
66
+ m = Tins::Named.new {
67
+ define_method(name) do |*rest, &block|
68
+ block = named_block if named_block
69
+ __send__(method, *(args + rest), &block)
70
+ end
71
+ }
72
+ if m.respond_to?(:set_temporary_name)
73
+ m.set_temporary_name "#{m.class} for method #{name.inspect}"
20
74
  end
21
- }
75
+ include m
76
+ end
22
77
  end
23
78
  end
@@ -4,4 +4,8 @@ module Tins
4
4
  class ::Proc
5
5
  include Tins::ProcCompose
6
6
  end
7
+
8
+ class ::Method
9
+ include Tins::ProcCompose
10
+ end
7
11
  end
@@ -1,10 +1,6 @@
1
1
  require 'tins/secure_write'
2
2
 
3
3
  module Tins
4
- #class ::Object
5
- # include Tins::SecureWrite
6
- #end
7
-
8
4
  class ::IO
9
5
  extend Tins::SecureWrite
10
6
  end
@@ -3,4 +3,5 @@ module Tins
3
3
  require 'tins/xt/string_underscore'
4
4
  require 'tins/xt/string_version'
5
5
  require 'tins/xt/string_byte_order_mark'
6
+ require 'tins/xt/string_named_placeholders'
6
7
  end
@@ -1,6 +1,8 @@
1
1
  module Tins
2
2
  require 'tins/string_camelize'
3
- class ::String
4
- include StringCamelize
3
+ unless String.respond_to?(:camelize)
4
+ class ::String
5
+ include StringCamelize
6
+ end
5
7
  end
6
8
  end
@@ -0,0 +1,7 @@
1
+ module Tins
2
+ require 'tins/string_named_placeholders'
3
+
4
+ class ::String
5
+ include StringNamedPlaceholders
6
+ end
7
+ end
@@ -1,6 +1,8 @@
1
1
  module Tins
2
2
  require 'tins/string_underscore'
3
- class ::String
4
- include StringUnderscore
3
+ unless String.respond_to?(:underscore)
4
+ class ::String
5
+ include StringUnderscore
6
+ end
5
7
  end
6
8
  end
@@ -4,6 +4,17 @@ module Tins
4
4
  class ::Hash
5
5
  include Tins::Subhash
6
6
 
7
+ # The subhash! method creates a filtered subset of this hash based on the
8
+ # given patterns and replaces the current hash with the result.
9
+ #
10
+ # This method works by first calling subhash with the provided patterns to
11
+ # create a new hash containing only the matching key-value pairs, then
12
+ # replacing the contents of the current hash with those pairs.
13
+ #
14
+ # @param patterns [Array<Object>] One or more patterns to match against
15
+ # keys
16
+ # @return [Hash] Returns self after replacing its contents with the
17
+ # filtered subset
7
18
  def subhash!(*patterns)
8
19
  replace subhash(*patterns)
9
20
  end
@@ -2,12 +2,49 @@ require 'tins/xt/time_dummy'
2
2
  require 'tins/xt/date_time_dummy'
3
3
  require 'tins/xt/date_dummy'
4
4
 
5
- module Tins::TimeFreezer
6
- def self.freeze(time)
7
- Time.dummy(time) do
8
- DateTime.dummy(time) do
9
- Date.dummy(time) do
10
- yield
5
+ module Tins
6
+ # TimeFreezer provides a mechanism to temporarily freeze time across multiple
7
+ # time-related classes.
8
+ #
9
+ # This module allows you to temporarily replace the behavior of Time, DateTime,
10
+ # and Date classes with dummy implementations that always return a specific
11
+ # time value. This is particularly useful for testing code that depends on
12
+ # current time values.
13
+ #
14
+ # @example Basic usage
15
+ # Tins::TimeFreezer.freeze(Time.new(2023, 1, 1)) do
16
+ # # All Time, DateTime, and Date calls will return the frozen time
17
+ # puts Time.now # => 2023-01-01 00:00:00 +0000
18
+ # end
19
+ #
20
+ # @example With DateTime and Date
21
+ # Tins::TimeFreezer.freeze(Time.new(2023, 1, 1)) do
22
+ # puts DateTime.now # => 2023-01-01T00:00:00+00:00
23
+ # puts Date.today # => 2023-01-01
24
+ # end
25
+ #
26
+ # @example Using time string (will be parsed by Time.parse)
27
+ # Tins::TimeFreezer.freeze("2023-01-01 12:00:00") do
28
+ # # Time.now will return the parsed time
29
+ # puts Time.now # => 2023-01-01 12:00:00 +0000
30
+ # end
31
+ module TimeFreezer
32
+ # Freezes time for the duration of the given block.
33
+ #
34
+ # This method temporarily replaces the behavior of Time, DateTime, and Date
35
+ # classes with dummy implementations that always return the specified time
36
+ # value.
37
+ #
38
+ # @param time [Time, DateTime, Date] The time value to freeze all
39
+ # time-related classes to
40
+ # @yield [] The block of code to execute with frozen time
41
+ # @return [Object] The return value of the yielded block
42
+ def self.freeze(time)
43
+ Time.dummy(time) do
44
+ DateTime.dummy(time) do
45
+ Date.dummy(time) do
46
+ yield
47
+ end
11
48
  end
12
49
  end
13
50
  end
data/lib/tins/xt/write.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  require 'tins/write'
2
2
 
3
3
  module Tins
4
- #class ::Object
5
- # include Tins::Write
6
- #end
7
-
8
4
  class ::IO
9
5
  extend Tins::Write
10
6
  end