aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/CHANGELOG.md7
-rw-r--r--actionmailer/lib/action_mailer/base.rb39
-rw-r--r--actionmailer/lib/action_mailer/preview.rb32
-rw-r--r--actionmailer/test/base_test.rb78
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb22
-rw-r--r--actionpack/test/dispatch/request_test.rb20
-rw-r--r--actionview/lib/action_view/testing/resolvers.rb3
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb16
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb6
10 files changed, 184 insertions, 51 deletions
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 0ecb0235bc..2280688cbc 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Add `Base.unregister_interceptor`, `Base.unregister_interceptors`,
+ `Base.unregister_preview_interceptor` and `Base.unregister_preview_interceptors`.
+ This makes it possible to dynamically add and remove email interceptors
+ at runtime in the same way they're registered.
+
+ *Claudio Ortolina*
+
* `config.force_ssl = true` will set
`config.action_mailer.default_url_options = { protocol: 'https' }`
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index cbd7cec70f..4c5d959e32 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -441,6 +441,8 @@ module ActionMailer
helper ActionMailer::MailHelper
+ private_class_method :find_class #:nodoc:
+
class_attribute :default_params
self.default_params = {
mime_version: "1.0",
@@ -460,16 +462,16 @@ module ActionMailer
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
end
+ # Unregister one or more Interceptors which would be called before mail is sent.
+ def unregister_interceptors(*interceptors)
+ interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
+ end
+
# Register an Observer which will be notified when mail is delivered.
# Either a class, string or symbol can be passed in as the Observer.
# If a string or symbol is passed in it will be camelized and constantized.
def register_observer(observer)
- delivery_observer = case observer
- when String, Symbol
- observer.to_s.camelize.constantize
- else
- observer
- end
+ delivery_observer = find_class(observer)
Mail.register_observer(delivery_observer)
end
@@ -478,16 +480,20 @@ module ActionMailer
# Either a class, string or symbol can be passed in as the Interceptor.
# If a string or symbol is passed in it will be camelized and constantized.
def register_interceptor(interceptor)
- delivery_interceptor = case interceptor
- when String, Symbol
- interceptor.to_s.camelize.constantize
- else
- interceptor
- end
+ delivery_interceptor = find_class(interceptor)
Mail.register_interceptor(delivery_interceptor)
end
+ # Unregister a previously registered interceptor.
+ # Either a class, string or symbol can be passed in as the Interceptor.
+ # If a string or symbol is passed in it will be camelized and constantized.
+ def unregister_interceptor(interceptor)
+ delivery_interceptor = find_class(interceptor)
+
+ Mail.unregister_interceptor(delivery_interceptor)
+ end
+
# Returns the name of current mailer. This method is also being used as a path for a view lookup.
# If this is an anonymous mailer, this method will return +anonymous+ instead.
def mailer_name
@@ -841,6 +847,15 @@ module ActionMailer
m
end
+ def self.find_class(klass_or_string_or_symbol)
+ case klass_or_string_or_symbol
+ when String, Symbol
+ klass_or_string_or_symbol.to_s.camelize.constantize
+ else
+ klass_or_string_or_symbol
+ end
+ end
+
protected
# Used by #mail to set the content type of the message.
diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb
index aab92fe8db..0b37f405ee 100644
--- a/actionmailer/lib/action_mailer/preview.rb
+++ b/actionmailer/lib/action_mailer/preview.rb
@@ -30,21 +30,39 @@ module ActionMailer
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
end
+ # Unregister one or more previously registered Interceptors.
+ def unregister_preview_interceptors(*interceptors)
+ interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) }
+ end
+
# Register an Interceptor which will be called before mail is previewed.
# Either a class or a string can be passed in as the Interceptor. If a
# string is passed in it will be <tt>constantize</tt>d.
def register_preview_interceptor(interceptor)
- preview_interceptor = case interceptor
- when String, Symbol
- interceptor.to_s.camelize.constantize
- else
- interceptor
- end
-
+ preview_interceptor = find_class(interceptor)
unless preview_interceptors.include?(preview_interceptor)
preview_interceptors << preview_interceptor
end
end
+
+ # Unregister a previously registered Interceptor.
+ # Either a class or a string can be passed in as the Interceptor. If a
+ # string is passed in it will be <tt>constantize</tt>d.
+ def unregister_preview_interceptor(interceptor)
+ preview_interceptor = find_class(interceptor)
+ preview_interceptors.delete(preview_interceptor)
+ end
+
+ private
+
+ def find_class(klass_or_string_or_symbol) #:nodoc:
+ case klass_or_string_or_symbol
+ when String, Symbol
+ klass_or_string_or_symbol.to_s.camelize.constantize
+ else
+ klass_or_string_or_symbol
+ end
+ end
end
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 50f2c71737..9828d80044 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -707,6 +707,47 @@ class BaseTest < ActiveSupport::TestCase
end
end
+ test "you can unregister an interceptor to the mail object that gets passed the mail object before delivery" do
+ mail_side_effects do
+ ActionMailer::Base.register_interceptor(MyInterceptor)
+ ActionMailer::Base.unregister_interceptor(MyInterceptor)
+ mail = BaseMailer.welcome
+ MyInterceptor.expects(:delivering_email).with(mail).never
+ mail.deliver_now
+ end
+ end
+
+ test "you can unregister an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
+ mail_side_effects do
+ ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
+ ActionMailer::Base.unregister_interceptor("BaseTest::MyInterceptor")
+ mail = BaseMailer.welcome
+ MyInterceptor.expects(:delivering_email).with(mail).never
+ mail.deliver_now
+ end
+ end
+
+ test "you can unregister an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before delivery" do
+ mail_side_effects do
+ ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
+ ActionMailer::Base.unregister_interceptor(:"base_test/my_interceptor")
+ mail = BaseMailer.welcome
+ MyInterceptor.expects(:delivering_email).with(mail).never
+ mail.deliver_now
+ end
+ end
+
+ test "you can unregister multiple interceptors to the mail object that both get passed the mail object before delivery" do
+ mail_side_effects do
+ ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
+ ActionMailer::Base.unregister_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
+ mail = BaseMailer.welcome
+ MyInterceptor.expects(:delivering_email).with(mail).never
+ MySecondInterceptor.expects(:delivering_email).with(mail).never
+ mail.deliver_now
+ end
+ end
+
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
mail1 = ProcMailer.welcome['X-Proc-Method']
yesterday = 1.day.ago
@@ -944,4 +985,41 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
end
end
end
+
+ test "you can unregister a preview interceptor to the mail object that gets passed the mail object before previewing" do
+ ActionMailer::Base.register_preview_interceptor(MyInterceptor)
+ ActionMailer::Base.unregister_preview_interceptor(MyInterceptor)
+ mail = BaseMailer.welcome
+ BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
+ MyInterceptor.expects(:previewing_email).with(mail).never
+ BaseMailerPreview.call(:welcome)
+ end
+
+ test "you can unregister a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
+ ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
+ ActionMailer::Base.unregister_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
+ mail = BaseMailer.welcome
+ BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
+ MyInterceptor.expects(:previewing_email).with(mail).never
+ BaseMailerPreview.call(:welcome)
+ end
+
+ test "you can unregister an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
+ ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
+ ActionMailer::Base.unregister_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
+ mail = BaseMailer.welcome
+ BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
+ MyInterceptor.expects(:previewing_email).with(mail).never
+ BaseMailerPreview.call(:welcome)
+ end
+
+ test "you can unregister multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
+ ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
+ ActionMailer::Base.unregister_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
+ mail = BaseMailer.welcome
+ BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
+ MyInterceptor.expects(:previewing_email).with(mail).never
+ MySecondInterceptor.expects(:previewing_email).with(mail).never
+ BaseMailerPreview.call(:welcome)
+ end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index ea61ad0c02..bd0f38953a 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -306,10 +306,16 @@ module ActionDispatch
end
end
- # Returns true if the request's content MIME type is
- # +application/x-www-form-urlencoded+ or +multipart/form-data+.
+ # Determine whether the request body contains form-data by checking
+ # the request Content-Type for one of the media-types:
+ # "application/x-www-form-urlencoded" or "multipart/form-data". The
+ # list of form-data media types can be modified through the
+ # +FORM_DATA_MEDIA_TYPES+ array.
+ #
+ # A request body is not assumed to contain form-data when no
+ # Content-Type header is provided and the request_method is POST.
def form_data?
- FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
+ FORM_DATA_MEDIA_TYPES.include?(media_type)
end
def body_stream #:nodoc:
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index aee2334da9..31b75498b6 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -43,7 +43,7 @@ module ActionDispatch
# Create a new +RemoteIp+ middleware instance.
#
- # The +check_ip_spoofing+ option is on by default. When on, an exception
+ # The +ip_spoofing_check+ option is on by default. When on, an exception
# is raised if it looks like the client is trying to lie about its own IP
# address. It makes sense to turn off this check on sites aimed at non-IP
# clients (like WAP devices), or behind proxies that set headers in an
@@ -57,9 +57,9 @@ module ActionDispatch
# with your proxy servers after it. If your proxies aren't removed, pass
# them in via the +custom_proxies+ parameter. That way, the middleware will
# ignore those IP addresses, and return the one that you want.
- def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
+ def initialize(app, ip_spoofing_check = true, custom_proxies = nil)
@app = app
- @check_ip = check_ip_spoofing
+ @check_ip = ip_spoofing_check
@proxies = if custom_proxies.blank?
TRUSTED_PROXIES
elsif custom_proxies.respond_to?(:any?)
@@ -116,10 +116,18 @@ module ActionDispatch
forwarded_ips = ips_from(@req.x_forwarded_for).reverse
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
- # If they are both set, it means that this request passed through two
- # proxies with incompatible IP header conventions, and there is no way
- # for us to determine which header is the right one after the fact.
- # Since we have no idea, we give up and explode.
+ # If they are both set, it means that either:
+ #
+ # 1) This request passed through two proxies with incompatible IP header
+ # conventions.
+ # 2) The client passed one of +Client-Ip+ or +X-Forwarded-For+
+ # (whichever the proxy servers weren't using) themselves.
+ #
+ # Either way, there is no way for us to determine which header is the
+ # right one after the fact. Since we have no idea, if we are concerned
+ # about IP spoofing we need to give up and explode. (If you're not
+ # concerned about IP spoofing you can turn the +ip_spoofing_check+
+ # option off.)
should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 22240699d9..08c4554721 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -1212,3 +1212,23 @@ class RequestVariant < BaseRequestTest
end
end
end
+
+class RequestFormData < BaseRequestTest
+ test 'media_type is from the FORM_DATA_MEDIA_TYPES array' do
+ assert stub_request('CONTENT_TYPE' => 'application/x-www-form-urlencoded').form_data?
+ assert stub_request('CONTENT_TYPE' => 'multipart/form-data').form_data?
+ end
+
+ test 'media_type is not from the FORM_DATA_MEDIA_TYPES array' do
+ assert !stub_request('CONTENT_TYPE' => 'application/xml').form_data?
+ assert !stub_request('CONTENT_TYPE' => 'multipart/related').form_data?
+ end
+
+ test 'no Content-Type header is provided and the request_method is POST' do
+ request = stub_request('REQUEST_METHOD' => 'POST')
+
+ assert_equal '', request.media_type
+ assert_equal 'POST', request.request_method
+ assert !request.form_data?
+ end
+end
diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb
index dfb7d463b4..63a60542d4 100644
--- a/actionview/lib/action_view/testing/resolvers.rb
+++ b/actionview/lib/action_view/testing/resolvers.rb
@@ -46,9 +46,8 @@ module ActionView #:nodoc:
class NullResolver < PathResolver
def query(path, exts, formats)
handler, format, variant = extract_handler_and_format_and_variant(path, formats)
- [ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format, :variant => variant)]
+ [ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, :virtual_path => path.virtual, :format => format, :variant => variant)]
end
end
-
end
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 27de313d05..b1333f110c 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -36,13 +36,8 @@ module ActiveRecord
# may vary depending on the klass of a relation, so we create a subclass of Relation
# for each different klass, and the delegations are compiled into that subclass only.
- BLACKLISTED_ARRAY_METHODS = [
- :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
- :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
- :keep_if, :pop, :shift, :delete_at, :select!
- ].to_set # :nodoc:
-
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
+ :[], :&, :|, :+, :-, :sample, :reverse, :compact, to: :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :to => :klass
@@ -114,21 +109,14 @@ module ActiveRecord
def respond_to?(method, include_private = false)
super || @klass.respond_to?(method, include_private) ||
- array_delegable?(method) ||
arel.respond_to?(method, include_private)
end
protected
- def array_delegable?(method)
- Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
- end
-
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
scoping { @klass.public_send(method, *args, &block) }
- elsif array_delegable?(method)
- to_a.public_send(method, *args, &block)
elsif arel.respond_to?(method)
arel.public_send(method, *args, &block)
else
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index 989f4e1e5d..b4269bd56d 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -40,12 +40,6 @@ module ActiveRecord
assert_respond_to target, method
end
end
-
- ActiveRecord::Delegation::BLACKLISTED_ARRAY_METHODS.each do |method|
- define_method "test_#{method}_is_not_delegated_to_Array" do
- assert_raises(NoMethodError) { call_method(target, method) }
- end
- end
end
class DelegationAssociationTest < DelegationTest