Moved support from both Action Pack and Active Record into a separate module called Active Support that can be included using svn:externals in both
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@273 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
1b0da48fe9
commit
bd323b3c99
57
activesupport/lib/class_attribute_accessors.rb
Normal file
57
activesupport/lib/class_attribute_accessors.rb
Normal file
@ -0,0 +1,57 @@
|
||||
# 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.each do |sym|
|
||||
class_eval <<-EOS
|
||||
if ! defined? @@#{sym.id2name}
|
||||
@@#{sym.id2name} = nil
|
||||
end
|
||||
|
||||
def self.#{sym.id2name}
|
||||
@@#{sym}
|
||||
end
|
||||
|
||||
def #{sym.id2name}
|
||||
@@#{sym}
|
||||
end
|
||||
|
||||
def call_#{sym.id2name}
|
||||
case @@#{sym.id2name}
|
||||
when Symbol then send(@@#{sym})
|
||||
when Proc then @@#{sym}.call(self)
|
||||
when String then @@#{sym}
|
||||
else nil
|
||||
end
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def cattr_writer(*syms)
|
||||
syms.each do |sym|
|
||||
class_eval <<-EOS
|
||||
if ! defined? @@#{sym.id2name}
|
||||
@@#{sym.id2name} = nil
|
||||
end
|
||||
|
||||
def self.#{sym.id2name}=(obj)
|
||||
@@#{sym.id2name} = obj
|
||||
end
|
||||
|
||||
def self.set_#{sym.id2name}(obj)
|
||||
@@#{sym.id2name} = obj
|
||||
end
|
||||
|
||||
def #{sym.id2name}=(obj)
|
||||
@@#{sym} = obj
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def cattr_accessor(*syms)
|
||||
cattr_reader(*syms)
|
||||
cattr_writer(*syms)
|
||||
end
|
||||
end
|
41
activesupport/lib/class_inheritable_attributes.rb
Normal file
41
activesupport/lib/class_inheritable_attributes.rb
Normal file
@ -0,0 +1,41 @@
|
||||
# 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.
|
||||
module ClassInheritableAttributes # :nodoc:
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
@@classes ||= {}
|
||||
|
||||
def inheritable_attributes
|
||||
@@classes[self] ||= {}
|
||||
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 read_inheritable_attribute(key)
|
||||
inheritable_attributes[key]
|
||||
end
|
||||
|
||||
def reset_inheritable_attributes
|
||||
inheritable_attributes.clear
|
||||
end
|
||||
|
||||
private
|
||||
def inherited(child)
|
||||
@@classes[child] = inheritable_attributes.dup
|
||||
end
|
||||
|
||||
end
|
||||
end
|
10
activesupport/lib/clean_logger.rb
Normal file
10
activesupport/lib/clean_logger.rb
Normal file
@ -0,0 +1,10 @@
|
||||
require 'logger'
|
||||
|
||||
class Logger #:nodoc:
|
||||
private
|
||||
remove_const "Format"
|
||||
Format = "%s\n"
|
||||
def format_message(severity, timestamp, msg, progname)
|
||||
Format % [msg]
|
||||
end
|
||||
end
|
65
activesupport/lib/dependencies.rb
Normal file
65
activesupport/lib/dependencies.rb
Normal file
@ -0,0 +1,65 @@
|
||||
require 'action_controller/support/module_attribute_accessors'
|
||||
|
||||
module Dependencies
|
||||
extend self
|
||||
|
||||
@@loaded = [ ]
|
||||
mattr_accessor :loaded
|
||||
|
||||
@@mechanism = :load
|
||||
mattr_accessor :mechanism
|
||||
|
||||
def depend_on(file_name, swallow_load_errors = false)
|
||||
begin
|
||||
loaded << require_or_load(file_name) if !loaded.include?(file_name)
|
||||
rescue LoadError
|
||||
raise unless swallow_load_errors
|
||||
end
|
||||
end
|
||||
|
||||
def associate_with(file_name)
|
||||
depend_on(file_name, true)
|
||||
end
|
||||
|
||||
def reload
|
||||
loaded.each do |file_name|
|
||||
begin
|
||||
silence_warnings { load("#{file_name}.rb") }
|
||||
rescue LoadError
|
||||
# The association didn't reside in its own file, so we assume it was required by other means
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
self.loaded = [ ]
|
||||
end
|
||||
|
||||
private
|
||||
def require_or_load(file_name)
|
||||
mechanism == :load ? silence_warnings { load("#{file_name}.rb") } : require(file_name)
|
||||
return file_name
|
||||
end
|
||||
end
|
||||
|
||||
Object.send(:define_method, :require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency)
|
||||
Object.send(:define_method, :require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association)
|
||||
|
||||
class Object
|
||||
class << self
|
||||
# Use const_missing to autoload associations so we don't have to
|
||||
# require_association when using single-table inheritance.
|
||||
unless respond_to?(:pre_dependency_const_missing)
|
||||
alias_method :pre_dependency_const_missing, :const_missing
|
||||
|
||||
def const_missing(class_id)
|
||||
begin
|
||||
require_dependency(Inflector.underscore(Inflector.demodulize(class_id.to_s)))
|
||||
return Object.const_get(class_id) if Object.const_defined?(class_id)
|
||||
rescue LoadError
|
||||
pre_dependency_const_missing(class_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
80
activesupport/lib/inflector.rb
Normal file
80
activesupport/lib/inflector.rb
Normal file
@ -0,0 +1,80 @@
|
||||
# The Inflector transforms words from singular to plural, class names to table names, modulized class names to ones without,
|
||||
# and class names to foreign keys.
|
||||
module Inflector
|
||||
extend self
|
||||
|
||||
def pluralize(word)
|
||||
result = word.dup
|
||||
plural_rules.each do |(rule, replacement)|
|
||||
break if result.gsub!(rule, replacement)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
def singularize(word)
|
||||
result = word.dup
|
||||
singular_rules.each do |(rule, replacement)|
|
||||
break if result.gsub!(rule, replacement)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
def camelize(lower_case_and_underscored_word)
|
||||
lower_case_and_underscored_word.gsub(/(^|_)(.)/){$2.upcase}
|
||||
end
|
||||
|
||||
def underscore(camel_cased_word)
|
||||
camel_cased_word.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
||||
end
|
||||
|
||||
def demodulize(class_name_in_module)
|
||||
class_name_in_module.gsub(/^.*::/, '')
|
||||
end
|
||||
|
||||
def tableize(class_name)
|
||||
pluralize(underscore(class_name))
|
||||
end
|
||||
|
||||
def classify(table_name)
|
||||
camelize(singularize(table_name))
|
||||
end
|
||||
|
||||
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
|
||||
Inflector.underscore(Inflector.demodulize(class_name)) +
|
||||
(separate_class_name_and_id_with_underscore ? "_id" : "id")
|
||||
end
|
||||
|
||||
private
|
||||
def plural_rules #:doc:
|
||||
[
|
||||
[/(x|ch|ss)$/, '\1es'], # search, switch, fix, box, process, address
|
||||
[/([^aeiouy]|qu)ies$/, '\1y'],
|
||||
[/([^aeiouy]|qu)y$/, '\1ies'], # query, ability, agency
|
||||
[/(?:([^f])fe|([lr])f)$/, '\1\2ves'], # half, safe, wife
|
||||
[/sis$/, 'ses'], # basis, diagnosis
|
||||
[/([ti])um$/, '\1a'], # datum, medium
|
||||
[/person$/, 'people'], # person, salesperson
|
||||
[/man$/, 'men'], # man, woman, spokesman
|
||||
[/child$/, 'children'], # child
|
||||
[/s$/, 's'], # no change (compatibility)
|
||||
[/$/, 's']
|
||||
]
|
||||
end
|
||||
|
||||
def singular_rules #:doc:
|
||||
[
|
||||
[/(x|ch|ss)es$/, '\1'],
|
||||
[/movies$/, 'movie'],
|
||||
[/([^aeiouy]|qu)ies$/, '\1y'],
|
||||
[/([lr])ves$/, '\1f'],
|
||||
[/([^f])ves$/, '\1fe'],
|
||||
[/(analy|ba|diagno|parenthe|progno|synop|the)ses$/, '\1sis'],
|
||||
[/([ti])a$/, '\1um'],
|
||||
[/people$/, 'person'],
|
||||
[/men$/, 'man'],
|
||||
[/status$/, 'status'],
|
||||
[/children$/, 'child'],
|
||||
[/s$/, '']
|
||||
]
|
||||
end
|
||||
end
|
6
activesupport/lib/misc.rb
Normal file
6
activesupport/lib/misc.rb
Normal file
@ -0,0 +1,6 @@
|
||||
def silence_warnings
|
||||
old_verbose, $VERBOSE = $VERBOSE, nil
|
||||
result = yield
|
||||
$VERBOSE = old_verbose
|
||||
return result
|
||||
end
|
57
activesupport/lib/module_attribute_accessors.rb
Normal file
57
activesupport/lib/module_attribute_accessors.rb
Normal file
@ -0,0 +1,57 @@
|
||||
# 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
|
||||
if ! defined? @@#{sym.id2name}
|
||||
@@#{sym.id2name} = nil
|
||||
end
|
||||
|
||||
def self.#{sym.id2name}
|
||||
@@#{sym}
|
||||
end
|
||||
|
||||
def #{sym.id2name}
|
||||
@@#{sym}
|
||||
end
|
||||
|
||||
def call_#{sym.id2name}
|
||||
case @@#{sym.id2name}
|
||||
when Symbol then send(@@#{sym})
|
||||
when Proc then @@#{sym}.call(self)
|
||||
when String then @@#{sym}
|
||||
else nil
|
||||
end
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def mattr_writer(*syms)
|
||||
syms.each do |sym|
|
||||
class_eval <<-EOS
|
||||
if ! defined? @@#{sym.id2name}
|
||||
@@#{sym.id2name} = nil
|
||||
end
|
||||
|
||||
def self.#{sym.id2name}=(obj)
|
||||
@@#{sym.id2name} = obj
|
||||
end
|
||||
|
||||
def self.set_#{sym.id2name}(obj)
|
||||
@@#{sym.id2name} = obj
|
||||
end
|
||||
|
||||
def #{sym.id2name}=(obj)
|
||||
@@#{sym} = obj
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def mattr_accessor(*syms)
|
||||
mattr_reader(*syms)
|
||||
mattr_writer(*syms)
|
||||
end
|
||||
end
|
5
activesupport/test/dependencies/service_one.rb
Normal file
5
activesupport/test/dependencies/service_one.rb
Normal file
@ -0,0 +1,5 @@
|
||||
$loaded_service_one ||= 0
|
||||
$loaded_service_one += 1
|
||||
|
||||
class ServiceOne
|
||||
end
|
2
activesupport/test/dependencies/service_two.rb
Normal file
2
activesupport/test/dependencies/service_two.rb
Normal file
@ -0,0 +1,2 @@
|
||||
class ServiceTwo
|
||||
end
|
39
activesupport/test/dependencies_test.rb
Normal file
39
activesupport/test/dependencies_test.rb
Normal file
@ -0,0 +1,39 @@
|
||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
require 'action_controller/support/dependencies'
|
||||
|
||||
$LOAD_PATH << File.dirname(__FILE__) + '/../fixtures/dependencies'
|
||||
|
||||
class DependenciesTest < Test::Unit::TestCase
|
||||
def teardown
|
||||
Dependencies.clear
|
||||
end
|
||||
|
||||
def test_require_dependency
|
||||
require_dependency("service_one")
|
||||
require_dependency("service_two")
|
||||
assert_equal 2, Dependencies.loaded.size
|
||||
end
|
||||
|
||||
def test_require_dependency_two_times
|
||||
require_dependency("service_one")
|
||||
require_dependency("service_one")
|
||||
assert_equal 1, Dependencies.loaded.size
|
||||
end
|
||||
|
||||
def test_reloading_dependency
|
||||
require_dependency("service_one")
|
||||
require_dependency("service_one")
|
||||
assert_equal 1, $loaded_service_one
|
||||
|
||||
Dependencies.reload
|
||||
assert_equal 2, $loaded_service_one
|
||||
end
|
||||
|
||||
def test_require_missing_dependency
|
||||
assert_raises(LoadError) { require_dependency("missing_service") }
|
||||
end
|
||||
|
||||
def test_require_missing_association
|
||||
assert_nothing_raised { require_association("missing_model") }
|
||||
end
|
||||
end
|
122
activesupport/test/inflector_test.rb
Normal file
122
activesupport/test/inflector_test.rb
Normal file
@ -0,0 +1,122 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class InflectorTest < Test::Unit::TestCase
|
||||
SingularToPlural = {
|
||||
"search" => "searches",
|
||||
"switch" => "switches",
|
||||
"fix" => "fixes",
|
||||
"box" => "boxes",
|
||||
"process" => "processes",
|
||||
"address" => "addresses",
|
||||
"case" => "cases",
|
||||
"stack" => "stacks",
|
||||
|
||||
"category" => "categories",
|
||||
"query" => "queries",
|
||||
"ability" => "abilities",
|
||||
"agency" => "agencies",
|
||||
"movie" => "movies",
|
||||
|
||||
"wife" => "wives",
|
||||
"safe" => "saves",
|
||||
"half" => "halves",
|
||||
|
||||
"salesperson" => "salespeople",
|
||||
"person" => "people",
|
||||
|
||||
"spokesman" => "spokesmen",
|
||||
"man" => "men",
|
||||
"woman" => "women",
|
||||
|
||||
"basis" => "bases",
|
||||
"diagnosis" => "diagnoses",
|
||||
|
||||
"datum" => "data",
|
||||
"medium" => "media",
|
||||
|
||||
"node_child" => "node_children",
|
||||
"child" => "children",
|
||||
|
||||
"experience" => "experiences",
|
||||
"day" => "days",
|
||||
|
||||
"comment" => "comments",
|
||||
"foobar" => "foobars"
|
||||
}
|
||||
|
||||
CamelToUnderscore = {
|
||||
"Product" => "product",
|
||||
"SpecialGuest" => "special_guest",
|
||||
"ApplicationController" => "application_controller"
|
||||
}
|
||||
|
||||
ClassNameToForeignKeyWithUnderscore = {
|
||||
"Person" => "person_id",
|
||||
"MyApplication::Billing::Account" => "account_id"
|
||||
}
|
||||
|
||||
ClassNameToForeignKeyWithoutUnderscore = {
|
||||
"Person" => "personid",
|
||||
"MyApplication::Billing::Account" => "accountid"
|
||||
}
|
||||
|
||||
ClassNameToTableName = {
|
||||
"PrimarySpokesman" => "primary_spokesmen",
|
||||
"NodeChild" => "node_children"
|
||||
}
|
||||
|
||||
def test_pluralize
|
||||
SingularToPlural.each do |singular, plural|
|
||||
assert_equal(plural, Inflector.pluralize(singular))
|
||||
end
|
||||
|
||||
assert_equal("plurals", Inflector.pluralize("plurals"))
|
||||
end
|
||||
|
||||
def test_singularize
|
||||
SingularToPlural.each do |singular, plural|
|
||||
assert_equal(singular, Inflector.singularize(plural))
|
||||
end
|
||||
end
|
||||
|
||||
def test_camelize
|
||||
CamelToUnderscore.each do |camel, underscore|
|
||||
assert_equal(camel, Inflector.camelize(underscore))
|
||||
end
|
||||
end
|
||||
|
||||
def test_underscore
|
||||
CamelToUnderscore.each do |camel, underscore|
|
||||
assert_equal(underscore, Inflector.underscore(camel))
|
||||
end
|
||||
|
||||
assert_equal "html_tidy", Inflector.underscore("HTMLTidy")
|
||||
assert_equal "html_tidy_generator", Inflector.underscore("HTMLTidyGenerator")
|
||||
end
|
||||
|
||||
def test_demodulize
|
||||
assert_equal "Account", Inflector.demodulize("MyApplication::Billing::Account")
|
||||
end
|
||||
|
||||
def test_foreign_key
|
||||
ClassNameToForeignKeyWithUnderscore.each do |klass, foreign_key|
|
||||
assert_equal(foreign_key, Inflector.foreign_key(klass))
|
||||
end
|
||||
|
||||
ClassNameToForeignKeyWithoutUnderscore.each do |klass, foreign_key|
|
||||
assert_equal(foreign_key, Inflector.foreign_key(klass, false))
|
||||
end
|
||||
end
|
||||
|
||||
def test_tableize
|
||||
ClassNameToTableName.each do |class_name, table_name|
|
||||
assert_equal(table_name, Inflector.tableize(class_name))
|
||||
end
|
||||
end
|
||||
|
||||
def test_classify
|
||||
ClassNameToTableName.each do |class_name, table_name|
|
||||
assert_equal(class_name, Inflector.classify(table_name))
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user