sidekiq 7.3.0 → 7.3.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/Changes.md +81 -0
- data/bin/sidekiqload +21 -12
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
- data/lib/generators/sidekiq/job_generator.rb +2 -0
- data/lib/sidekiq/api.rb +62 -33
- data/lib/sidekiq/capsule.rb +5 -3
- data/lib/sidekiq/cli.rb +1 -1
- data/lib/sidekiq/client.rb +21 -1
- data/lib/sidekiq/component.rb +22 -0
- data/lib/sidekiq/config.rb +22 -2
- data/lib/sidekiq/deploy.rb +2 -0
- data/lib/sidekiq/embedded.rb +2 -0
- data/lib/sidekiq/iterable_job.rb +2 -0
- data/lib/sidekiq/job/interrupt_handler.rb +2 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +3 -3
- data/lib/sidekiq/job/iterable.rb +69 -6
- data/lib/sidekiq/job_logger.rb +11 -23
- data/lib/sidekiq/job_util.rb +2 -0
- data/lib/sidekiq/launcher.rb +1 -1
- data/lib/sidekiq/metrics/query.rb +2 -0
- data/lib/sidekiq/metrics/shared.rb +15 -4
- data/lib/sidekiq/metrics/tracking.rb +13 -5
- data/lib/sidekiq/middleware/current_attributes.rb +19 -2
- data/lib/sidekiq/middleware/modules.rb +2 -0
- data/lib/sidekiq/monitor.rb +2 -1
- data/lib/sidekiq/paginator.rb +6 -0
- data/lib/sidekiq/processor.rb +10 -10
- data/lib/sidekiq/rails.rb +12 -0
- data/lib/sidekiq/redis_connection.rb +8 -1
- data/lib/sidekiq/ring_buffer.rb +2 -0
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/testing.rb +5 -5
- data/lib/sidekiq/version.rb +5 -1
- data/lib/sidekiq/web/action.rb +20 -4
- data/lib/sidekiq/web/application.rb +36 -79
- data/lib/sidekiq/web/helpers.rb +11 -10
- data/lib/sidekiq/web/router.rb +5 -2
- data/lib/sidekiq/web.rb +8 -1
- data/lib/sidekiq.rb +4 -3
- data/sidekiq.gemspec +1 -1
- data/web/assets/javascripts/dashboard-charts.js +2 -0
- data/web/assets/javascripts/dashboard.js +6 -0
- data/web/assets/stylesheets/application.css +9 -8
- data/web/locales/en.yml +3 -1
- data/web/locales/fr.yml +0 -1
- data/web/locales/gd.yml +0 -1
- data/web/locales/it.yml +32 -1
- data/web/locales/ja.yml +0 -1
- data/web/locales/pt-br.yml +1 -2
- data/web/locales/tr.yml +1 -2
- data/web/locales/uk.yml +24 -1
- data/web/locales/zh-cn.yml +0 -1
- data/web/locales/zh-tw.yml +0 -1
- data/web/views/_footer.erb +1 -2
- data/web/views/dashboard.erb +4 -1
- data/web/views/filtering.erb +1 -2
- data/web/views/metrics.erb +3 -4
- data/web/views/morgue.erb +2 -2
- data/web/views/queue.erb +1 -1
- metadata +10 -12
data/lib/sidekiq/job/iterable.rb
CHANGED
@@ -30,6 +30,40 @@ module Sidekiq
|
|
30
30
|
@_cursor = nil
|
31
31
|
@_start_time = nil
|
32
32
|
@_runtime = 0
|
33
|
+
@_args = nil
|
34
|
+
@_cancelled = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def arguments
|
38
|
+
@_args
|
39
|
+
end
|
40
|
+
|
41
|
+
# Three days is the longest period you generally need to wait for a retry to
|
42
|
+
# execute when using the default retry scheme. We don't want to "forget" the job
|
43
|
+
# is cancelled before it has a chance to execute and cancel itself.
|
44
|
+
CANCELLATION_PERIOD = (3 * 86_400).to_s
|
45
|
+
|
46
|
+
# Set a flag in Redis to mark this job as cancelled.
|
47
|
+
# Cancellation is asynchronous and is checked at the start of iteration
|
48
|
+
# and every 5 seconds thereafter as part of the recurring state flush.
|
49
|
+
def cancel!
|
50
|
+
return @_cancelled if cancelled?
|
51
|
+
|
52
|
+
key = "it-#{jid}"
|
53
|
+
_, result, _ = Sidekiq.redis do |c|
|
54
|
+
c.pipelined do |p|
|
55
|
+
p.hsetnx(key, "cancelled", Time.now.to_i)
|
56
|
+
p.hget(key, "cancelled")
|
57
|
+
# TODO When Redis 7.2 is required
|
58
|
+
# p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
59
|
+
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@_cancelled = result.to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
def cancelled?
|
66
|
+
@_cancelled
|
33
67
|
end
|
34
68
|
|
35
69
|
# A hook to override that will be called when the job starts iterating.
|
@@ -91,13 +125,14 @@ module Sidekiq
|
|
91
125
|
end
|
92
126
|
|
93
127
|
# @api private
|
94
|
-
def perform(*
|
128
|
+
def perform(*args)
|
129
|
+
@_args = args.dup.freeze
|
95
130
|
fetch_previous_iteration_state
|
96
131
|
|
97
132
|
@_executions += 1
|
98
133
|
@_start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
99
134
|
|
100
|
-
enumerator = build_enumerator(*
|
135
|
+
enumerator = build_enumerator(*args, cursor: @_cursor)
|
101
136
|
unless enumerator
|
102
137
|
logger.info("'#build_enumerator' returned nil, skipping the job.")
|
103
138
|
return
|
@@ -112,7 +147,7 @@ module Sidekiq
|
|
112
147
|
end
|
113
148
|
|
114
149
|
completed = catch(:abort) do
|
115
|
-
iterate_with_enumerator(enumerator,
|
150
|
+
iterate_with_enumerator(enumerator, args)
|
116
151
|
end
|
117
152
|
|
118
153
|
on_stop
|
@@ -128,6 +163,10 @@ module Sidekiq
|
|
128
163
|
|
129
164
|
private
|
130
165
|
|
166
|
+
def is_cancelled?
|
167
|
+
@_cancelled = Sidekiq.redis { |c| c.hget("it-#{jid}", "cancelled") }
|
168
|
+
end
|
169
|
+
|
131
170
|
def fetch_previous_iteration_state
|
132
171
|
state = Sidekiq.redis { |conn| conn.hgetall(iteration_key) }
|
133
172
|
|
@@ -144,6 +183,12 @@ module Sidekiq
|
|
144
183
|
STATE_TTL = 30 * 24 * 60 * 60 # one month
|
145
184
|
|
146
185
|
def iterate_with_enumerator(enumerator, arguments)
|
186
|
+
if is_cancelled?
|
187
|
+
logger.info { "Job cancelled" }
|
188
|
+
return true
|
189
|
+
end
|
190
|
+
|
191
|
+
time_limit = Sidekiq.default_configuration[:timeout]
|
147
192
|
found_record = false
|
148
193
|
state_flushed_at = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
149
194
|
|
@@ -153,14 +198,21 @@ module Sidekiq
|
|
153
198
|
|
154
199
|
is_interrupted = interrupted?
|
155
200
|
if ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - state_flushed_at >= STATE_FLUSH_INTERVAL || is_interrupted
|
156
|
-
flush_state
|
201
|
+
_, _, cancelled = flush_state
|
157
202
|
state_flushed_at = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
203
|
+
if cancelled
|
204
|
+
@_cancelled = true
|
205
|
+
logger.info { "Job cancelled" }
|
206
|
+
return true
|
207
|
+
end
|
158
208
|
end
|
159
209
|
|
160
210
|
return false if is_interrupted
|
161
211
|
|
162
|
-
|
163
|
-
|
212
|
+
verify_iteration_time(time_limit, object) do
|
213
|
+
around_iteration do
|
214
|
+
each_iteration(object, *arguments)
|
215
|
+
end
|
164
216
|
end
|
165
217
|
end
|
166
218
|
|
@@ -170,6 +222,16 @@ module Sidekiq
|
|
170
222
|
@_runtime += (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @_start_time)
|
171
223
|
end
|
172
224
|
|
225
|
+
def verify_iteration_time(time_limit, object)
|
226
|
+
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
227
|
+
yield
|
228
|
+
finish = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
229
|
+
total = finish - start
|
230
|
+
if total > time_limit
|
231
|
+
logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d) when processing `%s`. This can lead to job processing problems during deploys" % [total, time_limit, object] }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
173
235
|
def reenqueue_iteration_job
|
174
236
|
flush_state
|
175
237
|
logger.debug { "Interrupting job (cursor=#{@_cursor.inspect})" }
|
@@ -204,6 +266,7 @@ module Sidekiq
|
|
204
266
|
conn.multi do |pipe|
|
205
267
|
pipe.hset(key, state)
|
206
268
|
pipe.expire(key, STATE_TTL)
|
269
|
+
pipe.hget(key, "cancelled")
|
207
270
|
end
|
208
271
|
end
|
209
272
|
end
|
data/lib/sidekiq/job_logger.rb
CHANGED
@@ -2,36 +2,24 @@
|
|
2
2
|
|
3
3
|
module Sidekiq
|
4
4
|
class JobLogger
|
5
|
-
include Sidekiq::Component
|
6
|
-
|
7
5
|
def initialize(config)
|
8
6
|
@config = config
|
9
|
-
@logger = logger
|
10
|
-
|
11
|
-
|
12
|
-
# If true we won't do any job logging out of the box.
|
13
|
-
# The user is responsible for any logging.
|
14
|
-
def skip_default_logging?
|
15
|
-
config[:skip_default_job_logging]
|
7
|
+
@logger = @config.logger
|
8
|
+
@skip = !!@config[:skip_default_job_logging]
|
16
9
|
end
|
17
10
|
|
18
11
|
def call(item, queue)
|
19
|
-
|
12
|
+
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
13
|
+
@logger.info { "start" } unless @skip
|
20
14
|
|
21
|
-
|
22
|
-
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
23
|
-
@logger.info("start")
|
15
|
+
yield
|
24
16
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@logger.info("fail")
|
32
|
-
|
33
|
-
raise
|
34
|
-
end
|
17
|
+
Sidekiq::Context.add(:elapsed, elapsed(start))
|
18
|
+
@logger.info { "done" } unless @skip
|
19
|
+
rescue Exception
|
20
|
+
Sidekiq::Context.add(:elapsed, elapsed(start))
|
21
|
+
@logger.info { "fail" } unless @skip
|
22
|
+
raise
|
35
23
|
end
|
36
24
|
|
37
25
|
def prepare(job_hash, &block)
|
data/lib/sidekiq/job_util.rb
CHANGED
data/lib/sidekiq/launcher.rb
CHANGED
@@ -36,8 +36,8 @@ module Sidekiq
|
|
36
36
|
# has a heartbeat thread, caller can use `async_beat: false`
|
37
37
|
# and instead have thread call Launcher#heartbeat every N seconds.
|
38
38
|
def run(async_beat: true)
|
39
|
-
Sidekiq.freeze!
|
40
39
|
logger.debug { @config.merge!({}) }
|
40
|
+
Sidekiq.freeze!
|
41
41
|
@thread = safe_thread("heartbeat", &method(:start_heartbeat)) if async_beat
|
42
42
|
@poller.start
|
43
43
|
@managers.each(&:start)
|
@@ -1,10 +1,21 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sidekiq
|
4
4
|
module Metrics
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
class Counter
|
6
|
+
def initialize
|
7
|
+
@value = 0
|
8
|
+
@lock = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def increment
|
12
|
+
@lock.synchronize { @value += 1 }
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
@lock.synchronize { @value }
|
17
|
+
end
|
18
|
+
end
|
8
19
|
|
9
20
|
# Implements space-efficient but statistically useful histogram storage.
|
10
21
|
# A precise time histogram stores every time. Instead we break times into a set of
|
@@ -31,11 +31,11 @@ module Sidekiq
|
|
31
31
|
# We don't track time for failed jobs as they can have very unpredictable
|
32
32
|
# execution times. more important to know average time for successful jobs so we
|
33
33
|
# can better recognize when a perf regression is introduced.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
track_time(klass, time_ms)
|
35
|
+
rescue JobRetry::Skip
|
36
|
+
# This is raised when iterable job is interrupted.
|
37
|
+
track_time(klass, time_ms)
|
38
|
+
raise
|
39
39
|
rescue Exception
|
40
40
|
@lock.synchronize {
|
41
41
|
@jobs["#{klass}|f"] += 1
|
@@ -100,6 +100,14 @@ module Sidekiq
|
|
100
100
|
|
101
101
|
private
|
102
102
|
|
103
|
+
def track_time(klass, time_ms)
|
104
|
+
@lock.synchronize {
|
105
|
+
@grams[klass].record_time(time_ms)
|
106
|
+
@jobs["#{klass}|ms"] += time_ms
|
107
|
+
@totals["ms"] += time_ms
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
103
111
|
def reset
|
104
112
|
@lock.synchronize {
|
105
113
|
array = [@totals, @jobs, @grams]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/current_attributes"
|
2
4
|
|
3
5
|
module Sidekiq
|
@@ -31,11 +33,26 @@ module Sidekiq
|
|
31
33
|
attrs = strklass.constantize.attributes
|
32
34
|
# Retries can push the job N times, we don't
|
33
35
|
# want retries to reset cattr. #5692, #5090
|
34
|
-
|
36
|
+
if attrs.any?
|
37
|
+
# Older rails has a bug that `CurrentAttributes#attributes` always returns
|
38
|
+
# the same hash instance. We need to dup it to avoid being accidentally mutated.
|
39
|
+
job[key] = if returns_same_object?
|
40
|
+
attrs.dup
|
41
|
+
else
|
42
|
+
attrs
|
43
|
+
end
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
37
47
|
yield
|
38
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def returns_same_object?
|
53
|
+
ActiveSupport::VERSION::MAJOR < 8 ||
|
54
|
+
(ActiveSupport::VERSION::MAJOR == 8 && ActiveSupport::VERSION::MINOR == 0)
|
55
|
+
end
|
39
56
|
end
|
40
57
|
|
41
58
|
class Load
|
@@ -86,7 +103,7 @@ module Sidekiq
|
|
86
103
|
cattrs = build_cattrs_hash(klass_or_array)
|
87
104
|
|
88
105
|
config.client_middleware.add Save, cattrs
|
89
|
-
config.server_middleware.
|
106
|
+
config.server_middleware.prepend Load, cattrs
|
90
107
|
end
|
91
108
|
|
92
109
|
private
|
data/lib/sidekiq/monitor.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "fileutils"
|
4
5
|
require "sidekiq/api"
|
@@ -98,7 +99,7 @@ class Sidekiq::Monitor
|
|
98
99
|
pad = opts[:pad] || 0
|
99
100
|
max_length = opts[:max_length] || (80 - pad)
|
100
101
|
out = []
|
101
|
-
line = ""
|
102
|
+
line = +""
|
102
103
|
values.each do |value|
|
103
104
|
if (line.length + value.length) > max_length
|
104
105
|
out << line
|
data/lib/sidekiq/paginator.rb
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
module Sidekiq
|
4
4
|
module Paginator
|
5
|
+
TYPE_CACHE = {
|
6
|
+
"dead" => "zset",
|
7
|
+
"retry" => "zset",
|
8
|
+
"schedule" => "zset"
|
9
|
+
}
|
10
|
+
|
5
11
|
def page(key, pageidx = 1, page_size = 25, opts = nil)
|
6
12
|
current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
|
7
13
|
pageidx = current_page - 1
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -138,11 +138,11 @@ module Sidekiq
|
|
138
138
|
# Effectively this block denotes a "unit of work" to Rails.
|
139
139
|
@reloader.call do
|
140
140
|
klass = Object.const_get(job_hash["class"])
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
@retrier.local(
|
145
|
-
yield
|
141
|
+
instance = klass.new
|
142
|
+
instance.jid = job_hash["jid"]
|
143
|
+
instance._context = self
|
144
|
+
@retrier.local(instance, jobstr, queue) do
|
145
|
+
yield instance
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
@@ -180,9 +180,9 @@ module Sidekiq
|
|
180
180
|
ack = false
|
181
181
|
Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
|
182
182
|
Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
|
183
|
-
dispatch(job_hash, queue, jobstr) do |
|
184
|
-
config.server_middleware.invoke(
|
185
|
-
execute_job(
|
183
|
+
dispatch(job_hash, queue, jobstr) do |instance|
|
184
|
+
config.server_middleware.invoke(instance, job_hash, queue) do
|
185
|
+
execute_job(instance, job_hash["args"])
|
186
186
|
end
|
187
187
|
end
|
188
188
|
ack = true
|
@@ -216,8 +216,8 @@ module Sidekiq
|
|
216
216
|
end
|
217
217
|
end
|
218
218
|
|
219
|
-
def execute_job(
|
220
|
-
|
219
|
+
def execute_job(instance, cloned_args)
|
220
|
+
instance.perform(*cloned_args)
|
221
221
|
end
|
222
222
|
|
223
223
|
# Ruby doesn't provide atomic counters out of the box so we'll
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -4,6 +4,17 @@ require "sidekiq/job"
|
|
4
4
|
require "rails"
|
5
5
|
|
6
6
|
module Sidekiq
|
7
|
+
module ActiveJob
|
8
|
+
# @api private
|
9
|
+
class Wrapper
|
10
|
+
include Sidekiq::Job
|
11
|
+
|
12
|
+
def perform(job_data)
|
13
|
+
::ActiveJob::Base.execute(job_data.merge("provider_job_id" => jid))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
7
18
|
class Rails < ::Rails::Engine
|
8
19
|
class Reloader
|
9
20
|
def initialize(app = ::Rails.application)
|
@@ -39,6 +50,7 @@ module Sidekiq
|
|
39
50
|
# end
|
40
51
|
initializer "sidekiq.active_job_integration" do
|
41
52
|
ActiveSupport.on_load(:active_job) do
|
53
|
+
require_relative "../active_job/queue_adapters/sidekiq_adapter"
|
42
54
|
include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
|
43
55
|
end
|
44
56
|
end
|
@@ -66,7 +66,14 @@ module Sidekiq
|
|
66
66
|
scrubbed_options[:password] = redacted if scrubbed_options[:password]
|
67
67
|
scrubbed_options[:sentinel_password] = redacted if scrubbed_options[:sentinel_password]
|
68
68
|
scrubbed_options[:sentinels]&.each do |sentinel|
|
69
|
-
|
69
|
+
if sentinel.is_a?(String)
|
70
|
+
if (uri = URI(sentinel)) && uri.password
|
71
|
+
uri.password = redacted
|
72
|
+
sentinel.replace(uri.to_s)
|
73
|
+
end
|
74
|
+
elsif sentinel[:password]
|
75
|
+
sentinel[:password] = redacted
|
76
|
+
end
|
70
77
|
end
|
71
78
|
scrubbed_options
|
72
79
|
end
|
data/lib/sidekiq/ring_buffer.rb
CHANGED
data/lib/sidekiq/systemd.rb
CHANGED
data/lib/sidekiq/testing.rb
CHANGED
@@ -283,11 +283,11 @@ module Sidekiq
|
|
283
283
|
end
|
284
284
|
|
285
285
|
def process_job(job)
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
Sidekiq::Testing.server_middleware.invoke(
|
290
|
-
execute_job(
|
286
|
+
instance = new
|
287
|
+
instance.jid = job["jid"]
|
288
|
+
instance.bid = job["bid"] if instance.respond_to?(:bid=)
|
289
|
+
Sidekiq::Testing.server_middleware.invoke(instance, job, job["queue"]) do
|
290
|
+
execute_job(instance, job["args"])
|
291
291
|
end
|
292
292
|
end
|
293
293
|
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
@@ -27,6 +27,7 @@ module Sidekiq
|
|
27
27
|
redirect current_location
|
28
28
|
end
|
29
29
|
|
30
|
+
# deprecated, will warn in 8.0
|
30
31
|
def params
|
31
32
|
indifferent_hash = Hash.new { |hash, key| hash[key.to_s] if Symbol === key }
|
32
33
|
|
@@ -36,8 +37,19 @@ module Sidekiq
|
|
36
37
|
indifferent_hash
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
# Use like `url_params("page")` within your action blocks
|
41
|
+
def url_params(key)
|
42
|
+
request.params[key]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Use like `route_params(:name)` within your action blocks
|
46
|
+
# key is required in 8.0, nil is only used for backwards compatibility
|
47
|
+
def route_params(key = nil)
|
48
|
+
if key
|
49
|
+
env[WebRouter::ROUTE_PARAMS][key]
|
50
|
+
else
|
51
|
+
env[WebRouter::ROUTE_PARAMS]
|
52
|
+
end
|
41
53
|
end
|
42
54
|
|
43
55
|
def session
|
@@ -48,8 +60,12 @@ module Sidekiq
|
|
48
60
|
if content.is_a? Symbol
|
49
61
|
unless respond_to?(:"_erb_#{content}")
|
50
62
|
views = options[:views] || Web.settings.views
|
51
|
-
|
52
|
-
|
63
|
+
filename = "#{views}/#{content}.erb"
|
64
|
+
src = ERB.new(File.read(filename)).src
|
65
|
+
|
66
|
+
# Need to use lineno less by 1 because erb generates a
|
67
|
+
# comment before the source code.
|
68
|
+
WebAction.class_eval <<-RUBY, filename, -1 # standard:disable Style/EvalWithLocation
|
53
69
|
def _erb_#{content}
|
54
70
|
#{src}
|
55
71
|
end
|