tins 1.43.0 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.contexts/code_comment.rb +5 -8
- data/.contexts/lib.rb +0 -2
- data/CHANGES.md +12 -0
- data/README.md +158 -6
- data/Rakefile +19 -16
- data/examples/let.rb +8 -40
- data/examples/mail.rb +0 -1
- data/examples/turing.rb +3 -1
- data/lib/tins/alias.rb +1 -0
- data/lib/tins/annotate.rb +37 -27
- data/lib/tins/ask_and_send.rb +41 -0
- data/lib/tins/attempt.rb +39 -0
- data/lib/tins/bijection.rb +34 -0
- data/lib/tins/case_predicate.rb +21 -0
- data/lib/tins/complete.rb +16 -0
- data/lib/tins/concern.rb +64 -0
- data/lib/tins/date_dummy.rb +36 -4
- data/lib/tins/date_time_dummy.rb +34 -2
- data/lib/tins/deep_dup.rb +9 -2
- data/lib/tins/deprecate.rb +12 -0
- data/lib/tins/dslkit.rb +559 -83
- data/lib/tins/duration.rb +120 -5
- data/lib/tins/expose.rb +54 -5
- data/lib/tins/extract_last_argument_options.rb +9 -0
- data/lib/tins/file_binary.rb +104 -21
- data/lib/tins/find.rb +114 -11
- data/lib/tins/generator.rb +10 -2
- data/lib/tins/go.rb +81 -4
- data/lib/tins/hash_bfs.rb +4 -2
- data/lib/tins/hash_symbolize_keys_recursive.rb +62 -4
- data/lib/tins/hash_union.rb +47 -2
- data/lib/tins/if_predicate.rb +31 -0
- data/lib/tins/implement.rb +50 -0
- data/lib/tins/limited.rb +54 -5
- data/lib/tins/lines_file.rb +81 -2
- data/lib/tins/lru_cache.rb +54 -17
- data/lib/tins/memoize.rb +86 -58
- data/lib/tins/method_description.rb +87 -4
- data/lib/tins/minimize.rb +39 -11
- data/lib/tins/module_group.rb +27 -2
- data/lib/tins/named_set.rb +20 -0
- data/lib/tins/null.rb +86 -15
- data/lib/tins/once.rb +61 -4
- data/lib/tins/p.rb +44 -8
- data/lib/tins/partial_application.rb +66 -7
- data/lib/tins/proc_compose.rb +58 -1
- data/lib/tins/proc_prelude.rb +97 -10
- data/lib/tins/range_plus.rb +30 -2
- data/lib/tins/require_maybe.rb +36 -0
- data/lib/tins/responding.rb +39 -0
- data/lib/tins/secure_write.rb +24 -4
- data/lib/tins/sexy_singleton.rb +45 -48
- data/lib/tins/string_byte_order_mark.rb +33 -2
- data/lib/tins/string_camelize.rb +31 -2
- data/lib/tins/string_underscore.rb +33 -2
- data/lib/tins/string_version.rb +179 -10
- data/lib/tins/subhash.rb +35 -10
- data/lib/tins/temp_io.rb +7 -0
- data/lib/tins/temp_io_enum.rb +19 -0
- data/lib/tins/terminal.rb +31 -9
- data/lib/tins/thread_local.rb +67 -5
- data/lib/tins/time_dummy.rb +46 -21
- data/lib/tins/to.rb +15 -0
- data/lib/tins/to_proc.rb +17 -4
- data/lib/tins/token.rb +56 -1
- data/lib/tins/unit.rb +288 -149
- data/lib/tins/version.rb +1 -1
- data/lib/tins/write.rb +14 -3
- data/lib/tins/xt/blank.rb +81 -2
- data/lib/tins/xt/concern.rb +51 -0
- data/lib/tins/xt/full.rb +56 -11
- data/lib/tins/xt/irb.rb +46 -2
- data/lib/tins/xt/method_description.rb +0 -12
- data/lib/tins/xt/minimize.rb +7 -0
- data/lib/tins/xt/named.rb +71 -16
- data/lib/tins/xt/proc_compose.rb +4 -0
- data/lib/tins/xt/subhash.rb +11 -0
- data/lib/tins/xt/time_freezer.rb +43 -6
- data/lib/tins/xt.rb +1 -3
- data/lib/tins.rb +16 -3
- data/tests/duration_test.rb +4 -0
- data/tests/from_module_test.rb +30 -2
- data/tests/implement_test.rb +6 -8
- data/tests/lines_file_test.rb +2 -0
- data/tests/lru_cache_test.rb +12 -0
- data/tests/method_description_test.rb +14 -20
- data/tests/partial_application_test.rb +4 -0
- data/tests/proc_prelude_test.rb +1 -1
- data/tests/scope_test.rb +1 -1
- data/tests/string_version_test.rb +2 -0
- data/tests/to_test.rb +6 -6
- data/tins.gemspec +9 -9
- metadata +23 -41
- data/lib/tins/count_by.rb +0 -21
- data/lib/tins/deep_const_get.rb +0 -64
- data/lib/tins/timed_cache.rb +0 -51
- data/lib/tins/uniq_by.rb +0 -23
- data/lib/tins/xt/count_by.rb +0 -7
- data/lib/tins/xt/deep_const_get.rb +0 -7
- data/lib/tins/xt/uniq_by.rb +0 -25
- data/tests/count_by_test.rb +0 -17
- data/tests/deep_const_get_test.rb +0 -37
- data/tests/uniq_by_test.rb +0 -31
- /data/{COPYING → LICENSE} +0 -0
@@ -1,64 +1,115 @@
|
|
1
1
|
module Tins
|
2
|
+
# Provides methods for describing method signatures and parameters.
|
3
|
+
#
|
4
|
+
# This module is designed to be included in method objects to provide
|
5
|
+
# introspection capabilities, particularly useful for generating
|
6
|
+
# documentation or debugging method signatures.
|
2
7
|
module MethodDescription
|
8
|
+
# Represents individual parameters with specific types and names.
|
3
9
|
class Parameters
|
10
|
+
# Base class for all parameter types.
|
4
11
|
class Parameter < Struct.new(:type, :name)
|
12
|
+
# Compares two parameters by their type.
|
13
|
+
#
|
14
|
+
# @param other [Parameter] The other parameter to compare against
|
15
|
+
# @return [Boolean] Whether the types are equal
|
5
16
|
def ==(other)
|
6
17
|
type == other.type
|
7
18
|
end
|
8
19
|
|
20
|
+
# Returns a string representation of the parameter for debugging.
|
21
|
+
#
|
22
|
+
# @return [String] A debug-friendly representation
|
9
23
|
def inspect
|
10
24
|
"#<#{self.class} #{to_s.inspect}>"
|
11
25
|
end
|
12
26
|
end
|
13
27
|
|
28
|
+
# Represents a splat parameter (*args).
|
14
29
|
class RestParameter < Parameter
|
30
|
+
# Converts the parameter to its string form.
|
31
|
+
#
|
32
|
+
# @return [String] Formatted as "*name"
|
15
33
|
def to_s
|
16
34
|
"*#{name}"
|
17
35
|
end
|
18
36
|
end
|
19
37
|
|
38
|
+
# Represents a keyword rest parameter (**kwargs).
|
20
39
|
class KeyrestParameter < Parameter
|
40
|
+
# Converts the parameter to its string form.
|
41
|
+
#
|
42
|
+
# @return [String] Formatted as "**name"
|
21
43
|
def to_s
|
22
44
|
"**#{name}"
|
23
45
|
end
|
24
46
|
end
|
25
47
|
|
48
|
+
# Represents a required parameter.
|
26
49
|
class ReqParameter < Parameter
|
50
|
+
# Converts the parameter to its string form.
|
51
|
+
#
|
52
|
+
# @return [String] The parameter name
|
27
53
|
def to_s
|
28
54
|
name.to_s
|
29
55
|
end
|
30
56
|
end
|
31
57
|
|
58
|
+
# Represents an optional parameter (with default value).
|
32
59
|
class OptParameter < Parameter
|
60
|
+
# Converts the parameter to its string form.
|
61
|
+
#
|
62
|
+
# @return [String] Formatted as "name=..."
|
33
63
|
def to_s
|
34
|
-
"#{name}
|
64
|
+
"#{name}=…"
|
35
65
|
end
|
36
66
|
end
|
37
67
|
|
68
|
+
# Represents a keyword parameter.
|
38
69
|
class KeyParameter < Parameter
|
70
|
+
# Converts the parameter to its string form.
|
71
|
+
#
|
72
|
+
# @return [String] Formatted as "name:..."
|
39
73
|
def to_s
|
40
|
-
"#{name}
|
74
|
+
"#{name}:…"
|
41
75
|
end
|
42
76
|
end
|
43
77
|
|
78
|
+
# Represents a required keyword parameter (without default).
|
44
79
|
class KeyreqParameter < Parameter
|
80
|
+
# Converts the parameter to its string form.
|
81
|
+
#
|
82
|
+
# @return [String] Formatted as "name:"
|
45
83
|
def to_s
|
46
84
|
"#{name}:"
|
47
85
|
end
|
48
86
|
end
|
49
87
|
|
88
|
+
# Represents a block parameter (&block).
|
50
89
|
class BlockParameter < Parameter
|
90
|
+
# Converts the parameter to its string form.
|
91
|
+
#
|
92
|
+
# @return [String] Formatted as "&name"
|
51
93
|
def to_s
|
52
94
|
"&#{name}"
|
53
95
|
end
|
54
96
|
end
|
55
97
|
|
98
|
+
# Represents a generic parameter type not covered by other classes.
|
56
99
|
class GenericParameter < Parameter
|
100
|
+
# Converts the parameter to its string form.
|
101
|
+
#
|
102
|
+
# @return [String] Formatted as "name:type"
|
57
103
|
def to_s
|
58
104
|
[ name, type ] * ?:
|
59
105
|
end
|
60
106
|
end
|
61
107
|
|
108
|
+
# Builds a parameter instance based on type and name.
|
109
|
+
#
|
110
|
+
# @param type [Symbol] The type of parameter (e.g., :req, :opt)
|
111
|
+
# @param name [String, Symbol] The parameter name
|
112
|
+
# @return [Parameter] An appropriate Parameter subclass or GenericParameter
|
62
113
|
def self.build(type, name)
|
63
114
|
parameter_classname = "#{type.to_s.capitalize}Parameter"
|
64
115
|
parameter_class =
|
@@ -71,38 +122,70 @@ module Tins
|
|
71
122
|
end
|
72
123
|
end
|
73
124
|
|
125
|
+
# Represents the signature of a method including all its parameters.
|
74
126
|
class Signature
|
127
|
+
# Initializes a new signature with given parameters.
|
128
|
+
#
|
129
|
+
# @param parameters [Array<Parameters::Parameter>] The list of parameters
|
75
130
|
def initialize(*parameters)
|
76
131
|
@parameters = parameters
|
77
132
|
end
|
78
133
|
|
134
|
+
# Returns the parameters associated with this signature.
|
135
|
+
#
|
136
|
+
# @return [Array<Parameters::Parameter>] The method's parameters
|
79
137
|
attr_reader :parameters
|
80
138
|
|
139
|
+
# Checks if two signatures are equal based on their parameters.
|
140
|
+
#
|
141
|
+
# @param other [Signature] The other signature to compare against
|
142
|
+
# @return [Boolean] Whether the signatures are equal
|
81
143
|
def eql?(other)
|
82
144
|
@parameters.eql? other.parameters
|
83
145
|
end
|
84
146
|
|
147
|
+
# Checks if two signatures are equal (alias for eql?).
|
148
|
+
#
|
149
|
+
# @param other [Signature] The other signature to compare against
|
150
|
+
# @return [Boolean] Whether the signatures are equal
|
85
151
|
def ==(other)
|
86
152
|
@parameters == other.parameters
|
87
153
|
end
|
88
154
|
|
155
|
+
# Pattern matching operator for comparing with a method's signature.
|
156
|
+
#
|
157
|
+
# @param method [Method] The method whose signature we're checking
|
158
|
+
# @return [Boolean] Whether this signature matches the method's signature
|
89
159
|
def ===(method)
|
90
160
|
self == method.signature
|
91
161
|
end
|
92
162
|
|
163
|
+
# Converts the signature to a comma-separated string.
|
164
|
+
#
|
165
|
+
# @return [String] The parameters formatted as a string
|
93
166
|
def to_s
|
94
167
|
@parameters * ?,
|
95
168
|
end
|
96
169
|
|
170
|
+
# Returns a detailed string representation for debugging.
|
171
|
+
#
|
172
|
+
# @return [String] A debug-friendly representation of the signature
|
97
173
|
def inspect
|
98
174
|
"#<#{self.class} (#{to_s})>"
|
99
175
|
end
|
100
176
|
end
|
101
177
|
|
178
|
+
# Retrieves the signature of the method this module is included in.
|
179
|
+
#
|
180
|
+
# @return [Signature] The method's signature
|
102
181
|
def signature
|
103
182
|
description style: :parameters
|
104
183
|
end
|
105
184
|
|
185
|
+
# Generates a human-readable description of the method.
|
186
|
+
#
|
187
|
+
# @param style [:namespace, :name, :parameters] The output format style
|
188
|
+
# @return [String, Signature] The formatted description or signature object
|
106
189
|
def description(style: :namespace)
|
107
190
|
valid_styles = %i[ namespace name parameters ]
|
108
191
|
valid_styles.include?(style) or
|
@@ -122,10 +205,10 @@ module Tins
|
|
122
205
|
return signature
|
123
206
|
end
|
124
207
|
end
|
125
|
-
result = ''
|
208
|
+
result = +''
|
126
209
|
if style == :namespace
|
127
210
|
if owner <= Module
|
128
|
-
result << receiver.to_s << ?.
|
211
|
+
result << receiver.to_s << ?.
|
129
212
|
else
|
130
213
|
result << owner.name.to_s << ?#
|
131
214
|
end
|
data/lib/tins/minimize.rb
CHANGED
@@ -1,19 +1,43 @@
|
|
1
1
|
module Tins
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# Tins::Minimize provides methods for compressing sequential data into ranges
|
3
|
+
# and expanding ranges back into individual elements.
|
4
|
+
#
|
5
|
+
# This module is designed for objects that respond to `[]` and `size` methods,
|
6
|
+
# such as Array, and whose elements respond to the `succ` method (like String
|
7
|
+
# and Numeric types). It's particularly useful for representing sequential data
|
8
|
+
# more compactly and efficiently.
|
9
|
+
#
|
10
|
+
# @example Basic minimization
|
11
|
+
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize
|
12
|
+
# # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
13
|
+
#
|
14
|
+
# @example Numeric minimization
|
15
|
+
# [ 1, 2, 3, 7, 9, 10, 11 ].minimize
|
16
|
+
# # => [ 1..3, 7..7, 9..11 ]
|
17
|
+
#
|
18
|
+
# @example Sorting before minimization
|
19
|
+
# [ 5, 1, 4, 2 ].sort.minimize
|
20
|
+
# # => [ 1..2, 4..5 ]
|
21
|
+
#
|
22
|
+
# @example Unminimization
|
23
|
+
# [ 'A'..'C', 'G'..'G', 'K'..'M' ].unminimize
|
24
|
+
# # => [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ]
|
5
25
|
module Minimize
|
6
26
|
# Returns a minimized version of this object, that is successive elements
|
7
27
|
# are substituted with ranges a..b. In the situation ..., x, y,... and y !=
|
8
28
|
# x.succ a range x..x is created, to make it easier to iterate over all the
|
9
|
-
# ranges in one run.
|
10
|
-
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
29
|
+
# ranges in one run.
|
11
30
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
31
|
+
# @return [Array<Range>] An array of ranges representing the minimized data
|
32
|
+
# @example Sequential string elements
|
33
|
+
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize
|
34
|
+
# # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
35
|
+
#
|
36
|
+
# @example Numeric elements
|
37
|
+
# [ 1, 2, 3, 7, 9, 10, 11 ].minimize
|
38
|
+
# # => [ 1..3, 7..7, 9..11 ]
|
15
39
|
def minimize
|
16
|
-
result
|
40
|
+
result = []
|
17
41
|
last_index = size - 1
|
18
42
|
size.times do |i|
|
19
43
|
result << [ self[0] ] if i == 0
|
@@ -27,6 +51,8 @@ module Tins
|
|
27
51
|
|
28
52
|
# First minimizes this object, then calls the replace method with the
|
29
53
|
# result.
|
54
|
+
#
|
55
|
+
# @return [Object] Returns self (modified in place)
|
30
56
|
def minimize!
|
31
57
|
replace minimize
|
32
58
|
end
|
@@ -35,6 +61,8 @@ module Tins
|
|
35
61
|
# [ 'A'..'C', 'G'..'G', 'K'..'M' ].unminimize # => [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ]
|
36
62
|
# and
|
37
63
|
# [ 1..2, 4..5 ].unminimize # => [ 1, 2, 4, 5 ]
|
64
|
+
#
|
65
|
+
# @return [Array] An array of individual elements expanded from ranges
|
38
66
|
def unminimize
|
39
67
|
result = []
|
40
68
|
for range in self
|
@@ -46,10 +74,10 @@ module Tins
|
|
46
74
|
end
|
47
75
|
|
48
76
|
# Invert a minimized version of this object in place.
|
77
|
+
#
|
78
|
+
# @return [Object] Returns self (modified in place)
|
49
79
|
def unminimize!
|
50
80
|
replace unminimize
|
51
81
|
end
|
52
82
|
end
|
53
83
|
end
|
54
|
-
|
55
|
-
require 'tins/alias'
|
data/lib/tins/module_group.rb
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
module Tins
|
2
|
+
# A module that allows grouping multiple modules together for type checking.
|
3
|
+
#
|
4
|
+
# This implementation creates a shared module that gets included in each of the
|
5
|
+
# specified modules, enabling the use of `===` for type checking across all
|
6
|
+
# grouped modules simultaneously.
|
2
7
|
module ModuleGroup
|
8
|
+
# Creates a new module group from the given modules.
|
9
|
+
#
|
10
|
+
# This method dynamically creates an anonymous module and includes it in
|
11
|
+
# each of the provided modules. The returned module can then be used with
|
12
|
+
# the `===` operator for type checking across all grouped modules.
|
13
|
+
#
|
14
|
+
# @note This modifies the included modules by adding the new module to their
|
15
|
+
# inheritance chain, which can have side effects in complex class hierarchies.
|
16
|
+
#
|
17
|
+
# @param modules [Array<Module>] One or more modules to include in this group
|
18
|
+
# @return [Module] A new anonymous module that is included in all passed modules
|
19
|
+
#
|
20
|
+
# @example Creating a module group
|
21
|
+
# MyGroup = Tins::ModuleGroup[Array, String, Hash]
|
22
|
+
# MyGroup === [] # => true
|
23
|
+
# MyGroup === "" # => true
|
24
|
+
# MyGroup === {} # => true
|
25
|
+
#
|
26
|
+
# case some_value
|
27
|
+
# when MyGroup
|
28
|
+
# handle_collection_or_string(some_value)
|
29
|
+
# end
|
3
30
|
def self.[](*modules)
|
4
31
|
modul = Module.new
|
5
32
|
modules.each do |m|
|
@@ -9,5 +36,3 @@ module Tins
|
|
9
36
|
end
|
10
37
|
end
|
11
38
|
end
|
12
|
-
|
13
|
-
require 'tins/alias'
|
data/lib/tins/named_set.rb
CHANGED
@@ -1,12 +1,32 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
3
|
module Tins
|
4
|
+
# NamedSet extends Ruby's Set class to include a descriptive name.
|
5
|
+
#
|
6
|
+
# This class provides all the functionality of Ruby's standard Set while
|
7
|
+
# adding a named identifier that can be useful for debugging, logging, and
|
8
|
+
# identification purposes. The name is stored as an instance variable and can
|
9
|
+
# be accessed or modified
|
10
|
+
# through the `name` accessor.
|
11
|
+
#
|
12
|
+
# @example Basic usage
|
13
|
+
# set = Tins::NamedSet.new("user_roles")
|
14
|
+
# set.add(:admin)
|
15
|
+
# set.add(:user)
|
16
|
+
# puts set.name # => "user_roles"
|
17
|
+
# puts set.to_a # => [:admin, :user]
|
4
18
|
class NamedSet < Set
|
19
|
+
# Initializes a new NamedSet with the given name.
|
20
|
+
#
|
21
|
+
# @param name [String] A descriptive name for this set
|
5
22
|
def initialize(name)
|
6
23
|
@name = name
|
7
24
|
super()
|
8
25
|
end
|
9
26
|
|
27
|
+
# Gets or sets the name of this NamedSet.
|
28
|
+
#
|
29
|
+
# @return [String] The current name of the set
|
10
30
|
attr_accessor :name
|
11
31
|
end
|
12
32
|
end
|
data/lib/tins/null.rb
CHANGED
@@ -1,68 +1,131 @@
|
|
1
1
|
module Tins
|
2
|
-
#
|
2
|
+
# Tins::Null provides an implementation of the null object pattern in Ruby.
|
3
|
+
#
|
4
|
+
# The null object pattern is a behavioral design pattern that allows you to
|
5
|
+
# avoid null references by providing a default object that implements the
|
6
|
+
# expected interface but does nothing. This eliminates the need for null
|
7
|
+
# checks throughout your codebase.
|
8
|
+
#
|
9
|
+
# This module provides the core functionality for null objects, including:
|
10
|
+
# - Method missing behavior that returns self
|
11
|
+
# - Type conversion methods that return appropriate default values
|
12
|
+
# - Debugging support through NullPlus
|
13
|
+
#
|
14
|
+
# @example Basic usage
|
15
|
+
# # Instead of checking for nil:
|
16
|
+
# user = find_user(id)
|
17
|
+
# if user
|
18
|
+
# user.name
|
19
|
+
# else
|
20
|
+
# "Unknown"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # You can use the null object:
|
24
|
+
# user = find_user(id) || Tins::NULL
|
25
|
+
# user.name # => "" (instead of nil)
|
26
|
+
#
|
27
|
+
# @example With NullPlus for debugging
|
28
|
+
# user = find_user(id) || Tins::NullPlus.new(value: "Unknown", caller: caller)
|
29
|
+
# user.value # => "Unknown"
|
3
30
|
module Null
|
31
|
+
# Handle missing methods by returning self, allowing method chaining.
|
32
|
+
#
|
33
|
+
# @return [self] Always returns self to allow chaining
|
4
34
|
def method_missing(*)
|
5
35
|
self
|
6
36
|
end
|
7
37
|
|
38
|
+
# Handle missing constants by returning self.
|
39
|
+
#
|
40
|
+
# @return [self] Always returns self
|
8
41
|
def const_missing(*)
|
9
42
|
self
|
10
43
|
end
|
11
44
|
|
45
|
+
# Convert to string representation.
|
46
|
+
#
|
47
|
+
# @return [String] Empty string
|
12
48
|
def to_s
|
13
49
|
''
|
14
50
|
end
|
15
51
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
52
|
+
# Convert to float.
|
53
|
+
#
|
54
|
+
# @return [Float] Zero as float
|
20
55
|
def to_f
|
21
56
|
0.0
|
22
57
|
end
|
23
58
|
|
59
|
+
# Convert to integer.
|
60
|
+
#
|
61
|
+
# @return [Integer] Zero
|
24
62
|
def to_i
|
25
63
|
0
|
26
64
|
end
|
27
65
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
66
|
+
# Convert to array.
|
67
|
+
#
|
68
|
+
# @return [Array] Empty array
|
32
69
|
def to_a
|
33
70
|
[]
|
34
71
|
end
|
35
72
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
73
|
+
# Inspect representation.
|
74
|
+
#
|
75
|
+
# @return [String] "NULL"
|
40
76
|
def inspect
|
41
77
|
'NULL'
|
42
78
|
end
|
43
79
|
|
80
|
+
# Check if object is nil.
|
81
|
+
#
|
82
|
+
# @return [Boolean] Always returns true
|
44
83
|
def nil?
|
45
84
|
true
|
46
85
|
end
|
47
86
|
|
87
|
+
# Check if object is blank.
|
88
|
+
#
|
89
|
+
# @return [Boolean] Always returns true
|
48
90
|
def blank?
|
49
91
|
true
|
50
92
|
end
|
51
93
|
|
94
|
+
# Convert to JSON (for compatibility with JSON serialization).
|
95
|
+
#
|
96
|
+
# @return [nil] returns nil value
|
52
97
|
def as_json(*)
|
98
|
+
nil
|
53
99
|
end
|
54
100
|
|
101
|
+
# Convert to JSON string.
|
102
|
+
#
|
103
|
+
# @return [String] "null"
|
55
104
|
def to_json(*)
|
56
105
|
'null'
|
57
106
|
end
|
58
107
|
|
108
|
+
# Kernel extensions for null object creation.
|
59
109
|
module Kernel
|
110
|
+
# Create a null object or return the provided value if not nil.
|
111
|
+
#
|
112
|
+
# @param value [Object] The value to check
|
113
|
+
# @return [Object] Tins::NULL if value is nil, otherwise value
|
60
114
|
def null(value = nil)
|
61
115
|
value.nil? ? Tins::NULL : value
|
62
116
|
end
|
63
117
|
|
64
118
|
alias Null null
|
65
119
|
|
120
|
+
# Create a null object with additional debugging information.
|
121
|
+
#
|
122
|
+
# This creates a NullPlus object that includes caller information for
|
123
|
+
# debugging purposes when the null object is used.
|
124
|
+
#
|
125
|
+
# @param opts [Hash] Options for the null object
|
126
|
+
# @option opts [Object] :value The value to return from the null object
|
127
|
+
# @option opts [Array] :caller Caller information
|
128
|
+
# @return [Object] Tins::NullPlus if value is nil, otherwise value
|
66
129
|
def null_plus(opts = {})
|
67
130
|
value = opts[:value]
|
68
131
|
opts[:caller] = caller
|
@@ -77,17 +140,27 @@ module Tins
|
|
77
140
|
end
|
78
141
|
end
|
79
142
|
|
143
|
+
# NullClass represents the singleton null object instance.
|
80
144
|
class NullClass < Module
|
81
145
|
include Tins::Null
|
82
146
|
end
|
83
147
|
|
148
|
+
# The singleton null object instance.
|
84
149
|
NULL = NullClass.new
|
85
150
|
|
151
|
+
# Freeze the singleton to prevent modification.
|
86
152
|
NULL.freeze
|
87
153
|
|
154
|
+
# Enhanced null object with debugging capabilities.
|
155
|
+
#
|
156
|
+
# NullPlus extends the basic null object with additional features for debugging,
|
157
|
+
# including caller information and custom attribute access.
|
88
158
|
class NullPlus
|
89
159
|
include Tins::Null
|
90
160
|
|
161
|
+
# Initialize a NullPlus object with options.
|
162
|
+
#
|
163
|
+
# @param opts [Hash] Configuration options
|
91
164
|
def initialize(opts = {})
|
92
165
|
singleton_class.class_eval do
|
93
166
|
opts.each do |name, value|
|
@@ -97,5 +170,3 @@ module Tins
|
|
97
170
|
end
|
98
171
|
end
|
99
172
|
end
|
100
|
-
|
101
|
-
require 'tins/alias'
|
data/lib/tins/once.rb
CHANGED
@@ -1,9 +1,52 @@
|
|
1
1
|
module Tins
|
2
|
+
# A module for ensuring exclusive execution of code blocks using file-based
|
3
|
+
# locking.
|
4
|
+
#
|
5
|
+
# This module provides mechanisms to prevent multiple instances of a script
|
6
|
+
# from running simultaneously by using file system locks on the script itself
|
7
|
+
# or a specified lock file.
|
8
|
+
#
|
9
|
+
# @example Basic usage with automatic lock file detection
|
10
|
+
# Tins::Once.only_once do
|
11
|
+
# # Critical section - only one instance runs at a time
|
12
|
+
# perform_backup()
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example Using custom lock file
|
16
|
+
# Tins::Once.only_once("/var/run/myapp.lock") do
|
17
|
+
# # Custom lock file
|
18
|
+
# process_data()
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example Non-blocking attempt
|
22
|
+
# begin
|
23
|
+
# Tins::Once.try_only_once do
|
24
|
+
# # Will raise if another instance holds the lock
|
25
|
+
# update_cache()
|
26
|
+
# end
|
27
|
+
# rescue => e
|
28
|
+
# puts "Another instance is running"
|
29
|
+
# end
|
2
30
|
module Once
|
3
31
|
include File::Constants
|
4
32
|
|
5
|
-
|
6
|
-
|
33
|
+
# Executes a block of code exclusively, ensuring only one instance runs at
|
34
|
+
# a time.
|
35
|
+
#
|
36
|
+
# Uses the script name (or specified lock file) as the locking mechanism.
|
37
|
+
# The first invocation will acquire an exclusive lock and execute the
|
38
|
+
# block, while subsequent invocations will block until the lock is
|
39
|
+
# released.
|
40
|
+
#
|
41
|
+
# @param lock_filename [String] Optional custom lock filename.
|
42
|
+
# Defaults to `$0` (the script name), which means the script itself
|
43
|
+
# is used as the lock file.
|
44
|
+
# @param locking_constant [Integer] File locking constant.
|
45
|
+
# Defaults to `LOCK_EX` for exclusive locking.
|
46
|
+
# @yield [void] The block of code to execute exclusively
|
47
|
+
# @return [Object] The return value of the yielded block
|
48
|
+
# @raise [Errno::ENOENT] If the lock file doesn't exist
|
49
|
+
# @raise [SystemCallError] If file locking fails
|
7
50
|
def only_once(lock_filename = nil, locking_constant = nil)
|
8
51
|
lock_filename ||= $0
|
9
52
|
locking_constant ||= LOCK_EX
|
@@ -16,10 +59,24 @@ module Tins
|
|
16
59
|
end
|
17
60
|
end
|
18
61
|
|
62
|
+
# Attempts to execute a block of code exclusively, but fails immediately
|
63
|
+
# if another instance holds the lock.
|
64
|
+
#
|
65
|
+
# This is a non-blocking version that will raise an exception if the lock
|
66
|
+
# cannot be acquired immediately.
|
67
|
+
#
|
68
|
+
# @param lock_filename [String] Optional custom lock filename.
|
69
|
+
# Defaults to `$0` (the script name).
|
70
|
+
# @param locking_constant [Integer] File locking constant.
|
71
|
+
# Defaults to `LOCK_EX | LOCK_NB` for non-blocking exclusive locking.
|
72
|
+
# @yield [void] The block of code to execute exclusively
|
73
|
+
# @return [Object] The return value of the yielded block
|
74
|
+
# @raise [Errno::EAGAIN] If another process holds the lock
|
75
|
+
# @raise [Errno::ENOENT] If the lock file doesn't exist
|
19
76
|
def try_only_once(lock_filename = nil, locking_constant = nil, &block)
|
20
77
|
only_once(lock_filename, locking_constant || LOCK_EX | LOCK_NB, &block)
|
21
78
|
end
|
79
|
+
|
80
|
+
module_function :only_once, :try_only_once
|
22
81
|
end
|
23
82
|
end
|
24
|
-
|
25
|
-
require 'tins/alias'
|
data/lib/tins/p.rb
CHANGED
@@ -1,23 +1,59 @@
|
|
1
1
|
require 'pp'
|
2
2
|
|
3
3
|
module Tins
|
4
|
+
# A module that provides debugging methods for inspecting objects by raising
|
5
|
+
# exceptions with their inspected representations.
|
6
|
+
#
|
7
|
+
# This module adds p! and pp! methods that raise RuntimeError exceptions
|
8
|
+
# containing the inspected or pretty-inspected output of objects, making it
|
9
|
+
# easy to quickly debug values during development without printing to stdout.
|
10
|
+
#
|
11
|
+
# @example Using p! to inspect a single object
|
12
|
+
# p!(some_variable)
|
13
|
+
#
|
14
|
+
# @example Using pp! to inspect multiple objects
|
15
|
+
# pp!(first_var, second_var)
|
4
16
|
module P
|
5
17
|
private
|
6
18
|
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
19
|
+
# Raises a RuntimeError with the inspected representation of the given
|
20
|
+
# objects.
|
21
|
+
#
|
22
|
+
# This method is useful for quick debugging by raising an exception that
|
23
|
+
# contains the inspected output of the provided objects. It behaves
|
24
|
+
# similarly to Ruby's built-in +p+ method but raises an exception instead
|
25
|
+
# of printing to stdout.
|
26
|
+
#
|
27
|
+
# @example Basic usage with single object
|
28
|
+
# p!(some_variable)
|
29
|
+
#
|
30
|
+
# @example Basic usage with multiple objects
|
31
|
+
# p!(first_var, second_var)
|
32
|
+
#
|
33
|
+
# @param objs [Array<Object>] One or more objects to inspect and raise
|
34
|
+
# @raise [RuntimeError] Always raises a RuntimeError with inspected content
|
10
35
|
def p!(*objs)
|
11
36
|
raise((objs.size < 2 ? objs.first : objs).inspect)
|
12
37
|
end
|
13
38
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
39
|
+
# Raises a RuntimeError with the pretty-inspected representation of the
|
40
|
+
# given objects.
|
41
|
+
#
|
42
|
+
# This method is useful for quick debugging by raising an exception that
|
43
|
+
# contains the pretty-printed output of the provided objects. It behaves
|
44
|
+
# similarly to Ruby's built-in +pp+ method but raises an exception instead
|
45
|
+
# of printing to stdout.
|
46
|
+
#
|
47
|
+
# @example Basic usage with single object
|
48
|
+
# pp!(some_variable)
|
49
|
+
#
|
50
|
+
# @example Basic usage with multiple objects
|
51
|
+
# pp!(first_var, second_var)
|
52
|
+
#
|
53
|
+
# @param objs [Array<Object>] One or more objects to pretty-inspect and raise
|
54
|
+
# @raise [RuntimeError] Always raises a RuntimeError with pretty-inspected content
|
17
55
|
def pp!(*objs)
|
18
56
|
raise("\n" + (objs.size < 2 ? objs.first : objs).pretty_inspect.chomp)
|
19
57
|
end
|
20
58
|
end
|
21
59
|
end
|
22
|
-
|
23
|
-
require 'tins/alias'
|