sidekiq 5.2.9 → 6.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +232 -0
  3. data/README.md +18 -34
  4. data/bin/sidekiq +26 -2
  5. data/bin/sidekiqload +32 -24
  6. data/bin/sidekiqmon +8 -0
  7. data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
  8. data/lib/generators/sidekiq/worker_generator.rb +21 -13
  9. data/lib/sidekiq/api.rb +257 -219
  10. data/lib/sidekiq/cli.rb +144 -180
  11. data/lib/sidekiq/client.rb +64 -48
  12. data/lib/sidekiq/delay.rb +5 -6
  13. data/lib/sidekiq/exception_handler.rb +10 -12
  14. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  15. data/lib/sidekiq/extensions/active_record.rb +13 -10
  16. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  17. data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
  18. data/lib/sidekiq/fetch.rb +38 -31
  19. data/lib/sidekiq/job_logger.rb +45 -7
  20. data/lib/sidekiq/job_retry.rb +62 -61
  21. data/lib/sidekiq/launcher.rb +142 -52
  22. data/lib/sidekiq/logger.rb +166 -0
  23. data/lib/sidekiq/manager.rb +11 -13
  24. data/lib/sidekiq/middleware/chain.rb +15 -5
  25. data/lib/sidekiq/middleware/i18n.rb +5 -7
  26. data/lib/sidekiq/monitor.rb +133 -0
  27. data/lib/sidekiq/paginator.rb +18 -14
  28. data/lib/sidekiq/processor.rb +71 -70
  29. data/lib/sidekiq/rails.rb +29 -37
  30. data/lib/sidekiq/redis_connection.rb +50 -48
  31. data/lib/sidekiq/scheduled.rb +28 -29
  32. data/lib/sidekiq/sd_notify.rb +149 -0
  33. data/lib/sidekiq/systemd.rb +24 -0
  34. data/lib/sidekiq/testing/inline.rb +2 -1
  35. data/lib/sidekiq/testing.rb +35 -24
  36. data/lib/sidekiq/util.rb +45 -16
  37. data/lib/sidekiq/version.rb +2 -1
  38. data/lib/sidekiq/web/action.rb +15 -11
  39. data/lib/sidekiq/web/application.rb +84 -74
  40. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  41. data/lib/sidekiq/web/helpers.rb +108 -79
  42. data/lib/sidekiq/web/router.rb +23 -19
  43. data/lib/sidekiq/web.rb +60 -105
  44. data/lib/sidekiq/worker.rb +126 -102
  45. data/lib/sidekiq.rb +69 -44
  46. data/sidekiq.gemspec +23 -16
  47. data/web/assets/images/apple-touch-icon.png +0 -0
  48. data/web/assets/javascripts/application.js +25 -27
  49. data/web/assets/javascripts/dashboard.js +4 -23
  50. data/web/assets/stylesheets/application-dark.css +160 -0
  51. data/web/assets/stylesheets/application.css +33 -8
  52. data/web/locales/de.yml +14 -2
  53. data/web/locales/en.yml +2 -0
  54. data/web/locales/fr.yml +3 -3
  55. data/web/locales/ja.yml +4 -1
  56. data/web/locales/lt.yml +83 -0
  57. data/web/locales/pl.yml +4 -4
  58. data/web/locales/ru.yml +4 -0
  59. data/web/locales/vi.yml +83 -0
  60. data/web/views/_job_info.erb +2 -1
  61. data/web/views/busy.erb +51 -17
  62. data/web/views/dead.erb +2 -2
  63. data/web/views/layout.erb +2 -0
  64. data/web/views/morgue.erb +5 -2
  65. data/web/views/queue.erb +11 -2
  66. data/web/views/queues.erb +9 -1
  67. data/web/views/retries.erb +5 -2
  68. data/web/views/retry.erb +2 -2
  69. data/web/views/scheduled.erb +5 -2
  70. metadata +27 -60
  71. data/.circleci/config.yml +0 -61
  72. data/.github/contributing.md +0 -32
  73. data/.github/issue_template.md +0 -11
  74. data/.gitignore +0 -15
  75. data/.travis.yml +0 -11
  76. data/3.0-Upgrade.md +0 -70
  77. data/4.0-Upgrade.md +0 -53
  78. data/5.0-Upgrade.md +0 -56
  79. data/COMM-LICENSE +0 -97
  80. data/Ent-Changes.md +0 -238
  81. data/Gemfile +0 -23
  82. data/Pro-2.0-Upgrade.md +0 -138
  83. data/Pro-3.0-Upgrade.md +0 -44
  84. data/Pro-4.0-Upgrade.md +0 -35
  85. data/Pro-Changes.md +0 -759
  86. data/Rakefile +0 -9
  87. data/bin/sidekiqctl +0 -20
  88. data/code_of_conduct.md +0 -50
  89. data/lib/sidekiq/core_ext.rb +0 -1
  90. data/lib/sidekiq/ctl.rb +0 -221
  91. data/lib/sidekiq/logging.rb +0 -122
  92. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
data/lib/sidekiq/web.rb CHANGED
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require 'erb'
3
2
 
4
- require 'sidekiq'
5
- require 'sidekiq/api'
6
- require 'sidekiq/paginator'
7
- require 'sidekiq/web/helpers'
3
+ require "erb"
8
4
 
9
- require 'sidekiq/web/router'
10
- require 'sidekiq/web/action'
11
- require 'sidekiq/web/application'
5
+ require "sidekiq"
6
+ require "sidekiq/api"
7
+ require "sidekiq/paginator"
8
+ require "sidekiq/web/helpers"
12
9
 
13
- require 'rack/protection'
10
+ require "sidekiq/web/router"
11
+ require "sidekiq/web/action"
12
+ require "sidekiq/web/application"
13
+ require "sidekiq/web/csrf_protection"
14
14
 
15
- require 'rack/builder'
16
- require 'rack/file'
17
- require 'rack/session/cookie'
15
+ require "rack/content_length"
16
+ require "rack/builder"
17
+ require "rack/static"
18
18
 
19
19
  module Sidekiq
20
20
  class Web
@@ -25,12 +25,12 @@ module Sidekiq
25
25
  ASSETS = "#{ROOT}/assets"
26
26
 
27
27
  DEFAULT_TABS = {
28
- "Dashboard" => '',
29
- "Busy" => 'busy',
30
- "Queues" => 'queues',
31
- "Retries" => 'retries',
32
- "Scheduled" => 'scheduled',
33
- "Dead" => 'morgue',
28
+ "Dashboard" => "",
29
+ "Busy" => "busy",
30
+ "Queues" => "queues",
31
+ "Retries" => "retries",
32
+ "Scheduled" => "scheduled",
33
+ "Dead" => "morgue"
34
34
  }
35
35
 
36
36
  class << self
@@ -38,14 +38,6 @@ module Sidekiq
38
38
  self
39
39
  end
40
40
 
41
- def middlewares
42
- @middlewares ||= []
43
- end
44
-
45
- def use(*middleware_args, &block)
46
- middlewares << [middleware_args, block]
47
- end
48
-
49
41
  def default_tabs
50
42
  DEFAULT_TABS
51
43
  end
@@ -64,39 +56,52 @@ module Sidekiq
64
56
  end
65
57
 
66
58
  def enable(*opts)
67
- opts.each {|key| set(key, true) }
59
+ opts.each { |key| set(key, true) }
68
60
  end
69
61
 
70
62
  def disable(*opts)
71
- opts.each {|key| set(key, false) }
63
+ opts.each { |key| set(key, false) }
64
+ end
65
+
66
+ def middlewares
67
+ @middlewares ||= []
68
+ end
69
+
70
+ def use(*args, &block)
71
+ middlewares << [args, block]
72
72
  end
73
73
 
74
- # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
75
74
  def set(attribute, value)
76
75
  send(:"#{attribute}=", value)
77
76
  end
78
77
 
79
- attr_accessor :app_url, :session_secret, :redis_pool, :sessions
78
+ def sessions=(val)
79
+ puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
80
+ end
81
+
82
+ def session_secret=(val)
83
+ puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
84
+ end
85
+
86
+ attr_accessor :app_url, :redis_pool
80
87
  attr_writer :locales, :views
81
88
  end
82
89
 
83
90
  def self.inherited(child)
84
- child.app_url = self.app_url
85
- child.session_secret = self.session_secret
86
- child.redis_pool = self.redis_pool
87
- child.sessions = self.sessions
91
+ child.app_url = app_url
92
+ child.redis_pool = redis_pool
88
93
  end
89
94
 
90
95
  def settings
91
96
  self.class.settings
92
97
  end
93
98
 
94
- def use(*middleware_args, &block)
95
- middlewares << [middleware_args, block]
99
+ def middlewares
100
+ @middlewares ||= self.class.middlewares
96
101
  end
97
102
 
98
- def middlewares
99
- @middlewares ||= Web.middlewares.dup
103
+ def use(*args, &block)
104
+ middlewares << [args, block]
100
105
  end
101
106
 
102
107
  def call(env)
@@ -113,29 +118,19 @@ module Sidekiq
113
118
  end
114
119
 
115
120
  def enable(*opts)
116
- opts.each {|key| set(key, true) }
121
+ opts.each { |key| set(key, true) }
117
122
  end
118
123
 
119
124
  def disable(*opts)
120
- opts.each {|key| set(key, false) }
125
+ opts.each { |key| set(key, false) }
121
126
  end
122
127
 
123
128
  def set(attribute, value)
124
129
  send(:"#{attribute}=", value)
125
130
  end
126
131
 
127
- # Default values
128
- set :sessions, true
129
-
130
- attr_writer :sessions
131
-
132
- def sessions
133
- unless instance_variable_defined?("@sessions")
134
- @sessions = self.class.sessions
135
- @sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
136
- end
137
-
138
- @sessions
132
+ def sessions=(val)
133
+ puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
139
134
  end
140
135
 
141
136
  def self.register(extension)
@@ -144,50 +139,19 @@ module Sidekiq
144
139
 
145
140
  private
146
141
 
147
- def using?(middleware)
148
- middlewares.any? do |(m,_)|
149
- m.kind_of?(Array) && (m[0] == middleware || m[0].kind_of?(middleware))
150
- end
151
- end
152
-
153
- def build_sessions
154
- middlewares = self.middlewares
155
-
156
- unless using?(::Rack::Protection) || ENV['RACK_ENV'] == 'test'
157
- middlewares.unshift [[::Rack::Protection, { use: :authenticity_token }], nil]
158
- end
159
-
160
- s = sessions
161
- return unless s
162
-
163
- unless using? ::Rack::Session::Cookie
164
- unless secret = Web.session_secret
165
- require 'securerandom'
166
- secret = SecureRandom.hex(64)
167
- end
168
-
169
- options = { secret: secret }
170
- options = options.merge(s.to_hash) if s.respond_to? :to_hash
171
-
172
- middlewares.unshift [[::Rack::Session::Cookie, options], nil]
173
- end
174
- end
175
-
176
142
  def build
177
- build_sessions
178
-
179
- middlewares = self.middlewares
180
143
  klass = self.class
144
+ m = middlewares
181
145
 
182
146
  ::Rack::Builder.new do
183
- %w(stylesheets javascripts images).each do |asset_dir|
184
- map "/#{asset_dir}" do
185
- run ::Rack::File.new("#{ASSETS}/#{asset_dir}", { 'Cache-Control' => 'public, max-age=86400' })
186
- end
187
- end
188
-
189
- middlewares.each {|middleware, block| use(*middleware, &block) }
190
-
147
+ use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
148
+ root: ASSETS,
149
+ cascade: true,
150
+ header_rules: [
151
+ [:all, {"Cache-Control" => "public, max-age=86400"}]
152
+ ]
153
+ m.each { |middleware, block| use(*middleware, &block) }
154
+ use Sidekiq::Web::CsrfProtection unless $TESTING
191
155
  run WebApplication.new(klass)
192
156
  end
193
157
  end
@@ -196,18 +160,9 @@ module Sidekiq
196
160
  Sidekiq::WebApplication.helpers WebHelpers
197
161
  Sidekiq::WebApplication.helpers Sidekiq::Paginator
198
162
 
199
- Sidekiq::WebAction.class_eval "def _render\n#{ERB.new(File.read(Web::LAYOUT)).src}\nend"
200
- end
201
-
202
- if defined?(::ActionDispatch::Request::Session) &&
203
- !::ActionDispatch::Request::Session.method_defined?(:each)
204
- # mperham/sidekiq#2460
205
- # Rack apps can't reuse the Rails session store without
206
- # this monkeypatch, fixed in Rails 5.
207
- class ActionDispatch::Request::Session
208
- def each(&block)
209
- hash = self.to_hash
210
- hash.each(&block)
163
+ Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
164
+ def _render
165
+ #{ERB.new(File.read(Web::LAYOUT)).src}
211
166
  end
212
- end
167
+ RUBY
213
168
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/client'
3
2
 
4
- module Sidekiq
3
+ require "sidekiq/client"
5
4
 
5
+ module Sidekiq
6
6
  ##
7
7
  # Include this module in your worker class and you can easily create
8
8
  # asynchronous jobs:
@@ -21,15 +21,124 @@ module Sidekiq
21
21
  #
22
22
  # Note that perform_async is a class method, perform is an instance method.
23
23
  module Worker
24
+ ##
25
+ # The Options module is extracted so we can include it in ActiveJob::Base
26
+ # and allow native AJs to configure Sidekiq features/internals.
27
+ module Options
28
+ def self.included(base)
29
+ base.extend(ClassMethods)
30
+ base.sidekiq_class_attribute :sidekiq_options_hash
31
+ base.sidekiq_class_attribute :sidekiq_retry_in_block
32
+ base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
33
+ end
34
+
35
+ module ClassMethods
36
+ ACCESSOR_MUTEX = Mutex.new
37
+
38
+ ##
39
+ # Allows customization for this type of Worker.
40
+ # Legal options:
41
+ #
42
+ # queue - name of queue to use for this job type, default *default*
43
+ # retry - enable retries for this Worker in case of error during execution,
44
+ # *true* to use the default or *Integer* count
45
+ # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
46
+ # can be true, false or an integer number of lines to save, default *false*
47
+ #
48
+ # In practice, any option is allowed. This is the main mechanism to configure the
49
+ # options for a specific job.
50
+ def sidekiq_options(opts = {})
51
+ opts = opts.transform_keys(&:to_s) # stringify
52
+ self.sidekiq_options_hash = get_sidekiq_options.merge(opts)
53
+ end
54
+
55
+ def sidekiq_retry_in(&block)
56
+ self.sidekiq_retry_in_block = block
57
+ end
58
+
59
+ def sidekiq_retries_exhausted(&block)
60
+ self.sidekiq_retries_exhausted_block = block
61
+ end
62
+
63
+ def get_sidekiq_options # :nodoc:
64
+ self.sidekiq_options_hash ||= Sidekiq.default_worker_options
65
+ end
66
+
67
+ def sidekiq_class_attribute(*attrs)
68
+ instance_reader = true
69
+ instance_writer = true
70
+
71
+ attrs.each do |name|
72
+ synchronized_getter = "__synchronized_#{name}"
73
+
74
+ singleton_class.instance_eval do
75
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
76
+ end
77
+
78
+ define_singleton_method(synchronized_getter) { nil }
79
+ singleton_class.class_eval do
80
+ private(synchronized_getter)
81
+ end
82
+
83
+ define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
84
+
85
+ ivar = "@#{name}"
86
+
87
+ singleton_class.instance_eval do
88
+ m = "#{name}="
89
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
90
+ end
91
+ define_singleton_method("#{name}=") do |val|
92
+ singleton_class.class_eval do
93
+ ACCESSOR_MUTEX.synchronize do
94
+ undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
95
+ define_method(synchronized_getter) { val }
96
+ end
97
+ end
98
+
99
+ if singleton_class?
100
+ class_eval do
101
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
102
+ define_method(name) do
103
+ if instance_variable_defined? ivar
104
+ instance_variable_get ivar
105
+ else
106
+ singleton_class.send name
107
+ end
108
+ end
109
+ end
110
+ end
111
+ val
112
+ end
113
+
114
+ if instance_reader
115
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
116
+ define_method(name) do
117
+ if instance_variable_defined?(ivar)
118
+ instance_variable_get ivar
119
+ else
120
+ self.class.public_send name
121
+ end
122
+ end
123
+ end
124
+
125
+ if instance_writer
126
+ m = "#{name}="
127
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
128
+ attr_writer name
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
24
135
  attr_accessor :jid
25
136
 
26
137
  def self.included(base)
27
- raise ArgumentError, "You cannot include Sidekiq::Worker in an ActiveJob: #{base.name}" if base.ancestors.any? {|c| c.name == 'ActiveJob::Base' }
138
+ raise ArgumentError, "Sidekiq::Worker cannot be included in an ActiveJob: #{base.name}" if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
28
139
 
140
+ base.include(Options)
29
141
  base.extend(ClassMethods)
30
- base.sidekiq_class_attribute :sidekiq_options_hash
31
- base.sidekiq_class_attribute :sidekiq_retry_in_block
32
- base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
33
142
  end
34
143
 
35
144
  def logger
@@ -52,7 +161,7 @@ module Sidekiq
52
161
  end
53
162
 
54
163
  def perform_async(*args)
55
- @klass.client_push(@opts.merge('args' => args, 'class' => @klass))
164
+ @klass.client_push(@opts.merge("args" => args, "class" => @klass))
56
165
  end
57
166
 
58
167
  # +interval+ must be a timestamp, numeric or something that acts
@@ -62,17 +171,15 @@ module Sidekiq
62
171
  now = Time.now.to_f
63
172
  ts = (int < 1_000_000_000 ? now + int : int)
64
173
 
65
- payload = @opts.merge('class' => @klass, 'args' => args, 'at' => ts)
174
+ payload = @opts.merge("class" => @klass, "args" => args)
66
175
  # Optimization to enqueue something now that is scheduled to go out now or in the past
67
- payload.delete('at') if ts <= now
176
+ payload["at"] = ts if ts > now
68
177
  @klass.client_push(payload)
69
178
  end
70
179
  alias_method :perform_at, :perform_in
71
180
  end
72
181
 
73
182
  module ClassMethods
74
- ACCESSOR_MUTEX = Mutex.new
75
-
76
183
  def delay(*args)
77
184
  raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
78
185
  end
@@ -90,7 +197,7 @@ module Sidekiq
90
197
  end
91
198
 
92
199
  def perform_async(*args)
93
- client_push('class' => self, 'args' => args)
200
+ client_push("class" => self, "args" => args)
94
201
  end
95
202
 
96
203
  # +interval+ must be a timestamp, numeric or something that acts
@@ -100,10 +207,10 @@ module Sidekiq
100
207
  now = Time.now.to_f
101
208
  ts = (int < 1_000_000_000 ? now + int : int)
102
209
 
103
- item = { 'class' => self, 'args' => args, 'at' => ts }
210
+ item = {"class" => self, "args" => args}
104
211
 
105
212
  # Optimization to enqueue something now that is scheduled to go out now or in the past
106
- item.delete('at') if ts <= now
213
+ item["at"] = ts if ts > now
107
214
 
108
215
  client_push(item)
109
216
  end
@@ -122,99 +229,16 @@ module Sidekiq
122
229
  #
123
230
  # In practice, any option is allowed. This is the main mechanism to configure the
124
231
  # options for a specific job.
125
- def sidekiq_options(opts={})
126
- # stringify
127
- self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map{|k, v| [k.to_s, v]}])
128
- end
129
-
130
- def sidekiq_retry_in(&block)
131
- self.sidekiq_retry_in_block = block
132
- end
133
-
134
- def sidekiq_retries_exhausted(&block)
135
- self.sidekiq_retries_exhausted_block = block
136
- end
137
-
138
- def get_sidekiq_options # :nodoc:
139
- self.sidekiq_options_hash ||= Sidekiq.default_worker_options
232
+ def sidekiq_options(opts = {})
233
+ super
140
234
  end
141
235
 
142
236
  def client_push(item) # :nodoc:
143
- pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options['pool'] || Sidekiq.redis_pool
144
- # stringify
145
- item.keys.each do |key|
146
- item[key.to_s] = item.delete(key)
147
- end
237
+ pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
238
+ stringified_item = item.transform_keys(&:to_s)
148
239
 
149
- Sidekiq::Client.new(pool).push(item)
240
+ Sidekiq::Client.new(pool).push(stringified_item)
150
241
  end
151
-
152
- def sidekiq_class_attribute(*attrs)
153
- instance_reader = true
154
- instance_writer = true
155
-
156
- attrs.each do |name|
157
- synchronized_getter = "__synchronized_#{name}"
158
-
159
- singleton_class.instance_eval do
160
- undef_method(name) if method_defined?(name) || private_method_defined?(name)
161
- end
162
-
163
- define_singleton_method(synchronized_getter) { nil }
164
- singleton_class.class_eval do
165
- private(synchronized_getter)
166
- end
167
-
168
- define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
169
-
170
- ivar = "@#{name}"
171
-
172
- singleton_class.instance_eval do
173
- m = "#{name}="
174
- undef_method(m) if method_defined?(m) || private_method_defined?(m)
175
- end
176
- define_singleton_method("#{name}=") do |val|
177
- singleton_class.class_eval do
178
- ACCESSOR_MUTEX.synchronize do
179
- undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
180
- define_method(synchronized_getter) { val }
181
- end
182
- end
183
-
184
- if singleton_class?
185
- class_eval do
186
- undef_method(name) if method_defined?(name) || private_method_defined?(name)
187
- define_method(name) do
188
- if instance_variable_defined? ivar
189
- instance_variable_get ivar
190
- else
191
- singleton_class.send name
192
- end
193
- end
194
- end
195
- end
196
- val
197
- end
198
-
199
- if instance_reader
200
- undef_method(name) if method_defined?(name) || private_method_defined?(name)
201
- define_method(name) do
202
- if instance_variable_defined?(ivar)
203
- instance_variable_get ivar
204
- else
205
- self.class.public_send name
206
- end
207
- end
208
- end
209
-
210
- if instance_writer
211
- m = "#{name}="
212
- undef_method(m) if method_defined?(m) || private_method_defined?(m)
213
- attr_writer name
214
- end
215
- end
216
- end
217
-
218
242
  end
219
243
  end
220
244
  end