From 01b1a8772928fbf31180341356981b95d0581413 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 29 Jan 2006 00:37:39 +0000 Subject: Added reusable reloading support through the inclusion of the Relodable module that all subclasses of ActiveRecord::Base, ActiveRecord::Observer, ActiveController::Base, and ActionMailer::Base automatically gets [DHH]. Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb [Nicholas Seckar]. Refactored extensions to module, class, and object in active support [DHH] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3493 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionmailer/lib/action_mailer/base.rb | 7 ++ actionpack/lib/action_controller/base.rb | 7 ++ actionpack/lib/action_controller/layout.rb | 9 +- actionpack/lib/action_controller/test_process.rb | 7 ++ actionpack/test/template/text_helper_test.rb | 2 +- activerecord/lib/active_record/base.rb | 1 + activerecord/lib/active_record/observer.rb | 7 ++ .../test/class_inheritable_attributes_test.rb | 2 +- activesupport/CHANGELOG | 10 ++ activesupport/lib/active_support.rb | 3 +- .../active_support/class_attribute_accessors.rb | 44 -------- .../active_support/class_inheritable_attributes.rb | 115 --------------------- activesupport/lib/active_support/clean_logger.rb | 2 +- activesupport/lib/active_support/core_ext/blank.rb | 16 ++- activesupport/lib/active_support/core_ext/class.rb | 3 + .../core_ext/class/attribute_accessors.rb | 44 ++++++++ .../core_ext/class/inheritable_attributes.rb | 115 +++++++++++++++++++++ .../lib/active_support/core_ext/class/removal.rb | 21 ++++ .../lib/active_support/core_ext/exception.rb | 1 - .../lib/active_support/core_ext/module.rb | 2 + .../core_ext/module/attribute_accessors.rb | 44 ++++++++ .../active_support/core_ext/module/inclusion.rb | 15 +++ .../lib/active_support/core_ext/object.rb | 2 + .../active_support/core_ext/object/extending.rb | 45 ++++++++ .../lib/active_support/core_ext/object/misc.rb | 16 +++ .../active_support/core_ext/object_and_class.rb | 84 --------------- activesupport/lib/active_support/dependencies.rb | 65 +++++++++--- .../active_support/module_attribute_accessors.rb | 44 -------- activesupport/lib/active_support/reloadable.rb | 4 + activesupport/test/autoloading_fixtures/a/b.rb | 2 + activesupport/test/autoloading_fixtures/a/c/d.rb | 2 + activesupport/test/autoloading_fixtures/e.rb | 2 + .../test/class_inheritable_attributes_test.rb | 2 +- activesupport/test/core_ext/blank_test.rb | 2 +- activesupport/test/core_ext/class_test.rb | 37 +++++++ activesupport/test/core_ext/module_test.rb | 51 +++++++++ .../test/core_ext/object_and_class_ext_test.rb | 3 +- activesupport/test/dependencies_test.rb | 17 +++ activesupport/test/option_merger_test.rb | 2 +- railties/lib/dispatcher.rb | 3 +- 40 files changed, 542 insertions(+), 318 deletions(-) delete mode 100644 activesupport/lib/active_support/class_attribute_accessors.rb delete mode 100644 activesupport/lib/active_support/class_inheritable_attributes.rb create mode 100644 activesupport/lib/active_support/core_ext/class.rb create mode 100644 activesupport/lib/active_support/core_ext/class/attribute_accessors.rb create mode 100644 activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb create mode 100644 activesupport/lib/active_support/core_ext/class/removal.rb create mode 100644 activesupport/lib/active_support/core_ext/module.rb create mode 100644 activesupport/lib/active_support/core_ext/module/attribute_accessors.rb create mode 100644 activesupport/lib/active_support/core_ext/module/inclusion.rb create mode 100644 activesupport/lib/active_support/core_ext/object.rb create mode 100644 activesupport/lib/active_support/core_ext/object/extending.rb create mode 100644 activesupport/lib/active_support/core_ext/object/misc.rb delete mode 100644 activesupport/lib/active_support/core_ext/object_and_class.rb delete mode 100644 activesupport/lib/active_support/module_attribute_accessors.rb create mode 100644 activesupport/lib/active_support/reloadable.rb create mode 100644 activesupport/test/autoloading_fixtures/a/b.rb create mode 100644 activesupport/test/autoloading_fixtures/a/c/d.rb create mode 100644 activesupport/test/autoloading_fixtures/e.rb create mode 100644 activesupport/test/core_ext/class_test.rb create mode 100644 activesupport/test/core_ext/module_test.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 8659489808..39fa1e4aac 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -121,6 +121,13 @@ module ActionMailer class Base include AdvAttrAccessor, PartContainer + # Action Mailer subclasses should be reloaded by the dispatcher in Rails + # when Dependencies.mechanism = :load. + def self.inherited(child) #:nodoc: + child.send :include, Reloadable + super + end + private_class_method :new #:nodoc: cattr_accessor :template_root diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 7f8ff5ed09..43f948e73d 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -224,6 +224,13 @@ module ActionController #:nodoc: # FCGI.each_cgi{ |cgi| WeblogController.process_cgi(cgi) } class Base DEFAULT_RENDER_STATUS_CODE = "200 OK" + + # Action Controller subclasses should be reloaded by the dispatcher in Rails + # when Dependencies.mechanism = :load. + def self.inherited(child) #:nodoc: + child.send :include, Reloadable + super + end # Determines whether the view has access to controller internals @request, @response, @session, and @template. # By default, it does. diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index cc153c45a7..f2a5b97bf1 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -1,16 +1,16 @@ module ActionController #:nodoc: module Layout #:nodoc: - def self.append_features(base) - super + def self.included(base) + base.extend(ClassMethods) base.class_eval do alias_method :render_with_no_layout, :render alias_method :render, :render_with_a_layout class << self alias_method :inherited_without_layout, :inherited + alias_method :inherited, :inherited_with_layout end end - base.extend(ClassMethods) end # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in @@ -172,8 +172,9 @@ module ActionController #:nodoc: end private - def inherited(child) + def inherited_with_layout(child) inherited_without_layout(child) + child.send :include, Reloadable layout_match = child.name.underscore.sub(/_controller$/, '') child.layout(layout_match) unless layout_list.grep(%r{layouts/#{layout_match}\.[a-z][0-9a-z]*$}).empty? end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 289ab3d5e7..caba4bfb39 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -333,6 +333,13 @@ module ActionController #:nodoc: end end + def build_request_uri(action, parameters) + options = @controller.send(:rewrite_options, parameters) + options.update(:only_path => true, :action => action) + url = ActionController::UrlRewriter.new(@request, parameters) + @request.set_REQUEST_URI(url.rewrite(options)) + end + def session @response.session end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index b323568068..d1ef946f29 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -3,7 +3,7 @@ require "#{File.dirname(__FILE__)}/../testing_sandbox" require File.dirname(__FILE__) + '/../../lib/action_view/helpers/text_helper' require File.dirname(__FILE__) + '/../../../activesupport/lib/active_support/core_ext/numeric' # for human_size require File.dirname(__FILE__) + '/../../../activesupport/lib/active_support/core_ext/hash' # for stringify_keys -require File.dirname(__FILE__) + '/../../../activesupport/lib/active_support/core_ext/object_and_class.rb' # for blank? +require File.dirname(__FILE__) + '/../../../activesupport/lib/active_support/core_ext/object.rb' # for blank? class TextHelperTest < Test::Unit::TestCase include ActionView::Helpers::TextHelper diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d05e3c6cae..f134402c58 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -244,6 +244,7 @@ module ActiveRecord #:nodoc: cattr_accessor :logger def self.inherited(child) #:nodoc: + child.send :include, Reloadable @@subclasses[self] ||= [] @@subclasses[self] << child super diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index 7d33509c8c..d43909c3c5 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -81,6 +81,13 @@ module ActiveRecord class Observer include Singleton + # Observer subclasses should be reloaded by the dispatcher in Rails + # when Dependencies.mechanism = :load. + def self.inherited(child) #:nodoc: + child.send :include, Reloadable + super + end + # Attaches the observer to the supplied model classes. def self.observe(*models) define_method(:observed_class) { models } diff --git a/activerecord/test/class_inheritable_attributes_test.rb b/activerecord/test/class_inheritable_attributes_test.rb index 3419ffbd9a..a3f94e3abb 100644 --- a/activerecord/test/class_inheritable_attributes_test.rb +++ b/activerecord/test/class_inheritable_attributes_test.rb @@ -1,6 +1,6 @@ require 'test/unit' require 'abstract_unit' -require 'active_support/class_inheritable_attributes' +require 'active_support/core_ext/class/inheritable_attributes' class A include ClassInheritableAttributes diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index c448efbefb..d1e9c1c331 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,15 @@ *SVN* +* Added reusable reloading support through the inclusion of the Relodable module that all subclasses of ActiveRecord::Base, ActiveRecord::Observer, ActiveController::Base, and ActionMailer::Base automatically gets. This means that these classes will be reloaded by the dispatcher when Dependencies.mechanism = :load. You can make your own models reloadable easily: + + class Setting + include Reloadable + end + + Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [DHH] + +* Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb [Nicholas Seckar] + * Add Object#instance_exec, like instance_eval but passes its arguments to the block. (Active Support will not override the Ruby 1.9 implementation of this method.) [Sam Stephenson] * Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. [Sam Stephenson] diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index f498a8e1f5..7bd74101f5 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -23,13 +23,12 @@ $:.unshift(File.dirname(__FILE__)) -require 'active_support/class_attribute_accessors' -require 'active_support/class_inheritable_attributes' require 'active_support/inflector' require 'active_support/core_ext' require 'active_support/clean_logger' require 'active_support/dependencies' +require 'active_support/reloadable' require 'active_support/ordered_options' require 'active_support/option_merger' diff --git a/activesupport/lib/active_support/class_attribute_accessors.rb b/activesupport/lib/active_support/class_attribute_accessors.rb deleted file mode 100644 index 93a7d48f69..0000000000 --- a/activesupport/lib/active_support/class_attribute_accessors.rb +++ /dev/null @@ -1,44 +0,0 @@ -# Extends the class object with class and instance accessors for class attributes, -# just like the native attr* accessors for instance attributes. -class Class # :nodoc: - def cattr_reader(*syms) - syms.flatten.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} - @@#{sym} = nil - end - - def self.#{sym} - @@#{sym} - end - - def #{sym} - @@#{sym} - end - EOS - end - end - - def cattr_writer(*syms) - syms.flatten.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} - @@#{sym} = nil - end - - def self.#{sym}=(obj) - @@#{sym} = obj - end - - def #{sym}=(obj) - @@#{sym} = obj - end - EOS - end - end - - def cattr_accessor(*syms) - cattr_reader(*syms) - cattr_writer(*syms) - end -end diff --git a/activesupport/lib/active_support/class_inheritable_attributes.rb b/activesupport/lib/active_support/class_inheritable_attributes.rb deleted file mode 100644 index 2e70a64436..0000000000 --- a/activesupport/lib/active_support/class_inheritable_attributes.rb +++ /dev/null @@ -1,115 +0,0 @@ -# Retain for backward compatibility. Methods are now included in Class. -module ClassInheritableAttributes # :nodoc: -end - -# Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of -# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements -# to, for example, an array without those additions being shared with either their parent, siblings, or -# children, which is unlike the regular class-level attributes that are shared across the entire hierarchy. -class Class # :nodoc: - def class_inheritable_reader(*syms) - syms.each do |sym| - class_eval <<-EOS - def self.#{sym} - read_inheritable_attribute(:#{sym}) - end - - def #{sym} - self.class.#{sym} - end - EOS - end - end - - def class_inheritable_writer(*syms) - syms.each do |sym| - class_eval <<-EOS - def self.#{sym}=(obj) - write_inheritable_attribute(:#{sym}, obj) - end - - def #{sym}=(obj) - self.class.#{sym} = obj - end - EOS - end - end - - def class_inheritable_array_writer(*syms) - syms.each do |sym| - class_eval <<-EOS - def self.#{sym}=(obj) - write_inheritable_array(:#{sym}, obj) - end - - def #{sym}=(obj) - self.class.#{sym} = obj - end - EOS - end - end - - def class_inheritable_hash_writer(*syms) - syms.each do |sym| - class_eval <<-EOS - def self.#{sym}=(obj) - write_inheritable_hash(:#{sym}, obj) - end - - def #{sym}=(obj) - self.class.#{sym} = obj - end - EOS - end - end - - def class_inheritable_accessor(*syms) - class_inheritable_reader(*syms) - class_inheritable_writer(*syms) - end - - def class_inheritable_array(*syms) - class_inheritable_reader(*syms) - class_inheritable_array_writer(*syms) - end - - def class_inheritable_hash(*syms) - class_inheritable_reader(*syms) - class_inheritable_hash_writer(*syms) - end - - def inheritable_attributes - @inheritable_attributes ||= {} - end - - def write_inheritable_attribute(key, value) - inheritable_attributes[key] = value - end - - def write_inheritable_array(key, elements) - write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil? - write_inheritable_attribute(key, read_inheritable_attribute(key) + elements) - end - - def write_inheritable_hash(key, hash) - write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil? - write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash)) - end - - def read_inheritable_attribute(key) - inheritable_attributes[key] - end - - def reset_inheritable_attributes - inheritable_attributes.clear - end - - private - def inherited_with_inheritable_attributes(child) - inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) - child.instance_variable_set('@inheritable_attributes', inheritable_attributes.dup) - end - - alias inherited_without_inheritable_attributes inherited - alias inherited inherited_with_inheritable_attributes -end diff --git a/activesupport/lib/active_support/clean_logger.rb b/activesupport/lib/active_support/clean_logger.rb index 2730ef9008..376896cb3c 100644 --- a/activesupport/lib/active_support/clean_logger.rb +++ b/activesupport/lib/active_support/clean_logger.rb @@ -1,5 +1,5 @@ require 'logger' -require File.dirname(__FILE__) + '/class_attribute_accessors' +require File.dirname(__FILE__) + '/core_ext/class/attribute_accessors' class Logger #:nodoc: cattr_accessor :silencer diff --git a/activesupport/lib/active_support/core_ext/blank.rb b/activesupport/lib/active_support/core_ext/blank.rb index 791cdc5705..f7fbea3e89 100644 --- a/activesupport/lib/active_support/core_ext/blank.rb +++ b/activesupport/lib/active_support/core_ext/blank.rb @@ -1,4 +1,16 @@ -# The methods here are provided to speed up function blank? in class Object +class Object #:nodoc: + # "", " ", nil, [], and {} are blank + def blank? + if respond_to?(:empty?) && respond_to?(:strip) + empty? or strip.empty? + elsif respond_to?(:empty?) + empty? + else + !self + end + end +end + class NilClass #:nodoc: def blank? true @@ -35,4 +47,4 @@ class Numeric #:nodoc: def blank? false end -end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb new file mode 100644 index 0000000000..7bacdb3903 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/class.rb @@ -0,0 +1,3 @@ +require File.dirname(__FILE__) + '/class/attribute_accessors' +require File.dirname(__FILE__) + '/class/inheritable_attributes' +require File.dirname(__FILE__) + '/class/removal' \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb new file mode 100644 index 0000000000..93a7d48f69 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -0,0 +1,44 @@ +# Extends the class object with class and instance accessors for class attributes, +# just like the native attr* accessors for instance attributes. +class Class # :nodoc: + def cattr_reader(*syms) + syms.flatten.each do |sym| + class_eval(<<-EOS, __FILE__, __LINE__) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym} + @@#{sym} + end + + def #{sym} + @@#{sym} + end + EOS + end + end + + def cattr_writer(*syms) + syms.flatten.each do |sym| + class_eval(<<-EOS, __FILE__, __LINE__) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym}=(obj) + @@#{sym} = obj + end + + def #{sym}=(obj) + @@#{sym} = obj + end + EOS + end + end + + def cattr_accessor(*syms) + cattr_reader(*syms) + cattr_writer(*syms) + end +end diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb new file mode 100644 index 0000000000..2e70a64436 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -0,0 +1,115 @@ +# Retain for backward compatibility. Methods are now included in Class. +module ClassInheritableAttributes # :nodoc: +end + +# Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of +# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements +# to, for example, an array without those additions being shared with either their parent, siblings, or +# children, which is unlike the regular class-level attributes that are shared across the entire hierarchy. +class Class # :nodoc: + def class_inheritable_reader(*syms) + syms.each do |sym| + class_eval <<-EOS + def self.#{sym} + read_inheritable_attribute(:#{sym}) + end + + def #{sym} + self.class.#{sym} + end + EOS + end + end + + def class_inheritable_writer(*syms) + syms.each do |sym| + class_eval <<-EOS + def self.#{sym}=(obj) + write_inheritable_attribute(:#{sym}, obj) + end + + def #{sym}=(obj) + self.class.#{sym} = obj + end + EOS + end + end + + def class_inheritable_array_writer(*syms) + syms.each do |sym| + class_eval <<-EOS + def self.#{sym}=(obj) + write_inheritable_array(:#{sym}, obj) + end + + def #{sym}=(obj) + self.class.#{sym} = obj + end + EOS + end + end + + def class_inheritable_hash_writer(*syms) + syms.each do |sym| + class_eval <<-EOS + def self.#{sym}=(obj) + write_inheritable_hash(:#{sym}, obj) + end + + def #{sym}=(obj) + self.class.#{sym} = obj + end + EOS + end + end + + def class_inheritable_accessor(*syms) + class_inheritable_reader(*syms) + class_inheritable_writer(*syms) + end + + def class_inheritable_array(*syms) + class_inheritable_reader(*syms) + class_inheritable_array_writer(*syms) + end + + def class_inheritable_hash(*syms) + class_inheritable_reader(*syms) + class_inheritable_hash_writer(*syms) + end + + def inheritable_attributes + @inheritable_attributes ||= {} + end + + def write_inheritable_attribute(key, value) + inheritable_attributes[key] = value + end + + def write_inheritable_array(key, elements) + write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil? + write_inheritable_attribute(key, read_inheritable_attribute(key) + elements) + end + + def write_inheritable_hash(key, hash) + write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil? + write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash)) + end + + def read_inheritable_attribute(key) + inheritable_attributes[key] + end + + def reset_inheritable_attributes + inheritable_attributes.clear + end + + private + def inherited_with_inheritable_attributes(child) + inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) + child.instance_variable_set('@inheritable_attributes', inheritable_attributes.dup) + end + + alias inherited_without_inheritable_attributes inherited + alias inherited inherited_with_inheritable_attributes +end diff --git a/activesupport/lib/active_support/core_ext/class/removal.rb b/activesupport/lib/active_support/core_ext/class/removal.rb new file mode 100644 index 0000000000..8fc4d728b3 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/class/removal.rb @@ -0,0 +1,21 @@ +class Class #:nodoc: + def remove_subclasses + Object.remove_subclasses_of(self) + end + + def subclasses + Object.subclasses_of(self).map { |o| o.to_s } + end + + def remove_class(klass) + if klass.to_s.include? "::" + modules = klass.to_s.split("::") + final_klass = modules.pop + + final_module = modules.inject(Object) { |final_type, part| final_type.const_get(part) } + final_module.send(:remove_const, final_klass) rescue nil + else + Object.send(:remove_const, klass.to_s) rescue nil + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/exception.rb b/activesupport/lib/active_support/core_ext/exception.rb index 2541f37495..0b65af7bf8 100644 --- a/activesupport/lib/active_support/core_ext/exception.rb +++ b/activesupport/lib/active_support/core_ext/exception.rb @@ -1,5 +1,4 @@ class Exception - alias :clean_message :message TraceSubstitutions = [] diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb new file mode 100644 index 0000000000..0f46631e65 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -0,0 +1,2 @@ +require File.dirname(__FILE__) + '/module/inclusion' +require File.dirname(__FILE__) + '/module/attribute_accessors' \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb new file mode 100644 index 0000000000..fe4f8a4fc0 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -0,0 +1,44 @@ +# Extends the module object with module and instance accessors for class attributes, +# just like the native attr* accessors for instance attributes. +class Module # :nodoc: + def mattr_reader(*syms) + syms.each do |sym| + class_eval(<<-EOS, __FILE__, __LINE__) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym} + @@#{sym} + end + + def #{sym} + @@#{sym} + end + EOS + end + end + + def mattr_writer(*syms) + syms.each do |sym| + class_eval(<<-EOS, __FILE__, __LINE__) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym}=(obj) + @@#{sym} = obj + end + + def #{sym}=(obj) + @@#{sym} = obj + end + EOS + end + end + + def mattr_accessor(*syms) + mattr_reader(*syms) + mattr_writer(*syms) + end +end diff --git a/activesupport/lib/active_support/core_ext/module/inclusion.rb b/activesupport/lib/active_support/core_ext/module/inclusion.rb new file mode 100644 index 0000000000..124c4551bc --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/inclusion.rb @@ -0,0 +1,15 @@ +class Module + def remove_classes_including + included_in_classes.each { |klass| Class.remove_class(klass) } + end + + def included_in_classes + classes = [] + ObjectSpace.each_object(Class) { |k| classes << k if k.included_modules.include?(self) } + + classes.reverse.inject([]) do |unique_classes, klass| + unique_classes << klass unless unique_classes.collect { |k| k.to_s }.include?(klass.to_s) + unique_classes + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb new file mode 100644 index 0000000000..3dd6deaba1 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -0,0 +1,2 @@ +require File.dirname(__FILE__) + '/object/extending' +require File.dirname(__FILE__) + '/object/misc' \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb new file mode 100644 index 0000000000..8c638f83c2 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/extending.rb @@ -0,0 +1,45 @@ +class Object #:nodoc: + def remove_subclasses_of(*superclasses) + subclasses_of(*superclasses).each do |subclass| + Object.send(:remove_const, subclass.to_s) rescue nil + end + end + + def subclasses_of(*superclasses) + subclasses = [] + ObjectSpace.each_object(Class) do |k| + next if (k.ancestors & superclasses).empty? || superclasses.include?(k) || k.to_s.include?("::") || subclasses.include?(k) + subclasses << k + end + subclasses + end + + def extended_by + ancestors = class << self; ancestors end + ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ] + end + + def copy_instance_variables_from(object, exclude = []) + exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables + + instance_variables = object.instance_variables - exclude.map { |name| name.to_s } + instance_variables.each { |name| instance_variable_set(name, object.instance_variable_get(name)) } + end + + def extend_with_included_modules_from(object) + object.extended_by.each { |mod| extend mod } + end + + def instance_values + instance_variables.inject({}) do |values, name| + values[name[1..-1]] = instance_variable_get(name) + values + end + end + + unless defined? instance_exec # 1.9 + def instance_exec(*arguments, &block) + block.bind(self)[*arguments] + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb new file mode 100644 index 0000000000..f599eee6f0 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/misc.rb @@ -0,0 +1,16 @@ +class Object #:nodoc: + def with_options(options) + yield ActiveSupport::OptionMerger.new(self, options) + end + + def to_json + ActiveSupport::JSON.encode(self) + end + + def suppress(*exception_classes) + begin yield + rescue Exception => e + raise unless exception_classes.any? { |cls| e.kind_of?(cls) } + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object_and_class.rb b/activesupport/lib/active_support/core_ext/object_and_class.rb deleted file mode 100644 index d44afbd839..0000000000 --- a/activesupport/lib/active_support/core_ext/object_and_class.rb +++ /dev/null @@ -1,84 +0,0 @@ -class Object #:nodoc: - def remove_subclasses_of(*superclasses) - subclasses_of(*superclasses).each do |subclass| - Object.send(:remove_const, subclass.to_s) rescue nil - end - end - - def subclasses_of(*superclasses) - subclasses = [] - ObjectSpace.each_object(Class) do |k| - next if (k.ancestors & superclasses).empty? || superclasses.include?(k) || k.to_s.include?("::") || subclasses.include?(k) - subclasses << k - end - subclasses - end - - def extended_by - ancestors = class << self; ancestors end - ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ] - end - - def copy_instance_variables_from(object, exclude = []) - exclude += object.protected_instance_variables if - object.respond_to? :protected_instance_variables - - instance_variables = object.instance_variables - exclude.map { |name| name.to_s } - instance_variables.each do |name| - instance_variable_set name, object.instance_variable_get(name) - end - end - - def extend_with_included_modules_from(object) - object.extended_by.each { |mod| extend mod } - end - - # "", " ", nil, [], and {} are blank - def blank? - if respond_to?(:empty?) && respond_to?(:strip) - empty? or strip.empty? - elsif respond_to?(:empty?) - empty? - else - !self - end - end - - def suppress(*exception_classes) - begin yield - rescue Exception => e - raise unless exception_classes.any? {|cls| e.kind_of? cls} - end - end - - def with_options(options) - yield ActiveSupport::OptionMerger.new(self, options) - end - - def instance_values - instance_variables.inject({}) do |values, name| - values[name[1..-1]] = instance_variable_get(name) - values - end - end - - def to_json - ActiveSupport::JSON.encode(self) - end - - unless defined? instance_exec # 1.9 - def instance_exec(*arguments, &block) - block.bind(self)[*arguments] - end - end -end - -class Class #:nodoc: - def remove_subclasses - Object.remove_subclasses_of(self) - end - - def subclasses - Object.subclasses_of(self).map { |o| o.to_s } - end -end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 8d1288cd96..37a3f4acb3 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,5 +1,5 @@ require 'set' -require File.dirname(__FILE__) + '/module_attribute_accessors' +require File.dirname(__FILE__) + '/core_ext/module/attribute_accessors' require File.dirname(__FILE__) + '/core_ext/load_error' require File.dirname(__FILE__) + '/core_ext/kernel' @@ -69,10 +69,6 @@ module Dependencies #:nodoc: history << file_name end - def remove_subclasses_for(*classes) - Object.remove_subclasses_of(*classes) - end - # LoadingModules implement namespace-safe dynamic loading. # They support automatic loading via const_missing, allowing contained items to be automatically # loaded when required. No extra syntax is required, as expressions such as Controller::Admin::UserController @@ -211,27 +207,70 @@ Object.send(:define_method, :require_association) { |file_name| Dependencies.ass class Module #:nodoc: # Rename the original handler so we can chain it to the new one alias :rails_original_const_missing :const_missing - + + def parent + parent_name = name.split('::')[0..-2] * '::' + parent_name.empty? ? Object : parent_name.constantize + end + + def as_load_path + if self == Object || self == Kernel + '' + elsif is_a? Class + parent == self ? '' : parent.as_load_path + else + name.split('::').collect do |word| + word.underscore + end * '/' + end + end + # Use const_missing to autoload associations so we don't have to # require_association when using single-table inheritance. def const_missing(class_id) - if Object.const_defined?(:Controllers) and Object::Controllers.const_available?(class_id) + if Object.const_defined?(:Controllers) && Object::Controllers.const_available?(class_id) return Object::Controllers.const_get(class_id) end file_name = class_id.to_s.demodulize.underscore + file_path = as_load_path.empty? ? file_name : "#{as_load_path}/#{file_name}" begin - require_dependency(file_name) - raise NameError.new("uninitialized constant #{class_id}") unless Object.const_defined?(class_id) - return Object.const_get(class_id) + require_dependency(file_path) + brief_name = self == Object ? '' : "#{name}::" + raise NameError.new("uninitialized constant #{brief_name}#{class_id}") unless const_defined?(class_id) + return const_get(class_id) rescue MissingSourceFile => e - # Convert the exception to a NameError only if the file we are looking for is the missing one. - raise unless e.is_missing? file_name + # Re-raise the error if it does not concern the file we were trying to load. + raise unless e.is_missing? file_path + + # Look for a directory in the load path that we ought to load. + if $LOAD_PATH.any? { |base| File.directory? "#{base}/#{file_path}" } + mod = Module.new + const_set class_id, mod # Create the new module + return mod + end + + if parent && parent != self + suppress(NameError) do + return parent.send(:const_missing, class_id) + end + end + raise NameError.new("uninitialized constant #{class_id}").copy_blame!(e) end end end +class Class + def const_missing(class_id) + if [Object, Kernel].include?(self) || parent == self + super + else + parent.send :const_missing, class_id + end + end +end + class Object #:nodoc: def load(file, *extras) super(file, *extras) @@ -267,4 +306,4 @@ class Exception #:nodoc: @blamed_files = exc.blamed_files.clone self end -end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/module_attribute_accessors.rb b/activesupport/lib/active_support/module_attribute_accessors.rb deleted file mode 100644 index fe4f8a4fc0..0000000000 --- a/activesupport/lib/active_support/module_attribute_accessors.rb +++ /dev/null @@ -1,44 +0,0 @@ -# Extends the module object with module and instance accessors for class attributes, -# just like the native attr* accessors for instance attributes. -class Module # :nodoc: - def mattr_reader(*syms) - syms.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} - @@#{sym} = nil - end - - def self.#{sym} - @@#{sym} - end - - def #{sym} - @@#{sym} - end - EOS - end - end - - def mattr_writer(*syms) - syms.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} - @@#{sym} = nil - end - - def self.#{sym}=(obj) - @@#{sym} = obj - end - - def #{sym}=(obj) - @@#{sym} = obj - end - EOS - end - end - - def mattr_accessor(*syms) - mattr_reader(*syms) - mattr_writer(*syms) - end -end diff --git a/activesupport/lib/active_support/reloadable.rb b/activesupport/lib/active_support/reloadable.rb new file mode 100644 index 0000000000..fca397afeb --- /dev/null +++ b/activesupport/lib/active_support/reloadable.rb @@ -0,0 +1,4 @@ +# Classes that include this module will automatically be reloaded +# by the Rails dispatcher when Dependencies.mechanism = :load. +module Reloadable +end \ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/a/b.rb b/activesupport/test/autoloading_fixtures/a/b.rb new file mode 100644 index 0000000000..9c9e6454cf --- /dev/null +++ b/activesupport/test/autoloading_fixtures/a/b.rb @@ -0,0 +1,2 @@ +class A::B +end \ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/a/c/d.rb b/activesupport/test/autoloading_fixtures/a/c/d.rb new file mode 100644 index 0000000000..0f40d6fbc4 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/a/c/d.rb @@ -0,0 +1,2 @@ +class A::C::D +end \ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/e.rb b/activesupport/test/autoloading_fixtures/e.rb new file mode 100644 index 0000000000..2f59e4fb75 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/e.rb @@ -0,0 +1,2 @@ +class E +end \ No newline at end of file diff --git a/activesupport/test/class_inheritable_attributes_test.rb b/activesupport/test/class_inheritable_attributes_test.rb index 9a472eacd2..36914e2bbf 100644 --- a/activesupport/test/class_inheritable_attributes_test.rb +++ b/activesupport/test/class_inheritable_attributes_test.rb @@ -1,5 +1,5 @@ require 'test/unit' -require File.dirname(__FILE__) + '/../lib/active_support/class_inheritable_attributes' +require File.dirname(__FILE__) + '/../lib/active_support/core_ext/class/inheritable_attributes' class ClassInheritableAttributesTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/blank_test.rb index 2aba73a008..ff7345e774 100644 --- a/activesupport/test/core_ext/blank_test.rb +++ b/activesupport/test/core_ext/blank_test.rb @@ -1,5 +1,5 @@ require 'test/unit' -require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/object_and_class' +require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/object' require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/blank' class BlankTest < Test::Unit::TestCase diff --git a/activesupport/test/core_ext/class_test.rb b/activesupport/test/core_ext/class_test.rb new file mode 100644 index 0000000000..6e956fa45d --- /dev/null +++ b/activesupport/test/core_ext/class_test.rb @@ -0,0 +1,37 @@ +require 'test/unit' +require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/class' + +class A +end + +module X + class B + end +end + +module Y + module Z + class C + end + end +end + +class ClassTest < Test::Unit::TestCase + def test_removing_class_in_root_namespace + assert A.is_a?(Class) + Class.remove_class(A) + assert_raises(NameError) { A.is_a?(Class) } + end + + def test_removing_class_in_one_level_namespace + assert X::B.is_a?(Class) + Class.remove_class(X::B) + assert_raises(NameError) { X::B.is_a?(Class) } + end + + def test_removing_class_in_two_level_namespace + assert Y::Z::C.is_a?(Class) + Class.remove_class(Y::Z::C) + assert_raises(NameError) { Y::Z::C.is_a?(Class) } + end +end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb new file mode 100644 index 0000000000..3d11f1001f --- /dev/null +++ b/activesupport/test/core_ext/module_test.rb @@ -0,0 +1,51 @@ +require 'test/unit' +require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/class' +require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/module' + +module One +end + +class Ab + include One +end + +module Xy + class Bc + include One + end +end + +module Yz + module Zy + class Cd + include One + end + end +end + +class De +end + +class ModuleTest < Test::Unit::TestCase + def test_included_in_classes + assert One.included_in_classes.include?(Ab) + assert One.included_in_classes.include?(Xy::Bc) + assert One.included_in_classes.include?(Yz::Zy::Cd) + assert !One.included_in_classes.include?(De) + end + + def test_remove_classes_including + assert Ab.is_a?(Class) + assert Xy::Bc.is_a?(Class) + assert Yz::Zy::Cd.is_a?(Class) + assert De.is_a?(Class) + + One.remove_classes_including + + assert_raises(NameError) { Ae.is_a?(Class) } + assert_raises(NameError) { Xy::Bc.is_a?(Class) } + assert_raises(NameError) { Yz::Zy::Cd.is_a?(Class) } + + assert De.is_a?(Class) + end +end \ No newline at end of file diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index fc19183ede..5d7a06d08d 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -1,5 +1,6 @@ require 'test/unit' -require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/object_and_class' +require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/object' +require File.dirname(__FILE__) + '/../../lib/active_support/core_ext/class' class ClassA; end class ClassB < ClassA; end diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index ab28c4668e..953a64a0ef 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -1,5 +1,6 @@ require 'test/unit' $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib/active_support/' +require 'core_ext/string' require 'dependencies' class DependenciesTest < Test::Unit::TestCase @@ -99,4 +100,20 @@ class DependenciesTest < Test::Unit::TestCase $LOAD_PATH.shift Dependencies.mechanism = old_mechanism end + + def test_as_load_path + assert_equal '', DependenciesTest.as_load_path + end + + def test_module_loading + $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/autoloading_fixtures" + old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load + + assert_kind_of Module, A + assert_kind_of Class, A::B + assert_kind_of Class, A::C::D + ensure + $LOAD_PATH.shift + Dependencies.mechanism = old_mechanism + end end diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb index baa2cd30f3..f5287d5725 100644 --- a/activesupport/test/option_merger_test.rb +++ b/activesupport/test/option_merger_test.rb @@ -2,7 +2,7 @@ require 'test/unit' unless defined? ActiveSupport::OptionMerger require File.dirname(__FILE__) + '/../lib/active_support/option_merger' - require File.dirname(__FILE__) + '/../lib/active_support/core_ext/object_and_class' + require File.dirname(__FILE__) + '/../lib/active_support/core_ext/object' end class OptionMergerTest < Test::Unit::TestCase diff --git a/railties/lib/dispatcher.rb b/railties/lib/dispatcher.rb index 29dae09b55..7f03294860 100644 --- a/railties/lib/dispatcher.rb +++ b/railties/lib/dispatcher.rb @@ -53,8 +53,7 @@ class Dispatcher Controllers.clear! Dependencies.clear ActiveRecord::Base.reset_subclasses - Dependencies.remove_subclasses_for(ActiveRecord::Base, ActiveRecord::Observer, ActionController::Base) - Dependencies.remove_subclasses_for(ActionMailer::Base) if defined?(ActionMailer::Base) + Reloadable.remove_classes_including end private -- cgit v1.2.3