activesupport 7.0.8.6 → 7.0.9
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/CHANGELOG.md +66 -0
- data/lib/active_support/cache/file_store.rb +1 -1
- data/lib/active_support/cache/memory_store.rb +5 -3
- data/lib/active_support/core_ext/class/attribute.rb +0 -1
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/object/json.rb +6 -4
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/overlaps.rb +27 -1
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/deprecation/behaviors.rb +2 -2
- data/lib/active_support/deprecation/reporting.rb +8 -5
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/logger_thread_safe_level.rb +1 -0
- data/lib/active_support/notifications/instrumenter.rb +17 -9
- data/lib/active_support/number_helper/number_converter.rb +12 -3
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
- data/lib/active_support/number_helper.rb +379 -318
- data/lib/active_support/testing/assertions.rb +1 -1
- metadata +104 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 016e2913f33ba54595a5a6760dc3d228b75dba7a5ea6019bf3633ed1398c3554
|
|
4
|
+
data.tar.gz: 69361bd65952fd588551a2f2b2031cad8aa3b4834a05a08958dc25f7cdd15168
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 764c23f42df81f5231a69341f5f3fae03c8da75515f653ad4aadba0766b2047883a88b2566ffb7e1cb2eb53a532eb169083a6e911907e9572a8af36ce31dcc88
|
|
7
|
+
data.tar.gz: 298e9dc61c2c1324d81d5b3a2f6fe5f7565d32fb2c7b697b9dca48cba7dad72b1351b97e0bf42463fa93b6ba234f0ce5e9dcdc6b764c84b0662b6e45b594effc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,69 @@
|
|
|
1
|
+
## Rails 7.0.9 (October 28, 2025) ##
|
|
2
|
+
|
|
3
|
+
* Fix `ActiveSupport::Notifications.publish_event` to preserve units.
|
|
4
|
+
|
|
5
|
+
This solves the incorrect reporting of time spent running Active Record
|
|
6
|
+
asynchronous queries (by a factor `1000`).
|
|
7
|
+
|
|
8
|
+
*Jean Boussier*
|
|
9
|
+
|
|
10
|
+
* Fix ActiveSupport::Deprecation to handle blaming generated code
|
|
11
|
+
|
|
12
|
+
*Jean Boussier*, *fatkodima*
|
|
13
|
+
|
|
14
|
+
* Fix `#to_fs(:human_size)` to correctly work with negative numbers.
|
|
15
|
+
|
|
16
|
+
*Earlopain*
|
|
17
|
+
|
|
18
|
+
* Add `bigdecimal` as Active Support dependency that is a bundled gem candidate for Ruby 3.4.
|
|
19
|
+
|
|
20
|
+
`bigdecimal` 3.1.4 or higher version will be installed.
|
|
21
|
+
Ruby 2.7 and 3.0 users who want `bigdecimal` version 2.0.0 or 3.0.0 behavior as a default gem,
|
|
22
|
+
pin the `bigdecimal` version in your application Gemfile.
|
|
23
|
+
|
|
24
|
+
*Koichi ITO*
|
|
25
|
+
|
|
26
|
+
* Ensure `{down,up}case_first` returns non-frozen string.
|
|
27
|
+
|
|
28
|
+
*Jonathan Hefner*
|
|
29
|
+
|
|
30
|
+
* Add `drb`, `mutex_m` and `base64` that are bundled gem candidates for Ruby 3.4
|
|
31
|
+
|
|
32
|
+
*Yasuo Honda*
|
|
33
|
+
|
|
34
|
+
* Fix `delete_matched` for file cache store to work with keys longer than the
|
|
35
|
+
max filename size.
|
|
36
|
+
|
|
37
|
+
*fatkodima* and *Jonathan Hefner*
|
|
38
|
+
|
|
39
|
+
* Fix MemoryStore to prevent race conditions when incrementing or decrementing.
|
|
40
|
+
|
|
41
|
+
*Pierre Jambet*
|
|
42
|
+
|
|
43
|
+
* Fix MemoryStore to preserve entries TTL when incrementing or decrementing
|
|
44
|
+
|
|
45
|
+
This is to be more consistent with how MemCachedStore and RedisCacheStore behaves.
|
|
46
|
+
|
|
47
|
+
*Jean Boussier*
|
|
48
|
+
|
|
49
|
+
* NumberHelper: handle objects responding to_d.
|
|
50
|
+
|
|
51
|
+
*fatkodima*
|
|
52
|
+
|
|
53
|
+
* NumberHelper: handle very large numbers.
|
|
54
|
+
|
|
55
|
+
*Alex Ghiculescu*, *fatkodima*
|
|
56
|
+
|
|
57
|
+
* Fix Range#overlaps? not taking empty ranges into account on Ruby < 3.3
|
|
58
|
+
|
|
59
|
+
*Nobuyoshi Nakada*, *Shouichi Kamiya*, *Hartley McGuire*
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Rails 7.0.8.7 (December 10, 2024) ##
|
|
63
|
+
|
|
64
|
+
* No changes.
|
|
65
|
+
|
|
66
|
+
|
|
1
67
|
## Rails 7.0.8.6 (October 23, 2024) ##
|
|
2
68
|
|
|
3
69
|
* No changes.
|
|
@@ -152,7 +152,7 @@ module ActiveSupport
|
|
|
152
152
|
|
|
153
153
|
# Translate a file path into a key.
|
|
154
154
|
def file_path_key(path)
|
|
155
|
-
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
|
|
155
|
+
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
|
|
156
156
|
URI.decode_www_form_component(fname, Encoding::UTF_8)
|
|
157
157
|
end
|
|
158
158
|
|
|
@@ -190,10 +190,12 @@ module ActiveSupport
|
|
|
190
190
|
|
|
191
191
|
def modify_value(name, amount, options)
|
|
192
192
|
options = merged_options(options)
|
|
193
|
+
|
|
193
194
|
synchronize do
|
|
194
|
-
if
|
|
195
|
-
num =
|
|
196
|
-
|
|
195
|
+
if entry = read_entry(name, **options)
|
|
196
|
+
num = entry.value.to_i + amount
|
|
197
|
+
entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
|
|
198
|
+
write_entry(name, entry)
|
|
197
199
|
num
|
|
198
200
|
end
|
|
199
201
|
end
|
|
@@ -84,7 +84,6 @@ class Class
|
|
|
84
84
|
# class_attribute :settings, default: {}
|
|
85
85
|
def class_attribute(*attrs, instance_accessor: true,
|
|
86
86
|
instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil)
|
|
87
|
-
|
|
88
87
|
class_methods, methods = [], []
|
|
89
88
|
attrs.each do |name|
|
|
90
89
|
unless name.is_a?(Symbol) || name.is_a?(String)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "active_support/concern"
|
|
4
4
|
|
|
5
5
|
class Module
|
|
6
|
-
#
|
|
6
|
+
# == Bite-sized separation of concerns
|
|
7
7
|
#
|
|
8
8
|
# We often find ourselves with a medium-sized chunk of behavior that we'd
|
|
9
9
|
# like to extract, but only mix in to a single class.
|
|
@@ -18,9 +18,9 @@ class Module
|
|
|
18
18
|
# with a comment, as a least-bad alternative. Using modules in separate files
|
|
19
19
|
# means tedious sifting to get a big-picture view.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
21
|
+
# == Dissatisfying ways to separate small concerns
|
|
22
22
|
#
|
|
23
|
-
#
|
|
23
|
+
# === Using comments:
|
|
24
24
|
#
|
|
25
25
|
# class Todo < ApplicationRecord
|
|
26
26
|
# # Other todo implementation
|
|
@@ -37,7 +37,7 @@ class Module
|
|
|
37
37
|
# end
|
|
38
38
|
# end
|
|
39
39
|
#
|
|
40
|
-
#
|
|
40
|
+
# === With an inline module:
|
|
41
41
|
#
|
|
42
42
|
# Noisy syntax.
|
|
43
43
|
#
|
|
@@ -61,7 +61,7 @@ class Module
|
|
|
61
61
|
# include EventTracking
|
|
62
62
|
# end
|
|
63
63
|
#
|
|
64
|
-
#
|
|
64
|
+
# === Mix-in noise exiled to its own file:
|
|
65
65
|
#
|
|
66
66
|
# Once our chunk of behavior starts pushing the scroll-to-understand-it
|
|
67
67
|
# boundary, we give in and move it to a separate file. At this size, the
|
|
@@ -75,7 +75,7 @@ class Module
|
|
|
75
75
|
# include TodoEventTracking
|
|
76
76
|
# end
|
|
77
77
|
#
|
|
78
|
-
#
|
|
78
|
+
# == Introducing Module#concerning
|
|
79
79
|
#
|
|
80
80
|
# By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
|
|
81
81
|
# separate bite-sized concerns.
|
|
@@ -29,7 +29,7 @@ require "active_support/core_ext/date/conversions"
|
|
|
29
29
|
# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
|
|
30
30
|
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
|
|
31
31
|
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
|
|
32
|
-
# should give exactly the same results with or without
|
|
32
|
+
# should give exactly the same results with or without Active Support.
|
|
33
33
|
|
|
34
34
|
module ActiveSupport
|
|
35
35
|
module ToJsonWithActiveSupportEncoder # :nodoc:
|
|
@@ -225,9 +225,11 @@ class Pathname # :nodoc:
|
|
|
225
225
|
end
|
|
226
226
|
end
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
unless IPAddr.method_defined?(:as_json, false)
|
|
229
|
+
class IPAddr # :nodoc:
|
|
230
|
+
def as_json(options = nil)
|
|
231
|
+
to_s
|
|
232
|
+
end
|
|
231
233
|
end
|
|
232
234
|
end
|
|
233
235
|
|
|
@@ -68,7 +68,7 @@ class Object
|
|
|
68
68
|
# You can access these methods using the class name instead:
|
|
69
69
|
#
|
|
70
70
|
# class Phone < ActiveRecord::Base
|
|
71
|
-
# enum phone_number_type
|
|
71
|
+
# enum :phone_number_type, { home: 0, office: 1, mobile: 2 }
|
|
72
72
|
#
|
|
73
73
|
# with_options presence: true do
|
|
74
74
|
# validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
|
|
@@ -5,6 +5,32 @@ class Range
|
|
|
5
5
|
# (1..5).overlaps?(4..6) # => true
|
|
6
6
|
# (1..5).overlaps?(7..9) # => false
|
|
7
7
|
def overlaps?(other)
|
|
8
|
-
|
|
8
|
+
raise TypeError unless other.is_a? Range
|
|
9
|
+
|
|
10
|
+
self_begin = self.begin
|
|
11
|
+
other_end = other.end
|
|
12
|
+
other_excl = other.exclude_end?
|
|
13
|
+
|
|
14
|
+
return false if _empty_range?(self_begin, other_end, other_excl)
|
|
15
|
+
|
|
16
|
+
other_begin = other.begin
|
|
17
|
+
self_end = self.end
|
|
18
|
+
self_excl = self.exclude_end?
|
|
19
|
+
|
|
20
|
+
return false if _empty_range?(other_begin, self_end, self_excl)
|
|
21
|
+
return true if self_begin == other_begin
|
|
22
|
+
|
|
23
|
+
return false if _empty_range?(self_begin, self_end, self_excl)
|
|
24
|
+
return false if _empty_range?(other_begin, other_end, other_excl)
|
|
25
|
+
|
|
26
|
+
true
|
|
9
27
|
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
def _empty_range?(b, e, excl)
|
|
31
|
+
return false if b.nil? || e.nil?
|
|
32
|
+
|
|
33
|
+
comp = b <=> e
|
|
34
|
+
comp.nil? || comp > 0 || (comp == 0 && excl)
|
|
35
|
+
end
|
|
10
36
|
end
|
|
@@ -24,7 +24,7 @@ class String
|
|
|
24
24
|
#
|
|
25
25
|
# The second argument, +indent_string+, specifies which indent string to
|
|
26
26
|
# use. The default is +nil+, which tells the method to make a guess by
|
|
27
|
-
# peeking at the first indented line, and
|
|
27
|
+
# peeking at the first indented line, and fall back to a space if there is
|
|
28
28
|
# none.
|
|
29
29
|
#
|
|
30
30
|
# " foo".indent(2) # => " foo"
|
|
@@ -53,7 +53,7 @@ module ActiveSupport
|
|
|
53
53
|
# [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
|
|
54
54
|
# [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
|
|
55
55
|
# [+log+] Log all deprecation warnings to +Rails.logger+.
|
|
56
|
-
# [+notify+] Use
|
|
56
|
+
# [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
|
|
57
57
|
# [+silence+] Do nothing. On Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
|
|
58
58
|
#
|
|
59
59
|
# Setting behaviors only affects deprecations that happen after boot time.
|
|
@@ -80,7 +80,7 @@ module ActiveSupport
|
|
|
80
80
|
# [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
|
|
81
81
|
# [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
|
|
82
82
|
# [+log+] Log all deprecation warnings to +Rails.logger+.
|
|
83
|
-
# [+notify+] Use
|
|
83
|
+
# [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
|
|
84
84
|
# [+silence+] Do nothing.
|
|
85
85
|
#
|
|
86
86
|
# Setting behaviors only affects deprecations that happen after boot time.
|
|
@@ -128,7 +128,9 @@ module ActiveSupport
|
|
|
128
128
|
return _extract_callstack(callstack) if callstack.first.is_a? String
|
|
129
129
|
|
|
130
130
|
offending_line = callstack.find { |frame|
|
|
131
|
-
|
|
131
|
+
# Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
|
|
132
|
+
path = frame.absolute_path || frame.path
|
|
133
|
+
path && !ignored_callstack?(path)
|
|
132
134
|
} || callstack.first
|
|
133
135
|
|
|
134
136
|
[offending_line.path, offending_line.lineno, offending_line.label]
|
|
@@ -136,7 +138,7 @@ module ActiveSupport
|
|
|
136
138
|
|
|
137
139
|
def _extract_callstack(callstack)
|
|
138
140
|
warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
|
|
139
|
-
offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
|
|
141
|
+
offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
|
|
140
142
|
|
|
141
143
|
if offending_line
|
|
142
144
|
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
|
|
@@ -147,10 +149,11 @@ module ActiveSupport
|
|
|
147
149
|
end
|
|
148
150
|
end
|
|
149
151
|
|
|
150
|
-
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
|
|
152
|
+
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
|
|
153
|
+
LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
|
|
151
154
|
|
|
152
|
-
def ignored_callstack(path)
|
|
153
|
-
path.start_with?(RAILS_GEM_ROOT
|
|
155
|
+
def ignored_callstack?(path)
|
|
156
|
+
path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
|
|
154
157
|
end
|
|
155
158
|
end
|
|
156
159
|
end
|
|
@@ -161,7 +161,7 @@ module ActiveSupport
|
|
|
161
161
|
# upcase_first('w') # => "W"
|
|
162
162
|
# upcase_first('') # => ""
|
|
163
163
|
def upcase_first(string)
|
|
164
|
-
string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
|
|
164
|
+
string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
# Capitalizes all the words and replaces some characters in the string to
|
|
@@ -56,7 +56,7 @@ module ActiveSupport
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
class Event
|
|
59
|
-
attr_reader :name, :
|
|
59
|
+
attr_reader :name, :transaction_id, :children
|
|
60
60
|
attr_accessor :payload
|
|
61
61
|
|
|
62
62
|
def initialize(name, start, ending, transaction_id, payload)
|
|
@@ -72,7 +72,15 @@ module ActiveSupport
|
|
|
72
72
|
@allocation_count_finish = 0
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
def
|
|
75
|
+
def time
|
|
76
|
+
@time / 1000.0 if @time
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def end
|
|
80
|
+
@end / 1000.0 if @end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def record # :nodoc:
|
|
76
84
|
start!
|
|
77
85
|
begin
|
|
78
86
|
yield payload if block_given?
|
|
@@ -99,20 +107,20 @@ module ActiveSupport
|
|
|
99
107
|
@allocation_count_finish = now_allocations
|
|
100
108
|
end
|
|
101
109
|
|
|
102
|
-
# Returns the CPU time (in milliseconds) passed
|
|
103
|
-
#
|
|
110
|
+
# Returns the CPU time (in milliseconds) passed between the call to
|
|
111
|
+
# #start! and the call to #finish!.
|
|
104
112
|
def cpu_time
|
|
105
113
|
@cpu_time_finish - @cpu_time_start
|
|
106
114
|
end
|
|
107
115
|
|
|
108
|
-
# Returns the idle time time (in milliseconds) passed
|
|
109
|
-
#
|
|
116
|
+
# Returns the idle time time (in milliseconds) passed between the call to
|
|
117
|
+
# #start! and the call to #finish!.
|
|
110
118
|
def idle_time
|
|
111
119
|
duration - cpu_time
|
|
112
120
|
end
|
|
113
121
|
|
|
114
|
-
# Returns the number of allocations made
|
|
115
|
-
# the call to
|
|
122
|
+
# Returns the number of allocations made between the call to #start! and
|
|
123
|
+
# the call to #finish!.
|
|
116
124
|
def allocations
|
|
117
125
|
@allocation_count_finish - @allocation_count_start
|
|
118
126
|
end
|
|
@@ -130,7 +138,7 @@ module ActiveSupport
|
|
|
130
138
|
#
|
|
131
139
|
# @event.duration # => 1000.138
|
|
132
140
|
def duration
|
|
133
|
-
|
|
141
|
+
@end - @time
|
|
134
142
|
end
|
|
135
143
|
|
|
136
144
|
def <<(event)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "bigdecimal"
|
|
4
|
+
require "bigdecimal/util"
|
|
3
5
|
require "active_support/core_ext/big_decimal/conversions"
|
|
4
6
|
require "active_support/core_ext/object/blank"
|
|
5
7
|
require "active_support/core_ext/hash/keys"
|
|
@@ -128,7 +130,7 @@ module ActiveSupport
|
|
|
128
130
|
def execute
|
|
129
131
|
if !number
|
|
130
132
|
nil
|
|
131
|
-
elsif validate_float? && !
|
|
133
|
+
elsif validate_float? && !valid_bigdecimal
|
|
132
134
|
number
|
|
133
135
|
else
|
|
134
136
|
convert
|
|
@@ -173,8 +175,15 @@ module ActiveSupport
|
|
|
173
175
|
key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
|
|
174
176
|
end
|
|
175
177
|
|
|
176
|
-
def
|
|
177
|
-
|
|
178
|
+
def valid_bigdecimal
|
|
179
|
+
case number
|
|
180
|
+
when Float, Rational
|
|
181
|
+
number.to_d(0)
|
|
182
|
+
when String
|
|
183
|
+
BigDecimal(number, exception: false)
|
|
184
|
+
else
|
|
185
|
+
number.to_d rescue nil
|
|
186
|
+
end
|
|
178
187
|
end
|
|
179
188
|
end
|
|
180
189
|
end
|
|
@@ -10,13 +10,13 @@ module ActiveSupport
|
|
|
10
10
|
def convert
|
|
11
11
|
format = options[:format]
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
if
|
|
15
|
-
if
|
|
16
|
-
|
|
17
|
-
format = options[:negative_format] if (
|
|
13
|
+
number_d = valid_bigdecimal
|
|
14
|
+
if number_d
|
|
15
|
+
if number_d.negative?
|
|
16
|
+
number_d = number_d.abs
|
|
17
|
+
format = options[:negative_format] if (number_d * 10**options[:precision]) >= 0.5
|
|
18
18
|
end
|
|
19
|
-
number_s = NumberToRoundedConverter.convert(
|
|
19
|
+
number_s = NumberToRoundedConverter.convert(number_d, options)
|
|
20
20
|
else
|
|
21
21
|
number_s = number.to_s.strip
|
|
22
22
|
format = options[:negative_format] if number_s.sub!(/^-/, "")
|
|
@@ -43,13 +43,13 @@ module ActiveSupport
|
|
|
43
43
|
|
|
44
44
|
def exponent
|
|
45
45
|
max = STORAGE_UNITS.size - 1
|
|
46
|
-
exp = (Math.log(number) / Math.log(base)).to_i
|
|
46
|
+
exp = (Math.log(number.abs) / Math.log(base)).to_i
|
|
47
47
|
exp = max if exp > max # avoid overflow for the highest unit
|
|
48
48
|
exp
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def smaller_than_base?
|
|
52
|
-
number.to_i < base
|
|
52
|
+
number.to_i.abs < base
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def base
|