aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb38
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb3
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb40
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb49
-rwxr-xr-xactiverecord/lib/active_record/associations.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb13
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb6
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb113
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb8
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb16
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb14
-rw-r--r--activeresource/lib/active_resource/base.rb11
-rw-r--r--activeresource/test/observing_test.rb53
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/interpolation.rb87
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb142
-rw-r--r--activesupport/lib/active_support/vendor.rb9
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb62
-rw-r--r--railties/html/404.html33
-rw-r--r--railties/html/422.html33
-rw-r--r--railties/html/500.html31
-rw-r--r--railties/html/index.html61
-rw-r--r--railties/lib/initializer.rb4
-rw-r--r--railties/lib/rails/configuration.rb40
-rw-r--r--railties/lib/rails/paths.rb40
-rw-r--r--railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb8
-rw-r--r--railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb3
-rw-r--r--railties/test/initializer/check_ruby_version_test.rb2
-rw-r--r--railties/test/initializer/install_gem_spec_stubs_test.rb29
-rw-r--r--railties/test/initializer/path_test.rb2
-rw-r--r--railties/test/initializer/test_helper.rb48
-rw-r--r--railties/test/paths_test.rb92
32 files changed, 705 insertions, 395 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 6d2c28f969..3fde79dfa4 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -462,12 +462,27 @@ module ActionView
# video_path("hd") # => /videos/hd
# video_path("hd.avi") # => /videos/hd.avi
# video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
- # video_path("/trailers/hd.avi") # => /videos/hd.avi
+ # video_path("/trailers/hd.avi") # => /trailers/hd.avi
# video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi
def video_path(source)
compute_public_path(source, 'videos')
end
- alias_method :path_to_video, :video_path # aliased to avoid conflicts with an video_path named route
+ alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
+
+ # Computes the path to an audio asset in the public audios directory.
+ # Full paths from the document root will be passed through.
+ # Used internally by +audio_tag+ to build the audio path.
+ #
+ # ==== Examples
+ # audio_path("horse") # => /audios/horse
+ # audio_path("horse.wav") # => /audios/horse.avi
+ # audio_path("sounds/horse.wav") # => /audios/sounds/horse.avi
+ # audio_path("/sounds/horse.wav") # => /sounds/horse.avi
+ # audio_path("http://www.railsapplication.com/sounds/horse.wav") # => http://www.railsapplication.com/sounds/horse.wav
+ def audio_path(source)
+ compute_public_path(source, 'audios')
+ end
+ alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
# Returns an html image tag for the +source+. The +source+ can be a full
# path or a file that exists in your public images directory.
@@ -542,7 +557,7 @@ module ActionView
# video_tag("trailer.ogg") # =>
# <video src="/videos/trailer.ogg" />
# video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
- # <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
+ # <video autobuffer="true" controls="true" src="/videos/trailer.ogg" />
# video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
# <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" />
# video_tag("/trailers/hd.avi", :size => "16x16") # =>
@@ -572,6 +587,23 @@ module ActionView
end
end
+ # Returns an html audio tag for the +source+.
+ # The +source+ can be full path or file that exists in
+ # your public audios directory.
+ #
+ # ==== Examples
+ # audio_tag("sound") # =>
+ # <audio src="/audios/sound" />
+ # audio_tag("sound.wav") # =>
+ # <audio src="/audios/sound.wav" />
+ # audio_tag("sound.wav", :autoplay => true, :controls => true) # =>
+ # <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
+ def audio_tag(source, options = {})
+ options.symbolize_keys!
+ options[:src] = path_to_audio(source)
+ tag("audio", options)
+ end
+
def self.cache_asset_timestamps
@@cache_asset_timestamps
end
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 9b6e9d201f..66d7592874 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -8,8 +8,7 @@ module ActionView
module TagHelper
include ERB::Util
- BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
- autoplay controls loop).to_set
+ BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attr| attr.to_sym })
# Returns an empty HTML tag of type +name+ which by default is XHTML
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index e7d70302f8..921bfeb93a 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -158,8 +158,8 @@ class AssetTagHelperTest < ActionView::TestCase
VideoLinkToTag = {
%(video_tag("xml.ogg")) => %(<video src="/videos/xml.ogg" />),
- %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="autoplay" controls="controls" src="/videos/rss.m4v" />),
- %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="autobuffer" src="/videos/rss.m4v" />),
+ %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="true" controls="true" src="/videos/rss.m4v" />),
+ %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="true" src="/videos/rss.m4v" />),
%(video_tag("gold.m4v", :size => "160x120")) => %(<video height="120" src="/videos/gold.m4v" width="160" />),
%(video_tag("gold.m4v", "size" => "320x240")) => %(<video height="240" src="/videos/gold.m4v" width="320" />),
%(video_tag("trailer.ogg", :poster => "screenshot.png")) => %(<video poster="/images/screenshot.png" src="/videos/trailer.ogg" />),
@@ -168,7 +168,27 @@ class AssetTagHelperTest < ActionView::TestCase
%(video_tag("error.avi", "size" => "x")) => %(<video src="/videos/error.avi" />),
%(video_tag("http://media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="http://media.rubyonrails.org/video/rails_blog_2.mov" />),
%(video_tag(["multiple.ogg", "multiple.avi"])) => %(<video><source src="multiple.ogg" /><source src="multiple.avi" /></video>),
- %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="controls" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>)
+ %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="true" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>)
+ }
+
+ AudioPathToTag = {
+ %(audio_path("xml")) => %(/audios/xml),
+ %(audio_path("xml.wav")) => %(/audios/xml.wav),
+ %(audio_path("dir/xml.wav")) => %(/audios/dir/xml.wav),
+ %(audio_path("/dir/xml.wav")) => %(/dir/xml.wav)
+ }
+
+ PathToAudioToTag = {
+ %(path_to_audio("xml")) => %(/audios/xml),
+ %(path_to_audio("xml.wav")) => %(/audios/xml.wav),
+ %(path_to_audio("dir/xml.wav")) => %(/audios/dir/xml.wav),
+ %(path_to_audio("/dir/xml.wav")) => %(/dir/xml.wav)
+ }
+
+ AudioLinkToTag = {
+ %(audio_tag("xml.wav")) => %(<audio src="/audios/xml.wav" />),
+ %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="true" controls="true" src="/audios/rss.wav" />),
+ %(audio_tag("http://media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="http://media.rubyonrails.org/audio/rails_blog_2.mov" />),
}
def test_auto_discovery_link_tag
@@ -311,6 +331,18 @@ class AssetTagHelperTest < ActionView::TestCase
VideoLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_audio_path
+ AudioPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_path_to_audio_alias_for_audio_path
+ PathToAudioToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_audio_tag
+ AudioLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_timebased_asset_id
expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
@@ -354,7 +386,7 @@ class AssetTagHelperTest < ActionView::TestCase
"#{request.protocol}assets#{source.length}.example.com"
end
end
-
+
ActionController::Base.perform_caching = true
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index d187859b44..7cdd281223 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -17,6 +17,15 @@ module ActiveModel
@value = compute_value
end
+ # There is a significant speed improvement if the value
+ # does not need to be escaped, as <tt>tag!</tt> escapes all values
+ # to ensure that valid XML is generated. For known binary
+ # values, it is at least an order of magnitude faster to
+ # Base64 encode binary values and directly put them in the
+ # output XML than to pass the original value or the Base64
+ # encoded value to the <tt>tag!</tt> method. It definitely makes
+ # no sense to Base64 encode the value and then give it to
+ # <tt>tag!</tt>, since that just adds additional overhead.
def needs_encoding?
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
end
@@ -105,28 +114,6 @@ module ActiveModel
end
end
- def add_attributes
- (serializable_attributes + serializable_method_attributes).each do |attribute|
- add_tag(attribute)
- end
- end
-
- def add_procs
- if procs = options.delete(:procs)
- [ *procs ].each do |proc|
- proc.call(options)
- end
- end
- end
-
- def add_tag(attribute)
- builder.tag!(
- reformat_name(attribute.name),
- attribute.value.to_s,
- attribute.decorations(!options[:skip_types])
- )
- end
-
def serialize
args = [root]
@@ -152,6 +139,24 @@ module ActiveModel
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
+
+ def add_attributes
+ (serializable_attributes + serializable_method_attributes).each do |attribute|
+ builder.tag!(
+ reformat_name(attribute.name),
+ attribute.value.to_s,
+ attribute.decorations(!options[:skip_types])
+ )
+ end
+ end
+
+ def add_procs
+ if procs = options.delete(:procs)
+ [ *procs ].each do |proc|
+ proc.call(options)
+ end
+ end
+ end
end
def to_xml(options = {}, &block)
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 97fdd9b1ba..31d3a89b9d 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1273,9 +1273,16 @@ module ActiveRecord
if send(reflection.name).loaded? || reflection.options[:finder_sql]
send(reflection.name).map(&:id)
else
- send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
+ if reflection.through_reflection && reflection.source_reflection.belongs_to?
+ through = reflection.through_reflection
+ primary_key = reflection.source_reflection.primary_key_name
+ send(through.name).all(:select => "DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
+ else
+ send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id)
+ end
end
end
+
end
def collection_accessor_methods(reflection, association_proxy_class, writer = true)
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 15358979c2..ecd2d57a5a 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -133,6 +133,7 @@ module ActiveRecord
end
private
+
# Suffixes a, ?, c become regexp /(a|\?|c)$/
def rebuild_attribute_method_regexp
suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
@@ -238,19 +239,17 @@ module ActiveRecord
def method_missing(method_id, *args, &block)
method_name = method_id.to_s
- if self.class.private_method_defined?(method_name)
- raise NoMethodError.new("Attempt to call private method", method_name, args)
- end
-
# If we haven't generated any methods yet, generate them, then
# see if we've created the method we're looking for.
if !self.class.generated_methods?
self.class.define_attribute_methods
+ guard_private_attribute_method!(method_name, args)
if self.class.generated_methods.include?(method_name)
return self.send(method_id, *args, &block)
end
end
+ guard_private_attribute_method!(method_name, args)
if self.class.primary_key.to_s == method_name
id
elsif md = self.class.match_attribute_method?(method_name)
@@ -371,6 +370,12 @@ module ActiveRecord
end
private
+ # prevent method_missing from calling private methods with #send
+ def guard_private_attribute_method!(method_name, args)
+ if self.class.private_method_defined?(method_name)
+ raise NoMethodError.new("Attempt to call private method", method_name, args)
+ end
+ end
def missing_attribute(attr_name, stack)
raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 2d90ef35aa..5d88012e4f 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -78,11 +78,14 @@ HEADER
begin
tbl = StringIO.new
+ # first dump primary key column
if @connection.respond_to?(:pk_and_sequence_for)
pk, pk_seq = @connection.pk_and_sequence_for(table)
+ elsif @connection.respond_to?(:primary_key)
+ pk = @connection.primary_key(table)
end
pk ||= 'id'
-
+
tbl.print " create_table #{table.inspect}"
if columns.detect { |c| c.name == pk }
if pk != 'id'
@@ -94,6 +97,7 @@ HEADER
tbl.print ", :force => true"
tbl.puts " do |t|"
+ # then dump all non-primary key columns
column_specs = columns.map do |column|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
next if column.name == pk
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index c3811caa53..253fa03785 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -164,42 +164,9 @@ module ActiveRecord #:nodoc:
end
end
- class XmlSerializer < ActiveModel::Serializer #:nodoc:
+ class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
include Serialization::RecordSerializer
- def builder
- @builder ||= begin
- require 'builder' unless defined? ::Builder
- options[:indent] ||= 2
- builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
-
- unless options[:skip_instruct]
- builder.instruct!
- options[:skip_instruct] = true
- end
-
- builder
- end
- end
-
- def root
- root = (options[:root] || @serializable.class.to_s.underscore).to_s
- reformat_name(root)
- end
-
- def dasherize?
- !options.has_key?(:dasherize) || options[:dasherize]
- end
-
- def camelize?
- options.has_key?(:camelize) && options[:camelize]
- end
-
- def reformat_name(name)
- name = name.camelize if camelize?
- dasherize? ? name.dasherize : name
- end
-
def serializable_attributes
serializable_attribute_names.collect { |name| Attribute.new(name, @serializable) }
end
@@ -211,28 +178,6 @@ module ActiveRecord #:nodoc:
end
end
- def add_attributes
- (serializable_attributes + serializable_method_attributes).each do |attribute|
- add_tag(attribute)
- end
- end
-
- def add_procs
- if procs = options.delete(:procs)
- [ *procs ].each do |proc|
- proc.call(options)
- end
- end
- end
-
- def add_tag(attribute)
- builder.tag!(
- reformat_name(attribute.name),
- attribute.value.to_s,
- attribute.decorations(!options[:skip_types])
- )
- end
-
def add_associations(association, records, opts)
if records.is_a?(Enumerable)
tag = reformat_name(association.to_s)
@@ -282,50 +227,10 @@ module ActiveRecord #:nodoc:
end
end
- class Attribute #:nodoc:
- attr_reader :name, :value, :type
-
- def initialize(name, record)
- @name, @record = name, record
-
- @type = compute_type
- @value = compute_value
- end
-
- # There is a significant speed improvement if the value
- # does not need to be escaped, as <tt>tag!</tt> escapes all values
- # to ensure that valid XML is generated. For known binary
- # values, it is at least an order of magnitude faster to
- # Base64 encode binary values and directly put them in the
- # output XML than to pass the original value or the Base64
- # encoded value to the <tt>tag!</tt> method. It definitely makes
- # no sense to Base64 encode the value and then give it to
- # <tt>tag!</tt>, since that just adds additional overhead.
- def needs_encoding?
- ![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
- end
-
- def decorations(include_types = true)
- decorations = {}
-
- if type == :binary
- decorations[:encoding] = 'base64'
- end
-
- if include_types && type != :string
- decorations[:type] = type
- end
-
- if value.nil?
- decorations[:nil] = true
- end
-
- decorations
- end
-
+ class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
protected
def compute_type
- type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
+ type = @serializable.class.serialized_attributes.has_key?(name) ? :yaml : @serializable.class.columns_hash[name].type
case type
when :text
@@ -336,22 +241,12 @@ module ActiveRecord #:nodoc:
type
end
end
-
- def compute_value
- value = @record.send(name)
-
- if formatter = Hash::XML_FORMATTING[type.to_s]
- value ? formatter.call(value) : nil
- else
- value
- end
- end
end
class MethodAttribute < Attribute #:nodoc:
protected
def compute_type
- Hash::XML_TYPE_NAMES[@record.send(name).class.name] || :string
+ Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string
end
end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 7a4712d7c8..8529ff0285 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -243,8 +243,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 2, people(:michael).jobs.size
end
- def test_get_ids
- assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
+ def test_get_ids_for_belongs_to_source
+ assert_sql(/DISTINCT/) { assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort }
+ end
+
+ def test_get_ids_for_has_many_source
+ assert_equal [comments(:eager_other_comment1).id], authors(:mary).comment_ids
end
def test_get_ids_for_loaded_associations
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 17ed302465..183be1e2f9 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -277,6 +277,22 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
end
+ def test_read_attribute_overwrites_private_method_not_considered_implemented
+ # simulate a model with a db column that shares its name an inherited
+ # private method (e.g. Object#system)
+ #
+ Object.class_eval do
+ private
+ def title; "private!"; end
+ end
+ assert !@target.instance_method_already_implemented?(:title)
+ topic = @target.new
+ assert_equal nil, topic.title
+
+ Object.send(:undef_method, :title) # remove test method from object
+ end
+
+
private
def time_related_columns_on_topic
Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 4f02be3c06..9612b0beb6 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -156,6 +156,13 @@ class SchemaDumperTest < ActiveRecord::TestCase
index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition
end
+
+ def test_schema_dump_should_honor_nonstandard_primary_keys
+ output = standard_dump
+ match = output.match(%r{create_table "movies"(.*)do})
+ assert_not_nil(match, "nonstandardpk table not found")
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
+ end
if current_adapter?(:MysqlAdapter)
def test_schema_dump_should_not_add_default_value_for_mysql_text_field
@@ -163,13 +170,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t.text\s+"body",\s+:null => false$}, output
end
- def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
- output = standard_dump
- match = output.match(%r{create_table "movies"(.*)do})
- assert_not_nil(match, "nonstandardpk table not found")
- assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
- end
-
def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
output = standard_dump
assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index f919f911e4..88a431a6d9 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -804,7 +804,8 @@ module ActiveResource
# my_company.size = 10
# my_company.save # sends PUT /companies/1 (update)
def save
- new? ? create : update
+ notify(:before_save)
+ (new? ? create : update).tap { notify(:after_save) }
end
# Deletes the resource from the remote service.
@@ -820,7 +821,8 @@ module ActiveResource
# new_person.destroy
# Person.find(new_id) # 404 (Resource Not Found)
def destroy
- connection.delete(element_path, self.class.headers)
+ notify(:before_destroy)
+ connection.delete(element_path, self.class.headers).tap { notify(:after_destroy) }
end
# Evaluates to <tt>true</tt> if this resource is not <tt>new?</tt> and is
@@ -995,16 +997,20 @@ module ActiveResource
# Update the resource on the remote service.
def update
+ notify(:before_update)
connection.put(element_path(prefix_options), encode, self.class.headers).tap do |response|
load_attributes_from_response(response)
+ notify(:after_update)
end
end
# Create (i.e., \save to the remote service) the \new resource.
def create
+ notify(:before_create)
connection.post(collection_path, encode, self.class.headers).tap do |response|
self.id = id_from_response(response)
load_attributes_from_response(response)
+ notify(:after_create)
end
end
@@ -1088,5 +1094,6 @@ module ActiveResource
class Base
extend ActiveModel::Naming
include CustomMethods, Validations
+ include ActiveModel::Observing
end
end
diff --git a/activeresource/test/observing_test.rb b/activeresource/test/observing_test.rb
new file mode 100644
index 0000000000..334b256772
--- /dev/null
+++ b/activeresource/test/observing_test.rb
@@ -0,0 +1,53 @@
+require 'abstract_unit'
+
+class ObservingTest < Test::Unit::TestCase
+ cattr_accessor :history
+
+ class PersonObserver < ActiveModel::Observer
+ observe :person
+
+ %w( after_create after_destroy after_save after_update
+ before_create before_destroy before_save before_update).each do |method|
+ define_method(method) { log method }
+ end
+
+ private
+ def log(method)
+ (ObservingTest.history ||= []) << method.to_sym
+ end
+ end
+
+ def setup
+ @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, @matz
+ mock.post "/people.xml", {}, @matz, 201, 'Location' => '/people/1.xml'
+ mock.put "/people/1.xml", {}, nil, 204
+ mock.delete "/people/1.xml", {}, nil, 200
+ end
+
+ PersonObserver.instance
+ end
+
+ def teardown
+ self.history = nil
+ end
+
+ def test_create_fires_save_and_create_notifications
+ rick = Person.create(:name => 'Rick')
+ assert_equal [:before_save, :before_create, :after_create, :after_save], self.history
+ end
+
+ def test_update_fires_save_and_update_notifications
+ person = Person.find(1)
+ person.save
+ assert_equal [:before_save, :before_update, :after_update, :after_save], self.history
+ end
+
+ def test_destroy_fires_destroy_notifications
+ person = Person.find(1)
+ person.destroy
+ assert_equal [:before_destroy, :after_destroy], self.history
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index 98ad75429e..d06a5a32fb 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -7,3 +7,4 @@ require 'active_support/core_ext/string/access'
require 'active_support/core_ext/string/iterators'
require 'active_support/core_ext/string/xchar'
require 'active_support/core_ext/string/behavior'
+require 'active_support/core_ext/string/interpolation' \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb
new file mode 100644
index 0000000000..b21977ecc1
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb
@@ -0,0 +1,87 @@
+if RUBY_VERSION < '1.9'
+
+=begin
+ string.rb - Extension for String.
+
+ Copyright (C) 2005-2009 Masao Mutoh
+
+ You may redistribute it and/or modify it under the same
+ license terms as Ruby.
+=end
+
+# This feature is included in Ruby 1.9 or later but not occur TypeError.
+#
+# String#% method which accepts named arguments. Particularly useful if the
+# string is to be used by a translator because named arguments mean more
+# than %s/%d style.
+class String
+
+ unless instance_methods.find {|m| m.to_s == 'bytesize'}
+ # For older ruby (such as ruby-1.8.5)
+ alias :bytesize :size
+ end
+
+ alias :_old_format_m :% # :nodoc:
+
+ PERCENT_MATCH_RE = Regexp.union(
+ /%%/,
+ /%\{(\w+)\}/,
+ /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/
+ )
+
+ # call-seq:
+ # %(arg)
+ # %(hash)
+ #
+ # Format - Uses str as a format specification, and returns the result of applying it to arg.
+ # If the format specification contains more than one substitution, then arg must be
+ # an Array containing the values to be substituted. See Kernel::sprintf for details of the
+ # format string. This is the default behavior of the String class.
+ # * arg: an Array or other class except Hash.
+ # * Returns: formatted String
+ # Example:
+ # "%s, %s" % ["Masao", "Mutoh"]
+ #
+ # Also you can use a Hash as the "named argument". This is recommended way so translators
+ # can understand the meanings of the msgids easily.
+ # * hash: {:key1 => value1, :key2 => value2, ... }
+ # * Returns: formatted String
+ # Example:
+ # For strings.
+ # "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"}
+ #
+ # With field type to specify format such as d(decimal), f(float),...
+ # "%<age>d, %<weight>.1f" % {:age => 10, :weight => 43.4}
+ def %(args)
+ if args.kind_of?(Hash)
+ ret = dup
+ ret.gsub!(PERCENT_MATCH_RE) {|match|
+ if match == '%%'
+ '%'
+ elsif $1
+ key = $1.to_sym
+ args.has_key?(key) ? args[key] : match
+ elsif $2
+ key = $2.to_sym
+ args.has_key?(key) ? sprintf("%#{$3}", args[key]) : match
+ end
+ }
+ ret
+ else
+ ret = gsub(/%([{<])/, '%%\1')
+ begin
+ ret._old_format_m(args)
+ rescue ArgumentError => e
+ if $DEBUG
+ $stderr.puts " The string:#{ret}"
+ $stderr.puts " args:#{args.inspect}"
+ puts e.backtrace
+ else
+ raise ArgumentError, e.message
+ end
+ end
+ end
+ end
+end
+
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index dd13abcd5d..30e194536b 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,94 +1,96 @@
-module ActiveSupport::Testing
- class ProxyTestResult
- def initialize
- @calls = []
- end
-
- def __replay__(result)
- @calls.each do |name, args|
- result.send(name, *args)
+module ActiveSupport
+ module Testing
+ class ProxyTestResult
+ def initialize
+ @calls = []
end
- end
-
- def method_missing(name, *args)
- @calls << [name, args]
- end
- end
- module Isolation
- def self.forking_env?
- !ENV["NO_FORK"] && RUBY_PLATFORM !~ /mswin|mingw|java/
- end
-
- def run(result)
- unless defined?(@@ran_class_setup)
- self.class.setup
- @@ran_class_setup = true
+ def __replay__(result)
+ @calls.each do |name, args|
+ result.send(name, *args)
+ end
end
- yield(Test::Unit::TestCase::STARTED, name)
-
- @_result = result
+ def method_missing(name, *args)
+ @calls << [name, args]
+ end
+ end
- proxy = run_in_isolation do |proxy|
- super(proxy) { }
+ module Isolation
+ def self.forking_env?
+ !ENV["NO_FORK"] && RUBY_PLATFORM !~ /mswin|mingw|java/
end
- proxy.__replay__(@_result)
+ def run(result)
+ unless defined?(@@ran_class_setup)
+ self.class.setup if self.class.respond_to?(:setup)
+ @@ran_class_setup = true
+ end
- yield(Test::Unit::TestCase::FINISHED, name)
- end
+ yield(Test::Unit::TestCase::STARTED, name)
- module Forking
- def run_in_isolation(&blk)
- read, write = IO.pipe
+ @_result = result
- pid = fork do
- read.close
- proxy = ProxyTestResult.new
- yield proxy
- write.puts [Marshal.dump(proxy)].pack("m")
- exit!
+ proxy = run_in_isolation do |proxy|
+ super(proxy) { }
end
- write.close
- result = read.read
- Process.wait2(pid)
- Marshal.load(result.unpack("m")[0])
+ proxy.__replay__(@_result)
+
+ yield(Test::Unit::TestCase::FINISHED, name)
end
- end
- module Subprocess
- # Crazy H4X to get this working in windows / jruby with
- # no forking.
- def run_in_isolation(&blk)
- require "tempfile"
-
- if ENV["ISOLATION_TEST"]
- proxy = ProxyTestResult.new
- yield proxy
- File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
- file.puts [Marshal.dump(proxy)].pack("m")
- end
- exit!
- else
- Tempfile.open("isolation") do |tmpfile|
- ENV["ISOLATION_TEST"] = @method_name
- ENV["ISOLATION_OUTPUT"] = tmpfile.path
+ module Forking
+ def run_in_isolation(&blk)
+ read, write = IO.pipe
- load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
- `#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
+ pid = fork do
+ read.close
+ proxy = ProxyTestResult.new
+ yield proxy
+ write.puts [Marshal.dump(proxy)].pack("m")
+ exit!
+ end
- ENV.delete("ISOLATION_TEST")
- ENV.delete("ISOLATION_OUTPUT")
+ write.close
+ result = read.read
+ Process.wait2(pid)
+ Marshal.load(result.unpack("m")[0])
+ end
+ end
- return Marshal.load(tmpfile.read.unpack("m")[0])
+ module Subprocess
+ # Crazy H4X to get this working in windows / jruby with
+ # no forking.
+ def run_in_isolation(&blk)
+ require "tempfile"
+
+ if ENV["ISOLATION_TEST"]
+ proxy = ProxyTestResult.new
+ yield proxy
+ File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
+ file.puts [Marshal.dump(proxy)].pack("m")
+ end
+ exit!
+ else
+ Tempfile.open("isolation") do |tmpfile|
+ ENV["ISOLATION_TEST"] = @method_name
+ ENV["ISOLATION_OUTPUT"] = tmpfile.path
+
+ load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
+ `#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
+
+ ENV.delete("ISOLATION_TEST")
+ ENV.delete("ISOLATION_OUTPUT")
+
+ return Marshal.load(tmpfile.read.unpack("m")[0])
+ end
end
end
end
- end
- include forking_env? ? Forking : Subprocess
+ include forking_env? ? Forking : Subprocess
+ end
end
end
diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb
index b6223fe20a..ca64c5ebe3 100644
--- a/activesupport/lib/active_support/vendor.rb
+++ b/activesupport/lib/active_support/vendor.rb
@@ -19,10 +19,9 @@ rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.13"
end
-# TODO I18n gem has not been released yet
-# begin
-# gem 'i18n', '~> 0.1.3'
-# rescue Gem::LoadError
+begin
+ gem 'i18n', '~> 0.1.3'
+rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.3/lib"
require 'i18n'
-# end
+end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 6991b174b7..f77ad5236e 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -280,3 +280,65 @@ class CoreExtStringMultibyteTest < ActiveSupport::TestCase
end
end
end
+
+=begin
+ string.rb - Interpolation for String.
+
+ Copyright (C) 2005-2009 Masao Mutoh
+
+ You may redistribute it and/or modify it under the same
+ license terms as Ruby.
+=end
+class TestGetTextString < Test::Unit::TestCase
+ def test_sprintf
+ assert_equal("foo is a number", "%{msg} is a number" % {:msg => "foo"})
+ assert_equal("bar is a number", "%s is a number" % ["bar"])
+ assert_equal("bar is a number", "%s is a number" % "bar")
+ assert_equal("1, test", "%{num}, %{record}" % {:num => 1, :record => "test"})
+ assert_equal("test, 1", "%{record}, %{num}" % {:num => 1, :record => "test"})
+ assert_equal("1, test", "%d, %s" % [1, "test"])
+ assert_equal("test, 1", "%2$s, %1$d" % [1, "test"])
+ assert_raise(ArgumentError) { "%-%" % [1] }
+ end
+
+ def test_percent
+ assert_equal("% 1", "%% %<num>d" % {:num => 1.0})
+ assert_equal("%{num} %<num>d", "%%{num} %%<num>d" % {:num => 1})
+ end
+
+ def test_sprintf_percent_in_replacement
+ assert_equal("%<not_translated>s", "%{msg}" % { :msg => '%<not_translated>s', :not_translated => 'should not happen' })
+ end
+
+ def test_sprintf_lack_argument
+ assert_equal("%{num}, test", "%{num}, %{record}" % {:record => "test"})
+ assert_equal("%{record}", "%{record}" % {:num => 1})
+ end
+
+ def test_no_placeholder
+ assert_equal("aaa", "aaa" % {:num => 1})
+ assert_equal("bbb", "bbb" % [1])
+ end
+
+ def test_sprintf_ruby19_style
+ assert_equal("1", "%<num>d" % {:num => 1})
+ assert_equal("0b1", "%<num>#b" % {:num => 1})
+ assert_equal("foo", "%<msg>s" % {:msg => "foo"})
+ assert_equal("1.000000", "%<num>f" % {:num => 1.0})
+ assert_equal(" 1", "%<num>3.0f" % {:num => 1.0})
+ assert_equal("100.00", "%<num>2.2f" % {:num => 100.0})
+ assert_equal("0x64", "%<num>#x" % {:num => 100.0})
+ assert_raise(ArgumentError) { "%<num>,d" % {:num => 100} }
+ assert_raise(ArgumentError) { "%<num>/d" % {:num => 100} }
+ end
+
+ def test_sprintf_old_style
+ assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0])
+ end
+
+ def test_sprintf_mix
+ assert_equal("foo 1.000000", "%{name} %<num>f" % {:name => "foo", :num => 1.0})
+ assert_equal("%{name} 1.000000", "%{name} %f" % [1.0])
+ assert_equal("%{name} 1.000000", "%{name} %f" % [1.0, 2.0])
+ end
+end
diff --git a/railties/html/404.html b/railties/html/404.html
index eff660b90c..88ee108e90 100644
--- a/railties/html/404.html
+++ b/railties/html/404.html
@@ -1,23 +1,20 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
+<!DOCTYPE html>
+<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>The page you were looking for doesn't exist (404)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
</head>
<body>
@@ -27,4 +24,4 @@
<p>You may have mistyped the address or the page may have moved.</p>
</div>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/railties/html/422.html b/railties/html/422.html
index b54e4a3cad..9c3c96670b 100644
--- a/railties/html/422.html
+++ b/railties/html/422.html
@@ -1,23 +1,20 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
+<!DOCTYPE html>
+<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>The change you wanted was rejected (422)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
</head>
<body>
@@ -27,4 +24,4 @@
<p>Maybe you tried to change something you didn't have access to.</p>
</div>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/railties/html/500.html b/railties/html/500.html
index ec3bbf02c4..f71c86e652 100644
--- a/railties/html/500.html
+++ b/railties/html/500.html
@@ -1,23 +1,20 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
+<!DOCTYPE html>
+<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>We're sorry, but something went wrong (500)</title>
- <style type="text/css">
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
- div.dialog {
- width: 25em;
- padding: 0 4em;
- margin: 4em auto 0 auto;
- border: 1px solid #ccc;
- border-right-color: #999;
- border-bottom-color: #999;
- }
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
- </style>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
</head>
<body>
diff --git a/railties/html/index.html b/railties/html/index.html
index cd337dc74c..ff2dfd3193 100644
--- a/railties/html/index.html
+++ b/railties/html/index.html
@@ -1,5 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
@@ -14,20 +13,20 @@
font-size: 13px;
color: #333;
}
-
+
h1 {
font-size: 28px;
color: #000;
}
-
+
a {color: #03c}
a:hover {
background-color: #03c;
color: white;
text-decoration: none;
}
-
-
+
+
#page {
background-color: #f0f0f0;
width: 750px;
@@ -35,7 +34,7 @@
margin-left: auto;
margin-right: auto;
}
-
+
#content {
float: left;
background-color: white;
@@ -44,7 +43,7 @@
padding: 25px;
width: 500px;
}
-
+
#sidebar {
float: right;
width: 175px;
@@ -53,7 +52,7 @@
#footer {
clear: both;
}
-
+
#header, #about, #getting-started {
padding-left: 75px;
@@ -73,14 +72,14 @@
font-weight: normal;
font-size: 16px;
}
-
-
+
+
#about h3 {
margin: 0;
margin-bottom: 10px;
font-size: 14px;
}
-
+
#about-content {
background-color: #ffd;
border: 1px solid #fc0;
@@ -113,8 +112,8 @@
margin: 0;
padding: 10px;
}
-
-
+
+
#getting-started {
border-top: 1px solid #ccc;
margin-top: 25px;
@@ -150,8 +149,8 @@
color: #555;
font-size: 13px;
}
-
-
+
+
#search {
margin: 0;
padding-top: 10px;
@@ -163,8 +162,8 @@
margin: 2px;
}
#search-text {width: 170px}
-
-
+
+
#sidebar ul {
margin-left: 0;
padding-left: 0;
@@ -181,7 +180,7 @@
#sidebar ul.links li {
margin-bottom: 5px;
}
-
+
</style>
<script type="text/javascript" src="javascripts/prototype.js"></script>
<script type="text/javascript" src="javascripts/effects.js"></script>
@@ -194,11 +193,11 @@
onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
});
} else {
- new Effect[Element.visible('about-content') ?
+ new Effect[Element.visible('about-content') ?
'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
}
}
-
+
window.onload = function() {
$('search-text').value = '';
$('search').onsubmit = function() {
@@ -218,7 +217,7 @@
<input type="submit" value="Search" /> the Rails site
</form>
</li>
-
+
<li>
<h3>Join the community</h3>
<ul class="links">
@@ -227,7 +226,7 @@
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
</ul>
</li>
-
+
<li>
<h3>Browse the documentation</h3>
<ul class="links">
@@ -250,17 +249,17 @@
<h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
<div id="about-content" style="display: none"></div>
</div>
-
+
<div id="getting-started">
<h1>Getting started</h1>
<h2>Here&rsquo;s how to get rolling:</h2>
-
- <ol>
+
+ <ol>
<li>
- <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
+ <h2>Use <code>script/generate</code> to create your models and controllers</h2>
<p>To see all available options, run it without parameters.</p>
</li>
-
+
<li>
<h2>Set up a default route and remove or rename this file</h2>
<p>Routes are set up in config/routes.rb.</p>
@@ -268,13 +267,13 @@
<li>
<h2>Create your database</h2>
- <p>Run <tt>rake db:migrate</tt> to create your database. If you're not using SQLite (the default), edit <tt>config/database.yml</tt> with your username and password.</p>
+ <p>Run <code>rake db:migrate</code> to create your database. If you're not using SQLite (the default), edit <code>config/database.yml</code> with your username and password.</p>
</li>
</ol>
</div>
</div>
-
+
<div id="footer">&nbsp;</div>
</div>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index 560105670f..f0fb78c8f4 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -12,6 +12,10 @@ require 'rails/configuration'
RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
module Rails
+ # Sanity check to make sure this file is only loaded once
+ # TODO: Get to the point where this can be removed.
+ raise "It looks like initializer.rb was required twice" if defined?(Initializer)
+
class Initializer
class Error < StandardError ; end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index d877915460..1a2f217d20 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -62,33 +62,19 @@ module Rails
end
@paths = Rails::Application::Root.new(root_path)
- @paths.app = "app"
- @paths.app.metals = "app/metal"
- @paths.app.models = "app/models"
- @paths.app.controllers = "app/controllers"
- @paths.app.helpers = "app/helpers"
- @paths.app.services = "app/services"
- @paths.lib = "lib"
- @paths.vendor = "vendor"
- @paths.vendor.plugins = "vendor/plugins"
- @paths.cache = "tmp/cache"
- @paths.config = "config"
- @paths.config.locales = "config/locales"
- @paths.config.environments = "config/environments"
-
- @paths.app.controllers.concat builtin_directories
-
- @paths.app.load_path!
- @paths.app.metals.load_path!
- @paths.app.models.eager_load!
- @paths.app.controllers.eager_load!
- @paths.app.helpers.eager_load!
- @paths.app.services.load_path!
- @paths.app.metals.eager_load!
- @paths.lib.load_path!
- @paths.vendor.load_path!
-
- @paths.config.environments.glob = "#{RAILS_ENV}.rb"
+ @paths.app "app", :load_path => true
+ @paths.app.metals "app/metal", :eager_load => true
+ @paths.app.models "app/models", :eager_load => true
+ @paths.app.controllers "app/controllers", builtin_directories, :eager_load => true
+ @paths.app.helpers "app/helpers", :eager_load => true
+ @paths.app.services "app/services", :load_path => true
+ @paths.lib "lib", :load_path => true
+ @paths.vendor "vendor", :load_path => true
+ @paths.vendor.plugins "vendor/plugins"
+ @paths.cache "tmp/cache"
+ @paths.config "config"
+ @paths.config.locales "config/locales"
+ @paths.config.environments "config/environments", :glob => "#{RAILS_ENV}.rb"
RAILS_ROOT.replace root_path
end
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index d2f6d83659..3899b744b0 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -6,8 +6,8 @@ module Rails
def method_missing(id, *args)
name = id.to_s
- if name =~ /^(.*)=$/
- @children[$1] = Path.new(args.first, @root)
+ if name =~ /^(.*)=$/ || args.any?
+ @children[$1 || name] = Path.new(@root, *args)
elsif path = @children[name]
path
else
@@ -28,17 +28,15 @@ module Rails
# TODO: Move logic from set_root_path initializer
@path = File.expand_path(path)
@root = self
- @load_once, @eager_load, @all_paths = [], [], []
+ @all_paths = []
end
def load_once
- @load_once.uniq!
- @load_once
+ all_paths.map { |path| path.paths if path.load_once? }.compact.flatten.uniq
end
def eager_load
- @eager_load.uniq!
- @eager_load
+ all_paths.map { |path| path.paths if path.eager_load? }.compact.flatten.uniq
end
def all_paths
@@ -47,7 +45,7 @@ module Rails
end
def load_paths
- all_paths.map { |path| path.paths }.flatten
+ all_paths.map { |path| path.paths if path.load_path? }.compact.flatten.uniq
end
def add_to_load_path
@@ -55,6 +53,14 @@ module Rails
$LOAD_PATH.unshift(path) if File.directory?(path)
end
end
+
+ def push(*)
+ raise "Application root can only have one physical path"
+ end
+
+ alias unshift push
+ alias << push
+ alias concat push
end
class Path
@@ -63,11 +69,18 @@ module Rails
attr_reader :path
attr_accessor :glob
- def initialize(path, root)
+ def initialize(root, *paths)
+ @options = paths.last.is_a?(::Hash) ? paths.pop : {}
@children = {}
@root = root
- @paths = [path].flatten
- @glob = "**/*.rb"
+ @paths = paths.flatten
+ @glob = @options[:glob] || "**/*.rb"
+
+ @load_once = @options[:load_once]
+ @eager_load = @options[:eager_load]
+ @load_path = @options[:load_path] || @eager_load
+
+ @root.all_paths << self
end
def push(path)
@@ -86,7 +99,6 @@ module Rails
def load_once!
@load_once = true
- @root.load_once.push *self.paths
end
def load_once?
@@ -95,8 +107,7 @@ module Rails
def eager_load!
@eager_load = true
- @root.all_paths << self
- @root.eager_load.push *self.paths
+ @load_path = true
end
def eager_load?
@@ -105,7 +116,6 @@ module Rails
def load_path!
@load_path = true
- @root.all_paths << self
end
def load_path?
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb b/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
index ebc97f8130..aacfbe4a8f 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
+++ b/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
@@ -1,9 +1,7 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<!DOCTYPE html>
+<html>
<head>
- <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title><%= controller_class_name %>: <%%= controller.action_name %></title>
<%%= stylesheet_link_tag 'scaffold' %>
</head>
diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb b/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb
index 2e603d5b4a..69beb388db 100644
--- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb
+++ b/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb
@@ -5,6 +5,9 @@
<% for attribute in attributes -%>
<th><%= attribute.column.human_name %></th>
<% end -%>
+ <th></th>
+ <th></th>
+ <th></th>
</tr>
<%% @<%= plural_name %>.each do |<%= singular_name %>| %>
diff --git a/railties/test/initializer/check_ruby_version_test.rb b/railties/test/initializer/check_ruby_version_test.rb
index 33de653906..68feba058e 100644
--- a/railties/test/initializer/check_ruby_version_test.rb
+++ b/railties/test/initializer/check_ruby_version_test.rb
@@ -1,7 +1,7 @@
require "initializer/test_helper"
module InitializerTests
- class PathsTest < ActiveSupport::TestCase
+ class PathsTest < Test::Unit::TestCase
include ActiveSupport::Testing::Isolation
test "rails does not initialize with ruby version 1.8.1" do
diff --git a/railties/test/initializer/install_gem_spec_stubs_test.rb b/railties/test/initializer/install_gem_spec_stubs_test.rb
index 2e94c9968f..cfb12d7405 100644
--- a/railties/test/initializer/install_gem_spec_stubs_test.rb
+++ b/railties/test/initializer/install_gem_spec_stubs_test.rb
@@ -1,7 +1,7 @@
require "initializer/test_helper"
module InitializerTests
- class GemSpecStubsTest < ActiveSupport::TestCase
+ class GemSpecStubsTest < Test::Unit::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -34,19 +34,20 @@ module InitializerTests
assert $rubygems_required
end
- test "does not fail if rubygems does not exist" do
- Kernel.module_eval do
- alias old_require require
- def require(name)
- raise LoadError if name == "rubygems"
- old_require(name)
- end
- end
-
- assert_nothing_raised do
- Rails::Initializer.run { |c| c.frameworks = [] }
- end
- end
+ # Pending until we're further along
+ # test "does not fail if rubygems does not exist" do
+ # Kernel.module_eval do
+ # alias old_require require
+ # def require(name)
+ # raise LoadError if name == "rubygems"
+ # old_require(name)
+ # end
+ # end
+ #
+ # assert_nothing_raised do
+ # Rails::Initializer.run { |c| c.frameworks = [] }
+ # end
+ # end
test "adds fake Rubygems stubs if a framework is not loaded in Rubygems and we've vendored" do
Rails.vendor_rails = true
diff --git a/railties/test/initializer/path_test.rb b/railties/test/initializer/path_test.rb
index 26f796f93d..1b73cdc73e 100644
--- a/railties/test/initializer/path_test.rb
+++ b/railties/test/initializer/path_test.rb
@@ -1,6 +1,6 @@
require "initializer/test_helper"
-class PathsTest < ActiveSupport::TestCase
+class PathsTest < Test::Unit::TestCase
include ActiveSupport::Testing::Isolation
def self.setup
diff --git a/railties/test/initializer/test_helper.rb b/railties/test/initializer/test_helper.rb
index ddb03397ab..9d7dfff1c0 100644
--- a/railties/test/initializer/test_helper.rb
+++ b/railties/test/initializer/test_helper.rb
@@ -1,17 +1,18 @@
-require 'abstract_unit'
-require 'active_support/ruby/shim'
-require 'initializer'
+# This is a test helper file that simulates a rails application being
+# boot from scratch in vendored mode. This file should really only be
+# required in test cases that use the isolation helper so that requires
+# can be reset correctly.
+RAILS_ROOT = File.join(File.dirname(__FILE__), "root")
+RAILS_FRAMEWORK_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..'))
-RAILS_ROOT.replace File.join(File.dirname(__FILE__), "root")
+require "test/unit"
+# We are purposely avoiding adding things to the load path to catch bugs that only happen in the genuine article
+require File.join(RAILS_FRAMEWORK_ROOT, 'activesupport', 'lib', 'active_support', 'testing', 'isolation')
+require File.join(RAILS_FRAMEWORK_ROOT, 'activesupport', 'lib', 'active_support', 'testing', 'declarative')
-module Rails
- class << self
- attr_accessor :vendor_rails
- def vendor_rails?() @vendor_rails end
- end
-end
+class Test::Unit::TestCase
+ extend ActiveSupport::Testing::Declarative
-class ActiveSupport::TestCase
def assert_stderr(match)
$stderr = StringIO.new
yield
@@ -21,4 +22,27 @@ class ActiveSupport::TestCase
ensure
$stderr = STDERR
end
-end \ No newline at end of file
+end
+
+# Fake boot.rb
+module Rails
+ class << self
+ attr_accessor :vendor_rails
+
+ def vendor_rails?
+ @vendor_rails
+ end
+
+ def boot!
+ # Require the initializer
+ require File.join(RAILS_FRAMEWORK_ROOT, 'railties', 'lib', 'initializer')
+ # Run the initializer the same way boot.rb does it
+ Rails::Initializer.run(:install_gem_spec_stubs)
+ Rails::GemDependency.add_frozen_gem_path
+ Rails::Initializer.run(:set_load_path)
+ end
+ end
+end
+
+# All that for this:
+Rails.boot! \ No newline at end of file
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index fa2f6ceee2..d50882110f 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -17,17 +17,37 @@ class PathsTest < ActiveSupport::TestCase
assert_equal ["/foo/bar"], @root.app.to_a
end
+ test "creating a root level path without assignment" do
+ @root.app "/foo/bar"
+ assert_equal ["/foo/bar"], @root.app.to_a
+ end
+
+ test "trying to access a path that does not exist raises NoMethodError" do
+ assert_raises(NoMethodError) { @root.app }
+ end
+
test "relative paths are relative to the paths root" do
@root.app = "app"
assert_equal ["/foo/bar/app"], @root.app.to_a
end
+ test "relative paths are relative to the paths root without assignment" do
+ @root.app "app"
+ assert_equal ["/foo/bar/app"], @root.app.to_a
+ end
+
test "creating a child level path" do
@root.app = "/foo/bar"
@root.app.models = "/foo/bar/baz"
assert_equal ["/foo/bar/baz"], @root.app.models.to_a
end
+ test "creating a child level path without assignment" do
+ @root.app = "/foo/bar"
+ @root.app.models "/foo/bar/baz"
+ assert_equal ["/foo/bar/baz"], @root.app.models.to_a
+ end
+
test "child level paths are relative from the root" do
@root.app = "/app"
@root.app.models = "baz"
@@ -40,6 +60,11 @@ class PathsTest < ActiveSupport::TestCase
assert_equal ["/app", "/app2"], @root.app.to_a
end
+ test "adding multiple physical paths as an array without assignment" do
+ @root.app "/app", "/app2"
+ assert_equal ["/app", "/app2"], @root.app.to_a
+ end
+
test "adding multiple physical paths using #push" do
@root.app = "/app"
@root.app.push "/app2"
@@ -66,10 +91,10 @@ class PathsTest < ActiveSupport::TestCase
test "the root can only have one physical path" do
assert_raise(RuntimeError) { Rails::Application::Root.new(["/fiz", "/biz"]) }
- assert_raise(NoMethodError) { @root.push "/biz" }
- assert_raise(NoMethodError) { @root.unshift "/biz" }
- assert_raise(NoMethodError) { @root.concat ["/biz"]}
- assert_raise(NoMethodError) { @root << "/biz" }
+ assert_raise(RuntimeError) { @root.push "/biz" }
+ assert_raise(RuntimeError) { @root.unshift "/biz" }
+ assert_raise(RuntimeError) { @root.concat ["/biz"]}
+ assert_raise(RuntimeError) { @root << "/biz" }
end
test "it is possible to add a path that should be loaded only once" do
@@ -79,6 +104,19 @@ class PathsTest < ActiveSupport::TestCase
assert @root.load_once.include?(@root.app.paths.first)
end
+ test "it is possible to add a path without assignment and specify it should be loaded only once" do
+ @root.app "/app", :load_once => true
+ assert @root.app.load_once?
+ assert @root.load_once.include?("/app")
+ end
+
+ test "it is possible to add multiple paths without assignment and specify it should be loaded only once" do
+ @root.app "/app", "/app2", :load_once => true
+ assert @root.app.load_once?
+ assert @root.load_once.include?("/app")
+ assert @root.load_once.include?("/app2")
+ end
+
test "making a path load_once more than once only includes it once in @root.load_once" do
@root.app = "/app"
@root.app.load_once!
@@ -86,6 +124,13 @@ class PathsTest < ActiveSupport::TestCase
assert_equal 1, @root.load_once.select {|p| p == @root.app.paths.first }.size
end
+ test "paths added to a load_once path should be added to the load_once collection" do
+ @root.app = "/app"
+ @root.app.load_once!
+ @root.app << "/app2"
+ assert_equal 2, @root.load_once.size
+ end
+
test "it is possible to mark a path as eager" do
@root.app = "/app"
@root.app.eager_load!
@@ -93,6 +138,27 @@ class PathsTest < ActiveSupport::TestCase
assert @root.eager_load.include?(@root.app.paths.first)
end
+ test "it is possible to add a path without assignment and mark it as eager" do
+ @root.app "/app", :eager_load => true
+ assert @root.app.eager_load?
+ assert @root.eager_load.include?("/app")
+ end
+
+ test "it is possible to add multiple paths without assignment and mark them as eager" do
+ @root.app "/app", "/app2", :eager_load => true
+ assert @root.app.eager_load?
+ assert @root.eager_load.include?("/app")
+ assert @root.eager_load.include?("/app2")
+ end
+
+ test "it is possible to create a path without assignment and mark it both as eager and load once" do
+ @root.app "/app", :eager_load => true, :load_once => true
+ assert @root.app.eager_load?
+ assert @root.app.load_once?
+ assert @root.eager_load.include?("/app")
+ assert @root.load_once.include?("/app")
+ end
+
test "making a path eager more than once only includes it once in @root.eager_paths" do
@root.app = "/app"
@root.app.eager_load!
@@ -100,6 +166,13 @@ class PathsTest < ActiveSupport::TestCase
assert_equal 1, @root.eager_load.select {|p| p == @root.app.paths.first }.size
end
+ test "paths added to a eager_load path should be added to the eager_load collection" do
+ @root.app = "/app"
+ @root.app.eager_load!
+ @root.app << "/app2"
+ assert_equal 2, @root.eager_load.size
+ end
+
test "a path should have a glob that defaults to **/*.rb" do
@root.app = "/app"
assert_equal "**/*.rb", @root.app.glob
@@ -111,6 +184,11 @@ class PathsTest < ActiveSupport::TestCase
assert_equal "*.rb", @root.app.glob
end
+ test "it should be possible to override a path's default glob without assignment" do
+ @root.app "/app", :glob => "*.rb"
+ assert_equal "*.rb", @root.app.glob
+ end
+
test "a path can be added to the load path" do
@root.app = "app"
@root.app.load_path!
@@ -118,6 +196,12 @@ class PathsTest < ActiveSupport::TestCase
assert_equal ["/foo/bar/app"], @root.load_paths
end
+ test "a path can be added to the load path on creation" do
+ @root.app "/app", :load_path => true
+ assert @root.app.load_path?
+ assert_equal ["/app"], @root.load_paths
+ end
+
test "adding a path to the eager paths also adds it to the load path" do
@root.app = "app"
@root.app.eager_load!