aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext/module
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/core_ext/module')
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb53
-rw-r--r--activesupport/lib/active_support/core_ext/module/qualified_const.rb64
-rw-r--r--activesupport/lib/active_support/core_ext/module/reachable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/remove_method.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb2
5 files changed, 106 insertions, 26 deletions
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 41f9a76b13..7de824a77f 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,5 +1,3 @@
-require "active_support/core_ext/module/remove_method"
-
class Module
# Provides a delegate class method to easily expose contained objects' methods
# as your own. Pass one or more methods (specified as symbols or strings)
@@ -108,35 +106,48 @@ class Module
unless options.is_a?(Hash) && to = options[:to]
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
end
+ prefix, to, allow_nil = options[:prefix], options[:to], options[:allow_nil]
- if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
+ if prefix == true && to.to_s =~ /^[^a-z_]/
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
end
- prefix = options[:prefix] ? "#{options[:prefix] == true ? to : options[:prefix]}_" : ''
+ method_prefix =
+ if prefix
+ "#{prefix == true ? to : prefix}_"
+ else
+ ''
+ end
file, line = caller.first.split(':', 2)
line = line.to_i
methods.each do |method|
- on_nil =
- if options[:allow_nil]
- 'return'
- else
- %(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- end
+ method = method.to_s
+
+ if allow_nil
+ module_eval(<<-EOS, file, line - 2)
+ def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block)
+ if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
+ #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
+ end # end
+ end # end
+ EOS
+ else
+ exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- module_eval(<<-EOS, file, line - 5)
- def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
- #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
- rescue NoMethodError # rescue NoMethodError
- if #{to}.nil? # if client.nil?
- #{on_nil} # return # depends on :allow_nil
- else # else
- raise # raise
- end # end
- end # end
- EOS
+ module_eval(<<-EOS, file, line - 1)
+ def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block)
+ #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
+ rescue NoMethodError # rescue NoMethodError
+ if #{to}.nil? # if client.nil?
+ #{exception} # # add helpful message to the exception
+ else # else
+ raise # raise
+ end # end
+ end # end
+ EOS
+ end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
new file mode 100644
index 0000000000..d1a0ee2f83
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
@@ -0,0 +1,64 @@
+require 'active_support/core_ext/string/inflections'
+
+#--
+# Allows code reuse in the methods below without polluting Module.
+#++
+module QualifiedConstUtils
+ def self.raise_if_absolute(path)
+ raise NameError, "wrong constant name #$&" if path =~ /\A::[^:]+/
+ end
+
+ def self.names(path)
+ path.split('::')
+ end
+end
+
+##
+# Extends the API for constants to be able to deal with qualified names. Arguments
+# are assumed to be relative to the receiver.
+#
+#--
+# Qualified names are required to be relative because we are extending existing
+# methods that expect constant names, ie, relative paths of length 1. For example,
+# Object.const_get("::String") raises NameError and so does qualified_const_get.
+#++
+class Module
+ if method(:const_defined?).arity == 1
+ def qualified_const_defined?(path)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ return unless mod.const_defined?(name)
+ mod.const_get(name)
+ end
+ return true
+ end
+ else
+ def qualified_const_defined?(path, search_parents=true)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ return unless mod.const_defined?(name, search_parents)
+ mod.const_get(name)
+ end
+ return true
+ end
+ end
+
+ def qualified_const_get(path)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ mod.const_get(name)
+ end
+ end
+
+ def qualified_const_set(path, value)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ const_name = path.demodulize
+ mod_name = path.deconstantize
+ mod = mod_name.empty? ? self : qualified_const_get(mod_name)
+ mod.const_set(const_name, value)
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb
index 443d2c3d53..5d3d0e9851 100644
--- a/activesupport/lib/active_support/core_ext/module/reachable.rb
+++ b/activesupport/lib/active_support/core_ext/module/reachable.rb
@@ -3,8 +3,6 @@ require 'active_support/core_ext/string/inflections'
class Module
def reachable? #:nodoc:
- !anonymous? && name.constantize.equal?(self)
- rescue NameError
- false
+ !anonymous? && name.safe_constantize.equal?(self)
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb
index 07d7c9b018..b76bc16ee1 100644
--- a/activesupport/lib/active_support/core_ext/module/remove_method.rb
+++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb
@@ -1,11 +1,16 @@
class Module
def remove_possible_method(method)
- remove_method(method)
+ if method_defined?(method) || private_method_defined?(method)
+ remove_method(method)
+ end
rescue NameError
+ # If the requested method is defined on a superclass or included module,
+ # method_defined? returns true but remove_method throws a NameError.
+ # Ignore this.
end
def redefine_method(method, &block)
remove_possible_method(method)
define_method(method, &block)
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
index ed16c2f71b..061621c0ef 100644
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb
@@ -1,6 +1,7 @@
require 'thread'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/module/deprecation'
class Module
# Synchronize access around a method, delegating synchronization to a
@@ -40,4 +41,5 @@ class Module
alias_method_chain method, :synchronization
end
end
+ deprecate :synchronize
end