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
data/lib/tins/duration.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
1
3
|
module Tins
|
4
|
+
# A class to represent durations with support for formatting and parsing time
|
5
|
+
# intervals.
|
2
6
|
class Duration
|
3
7
|
include Comparable
|
4
8
|
|
@@ -7,14 +11,37 @@ module Tins
|
|
7
11
|
#
|
8
12
|
# @param [String] string The duration string to parse.
|
9
13
|
# @param [String] template for the duration format, see {#format}.
|
14
|
+
# Default: '%S%d+%h:%m:%s.%f'
|
10
15
|
#
|
11
16
|
# @return [Integer, Float] The number of (fractional) seconds of the duration.
|
12
17
|
#
|
13
|
-
# @
|
18
|
+
# @raise [ArgumentError] If the string doesn't match the expected template format
|
19
|
+
#
|
20
|
+
# @example Basic parsing
|
14
21
|
# Tins::Duration.parse('6+05:04:03', template: '%S%d+%h:%m:%s') # => 536643
|
15
22
|
# Tins::Duration.parse('6+05:04:03.21', template: '%S%d+%h:%m:%s.%f') # => 536643.21
|
23
|
+
#
|
24
|
+
# @example Parsing negative durations
|
25
|
+
# Tins::Duration.parse('-6+05:04:03', template: '%S%d+%h:%m:%s') # => -536643
|
26
|
+
#
|
27
|
+
# @example Custom template parsing
|
28
|
+
# Tins::Duration.parse('05:04:03.21', template: '%h:%m:%s.%f') # => 18243.21
|
29
|
+
#
|
30
|
+
# The parser supports the following directives in templates:
|
31
|
+
# - `%S` - Sign indicator (optional negative sign)
|
32
|
+
# - `%d` - Days component (integer)
|
33
|
+
# - `%h` - Hours component (integer)
|
34
|
+
# - `%m` - Minutes component (integer)
|
35
|
+
# - `%s` - Seconds component (integer)
|
36
|
+
# - `%f` - Fractional seconds component (decimal)
|
37
|
+
# - `%%` - Literal percent character
|
38
|
+
#
|
39
|
+
# The parser is greedy and consumes as much of the input string as possible
|
40
|
+
# for each directive.
|
41
|
+
# If a directive expects a specific format but doesn't find it, an
|
42
|
+
# ArgumentError is raised.
|
16
43
|
def self.parse(string, template: '%S%d+%h:%m:%s.%f')
|
17
|
-
s, t
|
44
|
+
s, t = string.to_s.dup, template.dup
|
18
45
|
d, sd = 0, 1
|
19
46
|
loop do
|
20
47
|
t.sub!(/\A(%[Sdhmsf%]|.)/) { |directive|
|
@@ -44,9 +71,13 @@ module Tins
|
|
44
71
|
sd * d
|
45
72
|
end
|
46
73
|
|
74
|
+
# Initializes a new Duration object with the specified number of seconds.
|
75
|
+
#
|
76
|
+
# @param seconds [ Integer, Float ] the total number of seconds to
|
77
|
+
# represent
|
47
78
|
def initialize(seconds)
|
48
|
-
@negative
|
49
|
-
seconds
|
79
|
+
@negative = seconds < 0
|
80
|
+
seconds = seconds.abs
|
50
81
|
@original_seconds = seconds
|
51
82
|
@days, @hours, @minutes, @seconds, @fractional_seconds =
|
52
83
|
[ 86_400, 3600, 60, 1, 0 ].inject([ [], seconds ]) {|(r, s), d|
|
@@ -60,40 +91,103 @@ module Tins
|
|
60
91
|
}
|
61
92
|
end
|
62
93
|
|
94
|
+
# Converts the original seconds value to a floating-point number.
|
95
|
+
#
|
96
|
+
# @return [Float] the original seconds value as a floating-point number
|
63
97
|
def to_f
|
64
98
|
@original_seconds.to_f
|
65
99
|
end
|
66
100
|
|
101
|
+
# The <=> method compares this object with another object after converting
|
102
|
+
# both to floats.
|
103
|
+
#
|
104
|
+
# @param other [Object] the object to compare with
|
105
|
+
#
|
106
|
+
# @return [Integer] -1 if this object is less than other, 0 if they are
|
107
|
+
# equal, 1 if this object is greater than other
|
67
108
|
def <=>(other)
|
68
109
|
to_f <=> other.to_f
|
69
110
|
end
|
70
111
|
|
112
|
+
# Returns true if the duration is negative.
|
113
|
+
#
|
114
|
+
# @return [TrueClass, FalseClass] true if the duration represents a
|
115
|
+
# negative time interval, false otherwise
|
71
116
|
def negative?
|
72
117
|
@negative
|
73
118
|
end
|
74
119
|
|
120
|
+
# Returns true if the duration includes days, false otherwise.
|
121
|
+
#
|
122
|
+
# @return [TrueClass, FalseClass] true if the duration has any days, false
|
123
|
+
# otherwise
|
75
124
|
def days?
|
76
125
|
@days > 0
|
77
126
|
end
|
78
127
|
|
128
|
+
# Returns true if the duration has any hours component
|
129
|
+
#
|
130
|
+
# @return [TrueClass, FalseClass] true if hours are present, false
|
131
|
+
# otherwise
|
79
132
|
def hours?
|
80
133
|
@hours > 0
|
81
134
|
end
|
82
135
|
|
136
|
+
# Returns true if the duration has minutes, false otherwise.
|
137
|
+
#
|
138
|
+
# @return [TrueClass, FalseClass] true if minutes are greater than 0, false otherwise
|
83
139
|
def minutes?
|
84
140
|
@minutes > 0
|
85
141
|
end
|
86
142
|
|
143
|
+
# Returns true if the duration has a positive seconds component.
|
144
|
+
#
|
145
|
+
# @return [TrueClass, FalseClass] true if seconds are greater than zero,
|
146
|
+
# false otherwise
|
87
147
|
def seconds?
|
88
148
|
@seconds > 0
|
89
149
|
end
|
90
150
|
|
151
|
+
# Returns true if the duration includes fractional seconds.
|
152
|
+
#
|
153
|
+
# @return [TrueClass, FalseClass] true if fractional seconds are present,
|
154
|
+
# false otherwise
|
91
155
|
def fractional_seconds?
|
92
156
|
@fractional_seconds > 0
|
93
157
|
end
|
94
158
|
|
159
|
+
# Formats the duration according to the given template and precision.
|
160
|
+
#
|
161
|
+
# The template string supports the following directives:
|
162
|
+
# - `%S` - Sign indicator (negative sign if duration is negative)
|
163
|
+
# - `%d` - Days component
|
164
|
+
# - `%h` - Hours component (zero-padded to 2 digits)
|
165
|
+
# - `%m` - Minutes component (zero-padded to 2 digits)
|
166
|
+
# - `%s` - Seconds component (zero-padded to 2 digits)
|
167
|
+
# - `%f` - Fractional seconds component (without the leading decimal point)
|
168
|
+
# - `%D` - Smart format (automatically includes days, fractional seconds, and sign)
|
169
|
+
# - `%%` - Literal percent character
|
170
|
+
#
|
171
|
+
# When using `%f`, the fractional part will be formatted according to the precision parameter.
|
172
|
+
#
|
173
|
+
# @param template [String] the format template to use for formatting
|
174
|
+
# Default: '%S%d+%h:%m:%s.%f'
|
175
|
+
# @param precision [Integer] the precision to use for fractional seconds
|
176
|
+
# When nil, uses default formatting with 6 decimal places
|
177
|
+
#
|
178
|
+
# @return [String] the formatted duration string
|
179
|
+
#
|
180
|
+
# @example Basic formatting
|
181
|
+
# duration = Tins::Duration.new(93784.123)
|
182
|
+
# duration.format('%d+%h:%m:%s.%f') # => "1+02:03:04.123000"
|
183
|
+
#
|
184
|
+
# @example Smart format
|
185
|
+
# duration.format('%D') # => "1+02:03:04.123"
|
186
|
+
#
|
187
|
+
# @example Custom precision
|
188
|
+
# duration.format('%s.%f', precision: 2) # => "04.12"
|
95
189
|
def format(template = '%S%d+%h:%m:%s.%f', precision: nil)
|
96
|
-
result = template.gsub(/%[DdhmSs]/) { |directive|
|
190
|
+
result = template.gsub(/%[DdhmSs%]/) { |directive|
|
97
191
|
case directive
|
98
192
|
when '%S' then ?- if negative?
|
99
193
|
when '%d' then @days
|
@@ -101,6 +195,7 @@ module Tins
|
|
101
195
|
when '%m' then '%02u' % @minutes
|
102
196
|
when '%s' then '%02u' % @seconds
|
103
197
|
when '%D' then format_smart
|
198
|
+
when '%%' then '%'
|
104
199
|
end
|
105
200
|
}
|
106
201
|
if result.include?('%f')
|
@@ -114,12 +209,32 @@ module Tins
|
|
114
209
|
result
|
115
210
|
end
|
116
211
|
|
212
|
+
# The to_s method returns a string representation of the duration by
|
213
|
+
# formatting it using the smart format method
|
214
|
+
#
|
215
|
+
# @return [String] the formatted duration string
|
117
216
|
def to_s
|
118
217
|
format_smart
|
119
218
|
end
|
120
219
|
|
121
220
|
private
|
122
221
|
|
222
|
+
# The format_smart method provides intelligent formatting based on the
|
223
|
+
# duration's components. It automatically determines which components are
|
224
|
+
# present and formats them accordingly, making it ideal for human-readable
|
225
|
+
# output where you want to avoid showing zero values.
|
226
|
+
#
|
227
|
+
# The smart format follows these rules:
|
228
|
+
# - If days are present, includes the day component (e.g., "1+02:03:04") -
|
229
|
+
# If fractional seconds are present, includes them with 3 decimal places
|
230
|
+
# (e.g., ".123")
|
231
|
+
# - If the duration is negative, includes the sign prefix
|
232
|
+
#
|
233
|
+
# This method is used internally by #to_s and can also be called directly
|
234
|
+
# for smart formatting using the %D directive.
|
235
|
+
#
|
236
|
+
# @return [String] a formatted duration string using intelligent component
|
237
|
+
# inclusion
|
123
238
|
def format_smart
|
124
239
|
template = '%h:%m:%s'
|
125
240
|
precision = nil
|
data/lib/tins/expose.rb
CHANGED
@@ -1,14 +1,63 @@
|
|
1
1
|
module Tins
|
2
|
+
# A module that provides a way to expose private and protected methods for
|
3
|
+
# testing or debugging purposes.
|
4
|
+
#
|
5
|
+
# This module is particularly useful in test suites where you need to verify
|
6
|
+
# internal implementation details without making methods public, or for
|
7
|
+
# debugging complex object states during development.
|
8
|
+
#
|
9
|
+
# @example Basic method exposure
|
10
|
+
# obj = MyClass.new
|
11
|
+
# exposed_obj = obj.expose
|
12
|
+
# exposed_obj.private_method # Accessible now
|
13
|
+
#
|
14
|
+
# @example Direct method call
|
15
|
+
# obj = MyClass.new
|
16
|
+
# result = obj.expose(:private_method)
|
17
|
+
# # Equivalent to: obj.private_method
|
18
|
+
#
|
19
|
+
# @example Block execution
|
20
|
+
# obj = MyClass.new
|
21
|
+
# obj.expose { self.private_method }
|
22
|
+
#
|
23
|
+
# @example Testing private methods
|
24
|
+
# describe MyClass do
|
25
|
+
# it "should handle edge cases" do
|
26
|
+
# obj = MyClass.new
|
27
|
+
# result = obj.expose(:private_calculation, arg1, arg2)
|
28
|
+
# expect(result).to eq(expected_value)
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# @note This module should only be used in test or debugging contexts.
|
33
|
+
# Using it in production code can compromise encapsulation.
|
34
|
+
# @note Modifying the exposed object's state may affect the original object
|
35
|
+
# since it operates on a duplicated instance.
|
2
36
|
module Expose
|
3
37
|
# Expose any (private/protected) method or internal state of this object
|
4
38
|
# returning the result for specing purposes.
|
5
39
|
#
|
6
|
-
#
|
7
|
-
# (shortcut for reader methods).
|
8
|
-
# @param block [ Proc ] any private/protected methods of the object can be
|
9
|
-
# called in this block.
|
40
|
+
# This method provides three distinct usage patterns:
|
10
41
|
#
|
11
|
-
#
|
42
|
+
# 1. **Method Call**: When given a method name, directly calls that method
|
43
|
+
# and returns its result.
|
44
|
+
#
|
45
|
+
# 2. **Block Execution**: When given a block, evaluates the block in the
|
46
|
+
# context of the object, allowing access to private/protected methods.
|
47
|
+
#
|
48
|
+
# 3. **Full Exposure**: When called without arguments, returns a duplicate
|
49
|
+
# of the object with all private and protected methods exposed as public.
|
50
|
+
#
|
51
|
+
# @param method_name [Symbol, String, nil] name of the method to call,
|
52
|
+
# or nil for full exposure
|
53
|
+
# @param args [Array] arguments to pass to the method when calling it
|
54
|
+
# @param block [Proc] block to execute in the context of the object
|
55
|
+
#
|
56
|
+
# @return [Object] result of the method call, block execution, or a new
|
57
|
+
# object with exposed methods (when called without args)
|
58
|
+
#
|
59
|
+
# @raise [NoMethodError] if method_name is given but doesn't exist
|
60
|
+
# @raise [ArgumentError] if both method_name and block are provided
|
12
61
|
def expose(method_name = nil, *args, &block)
|
13
62
|
if block
|
14
63
|
instance_eval(&block)
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module Tins
|
2
|
+
# Extracts the last argument from an array if it responds to to_hash
|
3
|
+
#
|
4
|
+
# This module provides a method to separate arguments into regular arguments
|
5
|
+
# and options (a hash) by checking if the last element responds to to_hash
|
2
6
|
module ExtractLastArgumentOptions
|
7
|
+
# Extracts the last argument if it responds to to_hash and returns an array
|
8
|
+
# with the remaining elements and the extracted options hash.
|
9
|
+
#
|
10
|
+
# @return [Array<Object, Hash>] an array containing the sliced array and
|
11
|
+
# the extracted options hash
|
3
12
|
def extract_last_argument_options
|
4
13
|
last_argument = last
|
5
14
|
if last_argument.respond_to?(:to_hash) and
|
data/lib/tins/file_binary.rb
CHANGED
@@ -1,37 +1,76 @@
|
|
1
1
|
module Tins
|
2
|
+
# A module for detecting and analyzing binary files based on content patterns
|
3
|
+
#
|
4
|
+
# This module provides functionality to determine whether a file contains
|
5
|
+
# binary data by examining its content against various thresholds for
|
6
|
+
# null bytes and high-order bits. It's useful for identifying files that
|
7
|
+
# are not plain text, such as images, executables, or other binary formats.
|
8
|
+
#
|
9
|
+
# The detection is performed by scanning a specified portion of the file
|
10
|
+
# and calculating the percentage of bytes that match binary criteria.
|
2
11
|
module FileBinary
|
12
|
+
# Constants used for binary detection logic
|
3
13
|
module Constants
|
14
|
+
# Seek constant for absolute positioning
|
4
15
|
SEEK_SET = ::File::SEEK_SET
|
5
16
|
|
6
|
-
|
7
|
-
|
17
|
+
# Regular expression matching null bytes (zero bytes)
|
18
|
+
ZERO_RE = +"\x00"
|
8
19
|
|
20
|
+
# Regular expression matching binary bytes (high-order bits set)
|
21
|
+
BINARY_RE = +"\x01-\x1f\x7f-\xff"
|
22
|
+
|
23
|
+
# Ensure proper encoding for Ruby 1.9+
|
9
24
|
if defined?(::Encoding)
|
10
25
|
ZERO_RE.force_encoding(Encoding::ASCII_8BIT)
|
11
26
|
BINARY_RE.force_encoding(Encoding::ASCII_8BIT)
|
12
27
|
end
|
13
28
|
end
|
14
29
|
|
30
|
+
# Default configuration options for binary detection
|
15
31
|
class << self
|
16
|
-
#
|
32
|
+
# Accessor for default options hash
|
17
33
|
attr_accessor :default_options
|
18
34
|
end
|
35
|
+
|
36
|
+
# Configuration hash with sensible defaults for binary file detection
|
19
37
|
self.default_options = {
|
38
|
+
# Starting offset for scanning file content
|
20
39
|
offset: 0,
|
21
|
-
|
40
|
+
|
41
|
+
# Buffer size in bytes for reading file content
|
42
|
+
buffer_size: 2 ** 13, # 8KB
|
43
|
+
|
44
|
+
# Percentage threshold for binary bytes (high-order bits set)
|
22
45
|
percentage_binary: 30.0,
|
46
|
+
|
47
|
+
# Percentage threshold for null bytes (zeros)
|
23
48
|
percentage_zeros: 0.0,
|
24
49
|
}
|
25
50
|
|
26
|
-
#
|
27
|
-
#
|
51
|
+
# Determines if a file is considered binary based on content analysis
|
52
|
+
#
|
53
|
+
# A file is classified as binary if either:
|
54
|
+
# 1. The percentage of null bytes exceeds the configured threshold
|
55
|
+
# 2. The percentage of binary bytes (bytes with high-order bit set) exceeds
|
56
|
+
# the configured threshold
|
57
|
+
#
|
58
|
+
# @param options [Hash] Configuration options for binary detection
|
59
|
+
# @option options [Integer] :offset (0) Starting position in file to begin analysis
|
60
|
+
# @option options [Integer] :buffer_size (8192) Number of bytes to read for analysis
|
61
|
+
# @option options [Float] :percentage_binary (30.0) Binary byte threshold percentage
|
62
|
+
# @option options [Float] :percentage_zeros (0.0) Null byte threshold percentage
|
63
|
+
#
|
64
|
+
# @return [Boolean, nil] true if binary, false if not binary, nil if file is empty
|
65
|
+
#
|
66
|
+
# @example Basic usage
|
67
|
+
# FileBinary.binary?('large_binary_file.dat') # => true
|
28
68
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# from FileBinary.default_options is used instead.
|
69
|
+
# @example Custom thresholds
|
70
|
+
# FileBinary.binary?('file.txt', percentage_binary: 10.0) # => false
|
71
|
+
#
|
72
|
+
# @example With offset and buffer size
|
73
|
+
# FileBinary.binary?('file.log', offset: 1024, buffer_size: 4096) # => true
|
35
74
|
def binary?(options = {})
|
36
75
|
options = FileBinary.default_options.merge(options)
|
37
76
|
old_pos = tell
|
@@ -47,9 +86,21 @@ module Tins
|
|
47
86
|
old_pos and seek old_pos, Constants::SEEK_SET
|
48
87
|
end
|
49
88
|
|
50
|
-
# Returns
|
51
|
-
#
|
52
|
-
#
|
89
|
+
# Returns the logical opposite of binary? - true if file is not binary (ASCII/text)
|
90
|
+
#
|
91
|
+
# @param options [Hash] Configuration options for ASCII detection
|
92
|
+
# @option options [Integer] :offset (0) Starting position in file to begin analysis
|
93
|
+
# @option options [Integer] :buffer_size (8192) Number of bytes to read for analysis
|
94
|
+
# @option options [Float] :percentage_binary (30.0) Binary byte threshold percentage
|
95
|
+
# @option options [Float] :percentage_zeros (0.0) Null byte threshold percentage
|
96
|
+
#
|
97
|
+
# @return [Boolean, nil] true if ASCII/text, false if binary, nil if file is empty
|
98
|
+
#
|
99
|
+
# @example Basic usage
|
100
|
+
# FileBinary.ascii?('script.rb') # => true
|
101
|
+
#
|
102
|
+
# @example With custom options
|
103
|
+
# FileBinary.ascii?('data.bin', percentage_zeros: 5.0) # => false
|
53
104
|
def ascii?(options = {})
|
54
105
|
case binary?(options)
|
55
106
|
when true then false
|
@@ -57,6 +108,9 @@ module Tins
|
|
57
108
|
end
|
58
109
|
end
|
59
110
|
|
111
|
+
# Module inclusion hook that extends the including class with ClassMethods
|
112
|
+
#
|
113
|
+
# @param modul [Module] The module that is including FileBinary
|
60
114
|
def self.included(modul)
|
61
115
|
modul.instance_eval do
|
62
116
|
extend ClassMethods
|
@@ -64,20 +118,49 @@ module Tins
|
|
64
118
|
super
|
65
119
|
end
|
66
120
|
|
121
|
+
# Class methods that provide file-level binary detection capabilities
|
122
|
+
#
|
123
|
+
# These methods allow binary detection without manually opening files
|
67
124
|
module ClassMethods
|
68
|
-
#
|
69
|
-
#
|
125
|
+
# Determines if a file is considered binary by name
|
126
|
+
#
|
127
|
+
# @param name [String] Path to the file to analyze
|
128
|
+
# @param options [Hash] Configuration options for binary detection
|
129
|
+
# @option options [Integer] :offset (0) Starting position in file to begin analysis
|
130
|
+
# @option options [Integer] :buffer_size (8192) Number of bytes to read for analysis
|
131
|
+
# @option options [Float] :percentage_binary (30.0) Binary byte threshold percentage
|
132
|
+
# @option options [Float] :percentage_zeros (0.0) Null byte threshold percentage
|
133
|
+
#
|
134
|
+
# @return [Boolean, nil] true if binary, false if not binary, nil if file is empty
|
135
|
+
#
|
136
|
+
# @example Basic usage
|
137
|
+
# FileBinary.binary?('config.json') # => false
|
138
|
+
#
|
139
|
+
# @example With custom options
|
140
|
+
# FileBinary.binary?('data.dat', percentage_binary: 25.0) # => true
|
70
141
|
def binary?(name, options = {})
|
71
142
|
open(name, 'rb') { |f| f.binary?(options) }
|
72
143
|
end
|
73
144
|
|
74
|
-
#
|
75
|
-
#
|
145
|
+
# Determines if a file is considered ASCII/text by name
|
146
|
+
#
|
147
|
+
# @param name [String] Path to the file to analyze
|
148
|
+
# @param options [Hash] Configuration options for ASCII detection
|
149
|
+
# @option options [Integer] :offset (0) Starting position in file to begin analysis
|
150
|
+
# @option options [Integer] :buffer_size (8192) Number of bytes to read for analysis
|
151
|
+
# @option options [Float] :percentage_binary (30.0) Binary byte threshold percentage
|
152
|
+
# @option options [Float] :percentage_zeros (0.0) Null byte threshold percentage
|
153
|
+
#
|
154
|
+
# @return [Boolean, nil] true if ASCII/text, false if binary, nil if file is empty
|
155
|
+
#
|
156
|
+
# @example Basic usage
|
157
|
+
# FileBinary.ascii?('readme.md') # => true
|
158
|
+
#
|
159
|
+
# @example With custom options
|
160
|
+
# FileBinary.ascii?('binary_file.dat', percentage_zeros: 10.0) # => false
|
76
161
|
def ascii?(name, options = {})
|
77
162
|
open(name, 'rb') { |f| f.ascii?(options) }
|
78
163
|
end
|
79
164
|
end
|
80
165
|
end
|
81
166
|
end
|
82
|
-
|
83
|
-
require 'tins/alias'
|