aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/metal.rb27
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb2
-rw-r--r--actionpack/test/controller/url_for_test.rb15
-rw-r--r--actionpack/test/dispatch/routing_test.rb62
-rw-r--r--actionpack/test/fixtures/test/_layout_with_partial_and_yield.html.erb4
-rw-r--r--actionpack/test/template/form_helper_test.rb12
-rw-r--r--actionpack/test/template/render_test.rb5
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb16
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb30
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/base.rb46
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb59
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb62
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb18
-rw-r--r--activerecord/lib/active_record/railtie.rb1
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/result.rb4
-rw-r--r--activerecord/lib/active_record/session_store.rb4
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb36
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb11
-rw-r--r--activerecord/test/cases/base_test.rb27
-rw-r--r--activerecord/test/cases/calculations_test.rb6
-rw-r--r--activerecord/test/cases/coders/yaml_column_test.rb5
-rw-r--r--activerecord/test/cases/connection_pool_test.rb64
-rw-r--r--activerecord/test/cases/habtm_destroy_order_test.rb17
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb5
-rw-r--r--activerecord/test/models/contact.rb12
-rw-r--r--activerecord/test/models/lesson.rb11
-rw-r--r--activerecord/test/models/student.rb3
-rw-r--r--activerecord/test/schema/schema.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb22
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb3
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb16
-rw-r--r--activesupport/test/json/decoding_test.rb4
-rw-r--r--activesupport/test/ordered_hash_test.rb6
-rw-r--r--railties/guides/source/contributing_to_ruby_on_rails.textile2
-rw-r--r--railties/lib/rails/commands/plugin.rb7
-rw-r--r--railties/test/generators/model_generator_test.rb2
48 files changed, 514 insertions, 198 deletions
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 032769a5c6..b2c8053584 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -43,11 +43,11 @@ module ActionController
end
end
- # ActionController::Metal is the simplest possible controller, providing a
+ # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
# valid Rack interface without the additional niceties provided by
- # ActionController::Base.
+ # <tt>ActionController::Base</tt>.
#
- # A sample Metal controller might look like this:
+ # A sample metal controller might look like this:
#
# class HelloController < ActionController::Metal
# def index
@@ -55,26 +55,25 @@ module ActionController
# end
# end
#
- # And then to route requests to your Metal controller, you would add
+ # And then to route requests to your metal controller, you would add
# something like this to <tt>config/routes.rb</tt>:
#
- # match '/hello', :to => HelloController.action(:index),
- # :as => 'hello'
+ # match 'hello', :to => HelloController.action(:index)
#
- # The action method returns a valid Rack application for the \Rails
+ # The +action+ method returns a valid Rack application for the \Rails
# router to dispatch to.
#
# == Rendering Helpers
#
- # ActionController::Metal by default provides no utilities for rendering
+ # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
# views, partials, or other responses aside from explicitly calling of
- # response_body=, content_type=, and status=. To
+ # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
# add the render helpers you're used to having in a normal controller, you
# can do the following:
#
# class HelloController < ActionController::Metal
# include ActionController::Rendering
- # append_view_path Rails.root + "app/views"
+ # append_view_path "#{Rails.root}/app/views"
#
# def index
# render "hello/index"
@@ -83,10 +82,11 @@ module ActionController
#
# == Redirection Helpers
#
- # To add redirection helpers to your Metal controller, do the following:
+ # To add redirection helpers to your metal controller, do the following:
#
# class HelloController < ActionController::Metal
# include ActionController::Redirecting
+ # include Rails.application.routes.url_helpers
#
# def index
# redirect_to root_url
@@ -95,9 +95,8 @@ module ActionController
#
# == Other Helpers
#
- # You can refer to the modules defined in ActionController to see
- # the other features in ActionController::Base that you can bring
- # into your Metal controller.
+ # You can refer to the modules included in <tt>ActionController::Base</tt> to see
+ # other features you can bring into your metal controller.
#
class Metal < AbstractController::Base
abstract!
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 796cd8c09b..535ff42b90 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -28,8 +28,11 @@ module ActionDispatch
rewritten_url = ""
unless options[:only_path]
- rewritten_url << (options[:protocol] || "http")
- rewritten_url << "://" unless rewritten_url.match("://")
+ unless options[:protocol] == false
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << ":" unless rewritten_url.match(%r{:|//})
+ end
+ rewritten_url << "//" unless rewritten_url.match("//")
rewritten_url << rewrite_authentication(options)
rewritten_url << host_or_subdomain_and_domain(options)
rewritten_url << ":#{options.delete(:port)}" if options[:port]
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index a91b2e88fb..f3f7cb6507 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1221,7 +1221,7 @@ module ActionDispatch
end
def shallow
- scope(:shallow => true) do
+ scope(:shallow => true, :shallow_path => @scope[:path]) do
yield
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index d7b9e0b4f4..d6edef0d34 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -1243,7 +1243,7 @@ module ActionView
def submit(value=nil, options={})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
- @template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
+ @template.submit_tag(value, options)
end
def emitted_hidden_id?
diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index 1f62d29e80..1c28da33bd 100644
--- a/actionpack/test/controller/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -95,16 +95,29 @@ module AbstractController
)
end
- def test_protocol_with_and_without_separator
+ def test_protocol_with_and_without_separators
add_host!
assert_equal('https://www.basecamphq.com/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
)
assert_equal('https://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https:')
+ )
+ assert_equal('https://www.basecamphq.com/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://')
)
end
+ def test_without_protocol
+ add_host!
+ assert_equal('//www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => '//')
+ )
+ assert_equal('//www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => false)
+ )
+ end
+
def test_trailing_slash
add_host!
options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'}
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 4bf7880294..bdd4606720 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -205,6 +205,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ scope '/hello' do
+ shallow do
+ resources :notes do
+ resources :trackbacks
+ end
+ end
+ end
+
resources :threads, :shallow => true do
resource :owner
resources :messages do
@@ -1676,6 +1684,60 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_shallow_nested_resources_within_scope
+ with_test_routes do
+
+ get '/hello/notes/1/trackbacks'
+ assert_equal 'trackbacks#index', @response.body
+ assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1)
+
+ get '/hello/notes/1/edit'
+ assert_equal 'notes#edit', @response.body
+ assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1')
+
+ get '/hello/notes/1/trackbacks/new'
+ assert_equal 'trackbacks#new', @response.body
+ assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1)
+
+ get '/hello/trackbacks/1'
+ assert_equal 'trackbacks#show', @response.body
+ assert_equal '/hello/trackbacks/1', trackback_path(:id => '1')
+
+ get '/hello/trackbacks/1/edit'
+ assert_equal 'trackbacks#edit', @response.body
+ assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1')
+
+ put '/hello/trackbacks/1'
+ assert_equal 'trackbacks#update', @response.body
+
+ post '/hello/notes/1/trackbacks'
+ assert_equal 'trackbacks#create', @response.body
+
+ delete '/hello/trackbacks/1'
+ assert_equal 'trackbacks#destroy', @response.body
+
+ get '/hello/notes'
+ assert_equal 'notes#index', @response.body
+
+ post '/hello/notes'
+ assert_equal 'notes#create', @response.body
+
+ get '/hello/notes/new'
+ assert_equal 'notes#new', @response.body
+ assert_equal '/hello/notes/new', new_note_path
+
+ get '/hello/notes/1'
+ assert_equal 'notes#show', @response.body
+ assert_equal '/hello/notes/1', note_path(:id => 1)
+
+ put '/hello/notes/1'
+ assert_equal 'notes#update', @response.body
+
+ delete '/hello/notes/1'
+ assert_equal 'notes#destroy', @response.body
+ end
+ end
+
def test_custom_resource_routes_are_scoped
with_test_routes do
assert_equal '/customers/recent', recent_customers_path
diff --git a/actionpack/test/fixtures/test/_layout_with_partial_and_yield.html.erb b/actionpack/test/fixtures/test/_layout_with_partial_and_yield.html.erb
new file mode 100644
index 0000000000..5db0822f07
--- /dev/null
+++ b/actionpack/test/fixtures/test/_layout_with_partial_and_yield.html.erb
@@ -0,0 +1,4 @@
+Before
+<%= render :partial => "test/partial.html.erb" %>
+<%= yield %>
+After
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 2c60096475..e27ed20b81 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -630,7 +630,7 @@ class FormHelperTest < ActionView::TestCase
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
- "<input name='commit' id='post_submit' type='submit' value='Create post' />"
+ "<input name='commit' type='submit' value='Create post' />"
end
assert_dom_equal expected, output_buffer
@@ -709,7 +709,7 @@ class FormHelperTest < ActionView::TestCase
"<textarea name='other_name[body]' id='other_name_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='other_name[secret]' value='0' type='hidden' />" +
"<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" +
- "<input name='commit' id='other_name_submit' value='Create post' type='submit' />"
+ "<input name='commit' value='Create post' type='submit' />"
end
assert_dom_equal expected, output_buffer
@@ -843,7 +843,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts', 'new_post', 'new_post') do
- "<input name='commit' id='post_submit' type='submit' value='Create Post' />"
+ "<input name='commit' type='submit' value='Create Post' />"
end
assert_dom_equal expected, output_buffer
@@ -859,7 +859,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do
- "<input name='commit' id='post_submit' type='submit' value='Confirm Post changes' />"
+ "<input name='commit' type='submit' value='Confirm Post changes' />"
end
assert_dom_equal expected, output_buffer
@@ -875,7 +875,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form do
- "<input name='commit' class='extra' id='post_submit' type='submit' value='Save changes' />"
+ "<input name='commit' class='extra' type='submit' value='Save changes' />"
end
assert_dom_equal expected, output_buffer
@@ -891,7 +891,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts/123', 'another_post_edit', 'another_post_edit', :method => 'put') do
- "<input name='commit' id='another_post_submit' type='submit' value='Update your Post' />"
+ "<input name='commit' type='submit' value='Update your Post' />"
end
assert_dom_equal expected, output_buffer
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index e02d69231f..034fb6c210 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -209,6 +209,11 @@ module RenderTestCases
@view.formats = nil
end
+ def test_render_layout_with_block_and_other_partial_inside
+ render = @view.render(:layout => "test/layout_with_partial_and_yield.html.erb") { "Yield!" }
+ assert_equal "Before\npartial html\nYield!\nAfter\n", render
+ end
+
def test_render_inline
assert_equal "Hello, World!", @view.render(:inline => "Hello, World!")
end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 34669c1542..8f3782eb48 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -98,7 +98,7 @@ module ActiveModel
def define_attr_method(name, value=nil, &block)
sing = singleton_class
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
- if method_defined?(:'original_#{name}')
+ if method_defined?('original_#{name}')
undef :'original_#{name}'
end
alias_method :'original_#{name}', :'#{name}'
@@ -229,11 +229,9 @@ module ActiveModel
def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
- module_eval <<-STR, __FILE__, __LINE__ + 1
- def #{matcher.method_name(new_name)}(*args)
- send(:#{matcher.method_name(old_name)}, *args)
- end
- STR
+ define_method(matcher.method_name(new_name)) do |*args|
+ send(matcher.method_name(old_name), *args)
+ end
end
end
@@ -274,11 +272,11 @@ module ActiveModel
method_name = matcher.method_name(attr_name)
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- if method_defined?(:'#{method_name}')
+ if method_defined?('#{method_name}')
undef :'#{method_name}'
end
- def #{method_name}(*args)
- send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
+ define_method('#{method_name}') do |*args|
+ send('#{matcher.method_missing_target}', '#{attr_name}', *args)
end
STR
end
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb
index 54cf8380ab..422aa25668 100644
--- a/activemodel/test/cases/attribute_methods_test.rb
+++ b/activemodel/test/cases/attribute_methods_test.rb
@@ -21,6 +21,21 @@ class ModelWithAttributes2
attribute_method_suffix '_test'
end
+class ModelWithAttributesWithSpaces
+ include ActiveModel::AttributeMethods
+
+ attribute_method_suffix ''
+
+ def attributes
+ { :'foo bar' => 'value of foo bar'}
+ end
+
+private
+ def attribute(name)
+ attributes[name.to_sym]
+ end
+end
+
class AttributeMethodsTest < ActiveModel::TestCase
test 'unrelated classes should not share attribute method matchers' do
assert_not_equal ModelWithAttributes.send(:attribute_method_matchers),
@@ -35,6 +50,21 @@ class AttributeMethodsTest < ActiveModel::TestCase
assert_equal "value of foo", ModelWithAttributes.new.foo
end
+ test '#define_attribute_methods generates attribute methods with spaces in their names' do
+ ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar'])
+
+ assert ModelWithAttributesWithSpaces.attribute_methods_generated?
+ assert_respond_to ModelWithAttributesWithSpaces.new, :'foo bar'
+ assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar')
+ end
+
+ test '#alias_attribute works with attributes with spaces in their names' do
+ ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar'])
+ ModelWithAttributesWithSpaces.alias_attribute(:'foo_bar', :'foo bar')
+
+ assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.foo_bar
+ end
+
test '#undefine_attribute_methods removes attribute methods' do
ModelWithAttributes.define_attribute_methods([:foo])
ModelWithAttributes.undefine_attribute_methods
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 285ce32e21..398936b3d8 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1450,8 +1450,8 @@ module ActiveRecord
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def destroy # def destroy
- #{reflection.name}.clear # posts.clear
super # super
+ #{reflection.name}.clear # posts.clear
end # end
RUBY
}
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 0941700803..effb17b2ff 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -249,6 +249,17 @@ module ActiveRecord #:nodoc:
# user = User.create(:preferences => %w( one two three ))
# User.find(user.id).preferences # raises SerializationTypeMismatch
#
+ # When you specify a class option, the default value for that attribute will be a new
+ # instance of that class.
+ #
+ # class User < ActiveRecord::Base
+ # serialize :preferences, OpenStruct
+ # end
+ #
+ # user = User.new
+ # user.preferences.theme_color = "red"
+ #
+ #
# == Single table inheritance
#
# Active Record allows inheritance by storing the name of the class in a column that by
@@ -668,16 +679,12 @@ module ActiveRecord #:nodoc:
# Returns an array of column objects for the table associated with this class.
def columns
- @@columns[table_name] ||= connection.columns(
- table_name, "#{name} Columns"
- ).tap { |columns|
- columns.each { |column| column.primary = column.name == primary_key }
- }
+ connection_pool.columns[table_name]
end
# Returns a hash of column objects for the table associated with this class.
def columns_hash
- @@columns_hash[table_name] ||= Hash[columns.map { |column| [column.name, column] }]
+ connection_pool.columns_hash[table_name]
end
# Returns an array of column names as strings.
@@ -734,14 +741,14 @@ module ActiveRecord #:nodoc:
def reset_column_information
connection.clear_cache!
undefine_attribute_methods
- reset_column_cache
+ connection_pool.clear_table_cache!(table_name) if table_exists?
+
@column_names = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
- @arel_engine = @relation = @arel_table = nil
+ @arel_engine = @relation = nil
end
- def reset_column_cache # :nodoc:
- @@columns.delete table_name
- @@columns_hash.delete table_name
+ def clear_cache! # :nodoc:
+ connection_pool.clear_cache!
end
def attribute_method?(attribute)
@@ -840,7 +847,7 @@ module ActiveRecord #:nodoc:
end
def arel_table
- @arel_table ||= Arel::Table.new(table_name, arel_engine)
+ Arel::Table.new(table_name, arel_engine)
end
def arel_engine
@@ -1388,8 +1395,6 @@ MSG
quoted_value
end
end
- @@columns_hash = {}
- @@columns = {}
public
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -1409,6 +1414,7 @@ MSG
@changed_attributes = {}
ensure_proper_type
+ set_serialized_attributes
populate_with_current_scope_attributes
self.attributes = attributes unless attributes.nil?
@@ -1447,10 +1453,7 @@ MSG
def init_with(coder)
@attributes = coder['attributes']
- (@attributes.keys & self.class.serialized_attributes.keys).each do |key|
- coder = self.class.serialized_attributes[key]
- @attributes[key] = coder.load @attributes[key]
- end
+ set_serialized_attributes
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
@association_cache = {}
@@ -1724,6 +1727,13 @@ MSG
private
+ def set_serialized_attributes
+ (@attributes.keys & self.class.serialized_attributes.keys).each do |key|
+ coder = self.class.serialized_attributes[key]
+ @attributes[key] = coder.load @attributes[key]
+ end
+ end
+
# Sets the attribute used for single table inheritance to this class name if this is not the
# ActiveRecord::Base descendant.
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index fcecc11aba..fb59d9fb07 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -19,6 +19,7 @@ module ActiveRecord
end
def load(yaml)
+ return object_class.new if object_class != Object && yaml.nil?
return yaml unless yaml.is_a?(String) && yaml =~ /^---/
begin
obj = YAML.load(yaml)
@@ -27,6 +28,7 @@ module ActiveRecord
raise SerializationTypeMismatch,
"Attribute was supposed to be a #{object_class}, but was a #{obj.class}"
end
+ obj ||= object_class.new if object_class != Object
obj
rescue *RESCUE_ERRORS
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 54f70c59f8..4475019e0e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -57,7 +57,9 @@ module ActiveRecord
# * +wait_timeout+: number of seconds to block and wait for a connection
# before giving up and raising a timeout error (default 5 seconds).
class ConnectionPool
+ attr_accessor :automatic_reconnect
attr_reader :spec, :connections
+ attr_reader :columns, :columns_hash, :primary_keys
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
# object which describes database connection information (e.g. adapter,
@@ -81,6 +83,59 @@ module ActiveRecord
@connections = []
@checked_out = []
+ @automatic_reconnect = true
+
+ @tables = Hash.new do |h, table_name|
+ with_connection do |conn|
+ h[table_name] = conn.table_exists?(table_name)
+ end
+ end
+
+ @columns = Hash.new do |h, table_name|
+ h[table_name] = with_connection do |conn|
+
+ # Fetch a list of columns
+ conn.columns(table_name, "#{table_name} Columns").tap do |columns|
+
+ # set primary key information
+ columns.each do |column|
+ column.primary = column.name == primary_keys[table_name]
+ end
+ end
+ end
+ end
+
+ @columns_hash = Hash.new do |h, table_name|
+ h[table_name] = Hash[columns[table_name].map { |col|
+ [col.name, col]
+ }]
+ end
+
+ @primary_keys = Hash.new do |h, table_name|
+ h[table_name] = with_connection do |conn|
+ @tables[table_name] ? conn.primary_key(table_name) : 'id'
+ end
+ end
+ end
+
+ # Clears out internal caches:
+ #
+ # * columns
+ # * columns_hash
+ # * primary_keys
+ # * tables
+ def clear_cache!
+ @columns.clear
+ @columns_hash.clear
+ @primary_keys.clear
+ @tables.clear
+ end
+
+ # Clear out internal caches for table with +table_name+
+ def clear_table_cache!(table_name)
+ @columns.delete table_name
+ @columns_hash.delete table_name
+ @primary_keys.delete table_name
end
# Retrieve the connection associated with the current thread, or call
@@ -232,6 +287,8 @@ module ActiveRecord
end
def checkout_new_connection
+ raise ConnectionNotEstablished unless @automatic_reconnect
+
c = new_connection
@connections << c
checkout_and_verify(c)
@@ -330,7 +387,7 @@ module ActiveRecord
pool = @connection_pools[klass.name]
return nil unless pool
- @connection_pools.delete_if { |key, value| value == pool }
+ pool.automatic_reconnect = false
pool.disconnect!
pool.spec.config
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index beb06ea622..4e3d8a096f 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -238,28 +238,28 @@ module ActiveRecord
def simplified_type(field_type)
case field_type
- when /int/i
- :integer
- when /float|double/i
- :float
- when /decimal|numeric|number/i
- extract_scale(field_type) == 0 ? :integer : :decimal
- when /datetime/i
- :datetime
- when /timestamp/i
- :timestamp
- when /time/i
- :time
- when /date/i
- :date
- when /clob/i, /text/i
- :text
- when /blob/i, /binary/i
- :binary
- when /char/i, /string/i
- :string
- when /boolean/i
- :boolean
+ when /int/i
+ :integer
+ when /float|double/i
+ :float
+ when /decimal|numeric|number/i
+ extract_scale(field_type) == 0 ? :integer : :decimal
+ when /datetime/i
+ :datetime
+ when /timestamp/i
+ :timestamp
+ when /time/i
+ :time
+ when /date/i
+ :date
+ when /clob/i, /text/i
+ :text
+ when /blob/i, /binary/i
+ :binary
+ when /char/i, /string/i
+ :string
+ when /boolean/i
+ :boolean
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index a04fc01d6f..acf1832938 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -42,62 +42,17 @@ module ActiveRecord
super
end
- # Returns the Ruby class that corresponds to the abstract data type.
- def klass
- case type
- when :integer then Fixnum
- when :float then Float
- when :decimal then BigDecimal
- when :datetime then Time
- when :date then Date
- when :timestamp then Time
- when :time then Time
- when :text, :string then String
- when :binary then String
- when :boolean then Object
- end
- end
-
- def type_cast(value)
- return nil if value.nil?
- case type
- when :string then value
- when :text then value
- when :integer then value.to_i rescue value ? 1 : 0
- when :float then value.to_f # returns self if it's already a Float
- when :decimal then self.class.value_to_decimal(value)
- when :datetime, :timestamp then value.class == Time ? value : self.class.string_to_time(value)
- when :time then value.class == Time ? value : self.class.string_to_dummy_time(value)
- when :date then value.class == Date ? value : self.class.string_to_date(value)
- when :binary then value
- when :boolean then self.class.value_to_boolean(value)
- else value
- end
- end
-
- def type_cast_code(var_name)
- case type
- when :string then var_name
- when :text then var_name
- when :integer then "#{var_name}.to_i rescue #{var_name} ? 1 : 0"
- when :float then "#{var_name}.to_f"
- when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
- when :datetime, :timestamp then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_time(#{var_name})"
- when :time then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_dummy_time(#{var_name})"
- when :date then "#{var_name}.class == Date ? #{var_name} : #{self.class.name}.string_to_date(#{var_name})"
- when :binary then var_name
- when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
- else var_name
- end
- end
-
private
def simplified_type(field_type)
return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL)
- return :string if field_type =~ /enum/i or field_type =~ /set/i
- return :integer if field_type =~ /year/i
- return :binary if field_type =~ /bit/i
- super
+
+ case field_type
+ when /enum/i, /set/i then :string
+ when /year/i then :integer
+ when /bit/i then :binary
+ else
+ super
+ end
end
def extract_limit(sql_type)
@@ -376,6 +331,7 @@ module ActiveRecord
end
sql
end
+ deprecate :add_limit_offset!
# SCHEMA STATEMENTS ========================================
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 15488cee52..cdf1ebfee4 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -537,7 +537,7 @@ module ActiveRecord
def columns(table_name, name = nil)#:nodoc:
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
columns = []
- result = execute(sql, :skip_logging)
+ result = execute(sql)
result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
result.free
columns
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index a4b1aa7154..46c0f3fafe 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -786,7 +786,7 @@ module ActiveRecord
def pk_and_sequence_for(table) #:nodoc:
# First try looking for a sequence with a dependency on the
# given table's primary key.
- result = query(<<-end_sql, 'PK and serial sequence')[0]
+ result = exec_query(<<-end_sql, 'PK and serial sequence').rows.first
SELECT attr.attname, seq.relname
FROM pg_class seq,
pg_attribute attr,
@@ -1071,7 +1071,7 @@ module ActiveRecord
# - format_type includes the column size constraint, e.g. varchar(50)
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
- query <<-end_sql
+ exec_query(<<-end_sql).rows
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index b04383d5bf..f650a1bc74 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -152,8 +152,11 @@ module ActiveRecord
# Don't cache statements without bind values
if binds.empty?
- stmt = @connection.prepare(sql)
- cols = stmt.columns
+ stmt = @connection.prepare(sql)
+ cols = stmt.columns
+ records = stmt.to_a
+ stmt.close
+ stmt = records
else
cache = @statements[sql] ||= {
:stmt => @connection.prepare(sql)
@@ -233,6 +236,15 @@ module ActiveRecord
def columns(table_name, name = nil) #:nodoc:
table_structure(table_name).map do |field|
+ case field["dflt_value"]
+ when /^null$/i
+ field["dflt_value"] = nil
+ when /^'(.*)'$/
+ field["dflt_value"] = $1.gsub(/''/, "'")
+ when /^"(.*)"$/
+ field["dflt_value"] = $1.gsub(/""/, '"')
+ end
+
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
end
end
@@ -334,7 +346,7 @@ module ActiveRecord
end
def table_structure(table_name)
- structure = @connection.table_info(quote_table_name(table_name))
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})").to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
structure
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 2accf0a48f..72687c9ca3 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -72,6 +72,7 @@ module ActiveRecord
ActiveSupport.on_load(:active_record) do
ActionDispatch::Reloader.to_cleanup do
ActiveRecord::Base.clear_reloadable_connections!
+ ActiveRecord::Base.clear_cache!
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 9924755ddf..49d4b8f76b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -193,7 +193,7 @@ db_namespace = namespace :db do
file_list = []
Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
# only files matching "20091231235959_some_name.rb" pattern
- if match_data = /(\d{14})_(.+)\.rb/.match(file)
+ if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
status = db_list.delete(match_data[1]) ? 'up' : 'down'
file_list << [status, match_data[1], match_data[2]]
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index b75a65e3ca..abc4c54109 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -285,7 +285,7 @@ module ActiveRecord
case operation
when 'count' then value.to_i
when 'sum' then type_cast_using_column(value || '0', column)
- when 'average' then value.try(:to_d)
+ when 'average' then value.respond_to?(:to_d) ? value.to_d : value
else type_cast_using_column(value, column)
end
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 47a5ac2700..0465b21e88 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -20,6 +20,10 @@ module ActiveRecord
hash_rows.each { |row| yield row }
end
+ def to_hash
+ hash_rows
+ end
+
private
def hash_rows
@hash_rows ||= @rows.map { |row|
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index e3342f046f..7e77aefb21 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -59,12 +59,12 @@ module ActiveRecord
end
def drop_table!
- reset_column_cache
+ connection_pool.clear_table_cache!(table_name)
connection.drop_table table_name
end
def create_table!
- reset_column_cache
+ connection_pool.clear_table_cache!(table_name)
connection.create_table(table_name) do |t|
t.string session_id_column, :limit => 255
t.text data_column_name
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
new file mode 100644
index 0000000000..1c2942170e
--- /dev/null
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -0,0 +1,36 @@
+module ActiveRecord
+ class Base
+ def self.fake_connection(config)
+ ConnectionAdapters::FakeAdapter.new nil, logger
+ end
+ end
+
+ module ConnectionAdapters
+ class FakeAdapter < AbstractAdapter
+ attr_accessor :tables, :primary_keys
+
+ def initialize(connection, logger)
+ super
+ @tables = []
+ @primary_keys = {}
+ @columns = Hash.new { |h,k| h[k] = [] }
+ end
+
+ def primary_key(table)
+ @primary_keys[table]
+ end
+
+ def merge_column(table_name, name, sql_type = nil, options = {})
+ @columns[table_name] << ActiveRecord::ConnectionAdapters::Column.new(
+ name.to_s,
+ options[:default],
+ sql_type.to_s,
+ options[:null])
+ end
+
+ def columns(table_name, message)
+ @columns[table_name]
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 30730c7094..126b767d06 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -365,7 +365,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_removing_associations_on_destroy
david = DeveloperWithBeforeDestroyRaise.find(1)
assert !david.projects.empty?
- assert_raise(RuntimeError) { david.destroy }
+ david.destroy
assert david.projects.empty?
assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
end
@@ -757,10 +757,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david = Developer.find(1)
# clear cache possibly created by other tests
david.projects.reset_column_information
- assert_queries(0) { david.projects.columns; david.projects.columns }
- # and again to verify that reset_column_information clears the cache correctly
+
+ # One query for columns, one for primary key
+ assert_queries(2) { david.projects.columns; david.projects.columns }
+
+ ## and again to verify that reset_column_information clears the cache correctly
david.projects.reset_column_information
- assert_queries(0) { david.projects.columns; david.projects.columns }
+ assert_queries(2) { david.projects.columns; david.projects.columns }
end
def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 5cbc52732b..68adeff882 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1002,6 +1002,25 @@ class BasicsTest < ActiveRecord::TestCase
Topic.serialize(:content)
end
+ def test_serialized_default_class
+ Topic.serialize(:content, Hash)
+ topic = Topic.new
+ assert_equal Hash, topic.content.class
+ assert_equal Hash, topic.read_attribute(:content).class
+ topic.content["beer"] = "MadridRb"
+ assert topic.save
+ topic.reload
+ assert_equal Hash, topic.content.class
+ assert_equal "MadridRb", topic.content["beer"]
+ ensure
+ Topic.serialize(:content)
+ end
+
+ def test_serialized_no_default_class_for_object
+ topic = Topic.new
+ assert_nil topic.content
+ end
+
def test_serialized_boolean_value_true
Topic.serialize(:content)
topic = Topic.new(:content => true)
@@ -1534,6 +1553,14 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_clear_cache!
+ # preheat cache
+ c1 = Post.columns
+ ActiveRecord::Base.clear_cache!
+ c2 = Post.columns
+ assert_not_equal c1, c2
+ end
+
def test_default_scope_is_reset
Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
UnloadablePost.table_name = 'posts'
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 644c9cb528..3121f1615d 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -28,6 +28,12 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 3.5, value
end
+ def test_should_return_integer_average_if_db_returns_such
+ Account.connection.stubs :select_value => 3
+ value = Account.average(:id)
+ assert_equal 3, value
+ end
+
def test_should_return_nil_as_average
assert_nil NumericData.average(:bank_balance)
end
diff --git a/activerecord/test/cases/coders/yaml_column_test.rb b/activerecord/test/cases/coders/yaml_column_test.rb
index f85f11b57f..c7dcc21809 100644
--- a/activerecord/test/cases/coders/yaml_column_test.rb
+++ b/activerecord/test/cases/coders/yaml_column_test.rb
@@ -1,3 +1,4 @@
+
require "cases/helper"
module ActiveRecord
@@ -20,9 +21,9 @@ module ActiveRecord
assert_nil coder.load "--- "
end
- def test_nil_is_ok_with_different_class
+ def test_returns_new_with_different_class
coder = YAMLColumn.new SerializationTypeMismatch
- assert_nil coder.load "--- "
+ assert_equal SerializationTypeMismatch, coder.load("--- ").class
end
def test_returns_string_unless_starts_with_dash
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 2e18117895..55ac1bc406 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -3,6 +3,50 @@ require "cases/helper"
module ActiveRecord
module ConnectionAdapters
class ConnectionPoolTest < ActiveRecord::TestCase
+ def setup
+ # Keep a duplicate pool so we do not bother others
+ @pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ end
+
+ def test_pool_caches_columns
+ columns = @pool.columns['posts']
+ assert_equal columns, @pool.columns['posts']
+ end
+
+ def test_pool_caches_columns_hash
+ columns_hash = @pool.columns_hash['posts']
+ assert_equal columns_hash, @pool.columns_hash['posts']
+ end
+
+ def test_clearing_cache
+ @pool.columns['posts']
+ @pool.columns_hash['posts']
+ @pool.primary_keys['posts']
+
+ @pool.clear_cache!
+
+ assert_equal 0, @pool.columns.size
+ assert_equal 0, @pool.columns_hash.size
+ assert_equal 0, @pool.primary_keys.size
+ end
+
+ def test_primary_key
+ assert_equal 'id', @pool.primary_keys['posts']
+ end
+
+ def test_primary_key_for_non_existent_table
+ assert_equal 'id', @pool.primary_keys['omgponies']
+ end
+
+ def test_primary_key_is_set_on_columns
+ posts_columns = @pool.columns_hash['posts']
+ assert posts_columns['id'].primary
+
+ (posts_columns.keys - ['id']).each do |key|
+ assert !posts_columns[key].primary
+ end
+ end
+
def test_clear_stale_cached_connections!
pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
@@ -55,6 +99,26 @@ module ActiveRecord
end.join()
end
+
+ def test_automatic_reconnect=
+ pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ assert pool.automatic_reconnect
+ assert pool.connection
+
+ pool.disconnect!
+ assert pool.connection
+
+ pool.disconnect!
+ pool.automatic_reconnect = false
+
+ assert_raises(ConnectionNotEstablished) do
+ pool.connection
+ end
+
+ assert_raises(ConnectionNotEstablished) do
+ pool.with_connection
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/habtm_destroy_order_test.rb b/activerecord/test/cases/habtm_destroy_order_test.rb
new file mode 100644
index 0000000000..15598392e2
--- /dev/null
+++ b/activerecord/test/cases/habtm_destroy_order_test.rb
@@ -0,0 +1,17 @@
+require "cases/helper"
+require "models/lesson"
+require "models/student"
+
+class HabtmDestroyOrderTest < ActiveRecord::TestCase
+ test "may not delete a lesson with students" do
+ sicp = Lesson.new(:name => "SICP")
+ ben = Student.new(:name => "Ben Bitdiddle")
+ sicp.students << ben
+ sicp.save!
+ assert_raises LessonError do
+ assert_no_difference('Lesson.count') do
+ sicp.destroy
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 2cc993b6ed..97bb631d2d 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -50,7 +50,7 @@ ensure
end
ActiveRecord::Base.connection.class.class_eval do
- IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
+ IGNORED_SQL = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/]
# FIXME: this needs to be refactored so specific database can add their own
# ignored SQL. This ignored SQL is for Oracle.
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6f0f73e3bd..9d7c49768b 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -2022,7 +2022,10 @@ if ActiveRecord::Base.connection.supports_migrations?
assert ! column(:name).default
assert_equal :date, column(:birthdate).type
- assert_queries(1) do
+ # One query for columns (delete_me table)
+ # One query for primary key (delete_me table)
+ # One query to do the bulk change
+ assert_queries(3) do
with_bulk_change_table do |t|
t.change :name, :string, :default => 'NONAME'
t.change :birthdate, :datetime
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 5bbe7ebb12..e081eee661 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -1,12 +1,14 @@
class Contact < ActiveRecord::Base
- def self.columns
- @columns
- end
+ establish_connection(:adapter => 'fake')
+
+ connection.tables = ['contacts']
+ connection.primary_keys = {
+ 'contacts' => 'id'
+ }
# mock out self.columns so no pesky db is needed for these tests
def self.column(name, sql_type = nil, options = {})
- @columns ||= []
- @columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, options[:default], sql_type.to_s, options[:null])
+ connection.merge_column('contacts', name, sql_type, options)
end
column :name, :string
diff --git a/activerecord/test/models/lesson.rb b/activerecord/test/models/lesson.rb
new file mode 100644
index 0000000000..4c88153068
--- /dev/null
+++ b/activerecord/test/models/lesson.rb
@@ -0,0 +1,11 @@
+class LessonError < Exception
+end
+
+class Lesson < ActiveRecord::Base
+ has_and_belongs_to_many :students
+ before_destroy :ensure_no_students
+
+ def ensure_no_students
+ raise LessonError unless students.empty?
+ end
+end
diff --git a/activerecord/test/models/student.rb b/activerecord/test/models/student.rb
new file mode 100644
index 0000000000..f459f2a9a3
--- /dev/null
+++ b/activerecord/test/models/student.rb
@@ -0,0 +1,3 @@
+class Student < ActiveRecord::Base
+ has_and_belongs_to_many :lessons
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 5f9bb7ee41..326c336317 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -279,6 +279,15 @@ ActiveRecord::Schema.define do
t.integer :version, :null => false, :default => 0
end
+ create_table :lessons, :force => true do |t|
+ t.string :name
+ end
+
+ create_table :lessons_students, :id => false, :force => true do |t|
+ t.references :lesson
+ t.references :student
+ end
+
create_table :line_items, :force => true do |t|
t.integer :invoice_id
t.integer :amount
@@ -509,6 +518,10 @@ ActiveRecord::Schema.define do
t.string :sponsorable_type
end
+ create_table :students, :force => true do |t|
+ t.string :name
+ end
+
create_table :subscribers, :force => true, :id => false do |t|
t.string :nick, :null => false
t.string :name
diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
index 029b8c41b4..21b84b994b 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -66,7 +66,7 @@ class DateTime
# Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class
# If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time
def to_time
- self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self
+ self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
end
# To be able to keep Times, Dates and DateTimes interchangeable on conversions
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index 49ac18d245..d9d5e02778 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -55,31 +55,9 @@ class Time
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
- # Converts a Time object to a Date, dropping hour, minute, and second precision.
- #
- # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007
- # my_time.to_date # => Mon, 12 Nov 2007
- #
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
- # your_time.to_date # => Tue, 13 Jan 2009
- def to_date
- ::Date.new(year, month, day)
- end unless method_defined?(:to_date)
-
# A method to keep Time, Date and DateTime instances interchangeable on conversions.
# In this case, it simply returns +self+.
def to_time
self
end unless method_defined?(:to_time)
-
- # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
- #
- # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007
- # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500
- #
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
- # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500
- def to_datetime
- ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
- end unless method_defined?(:to_datetime)
end
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index b2f04b427b..fbc40d1b69 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -137,16 +137,19 @@ module ActiveSupport
end
def each_key
+ return to_enum(:each_key) unless block_given?
@keys.each { |key| yield key }
self
end
def each_value
+ return to_enum(:each_value) unless block_given?
@keys.each { |key| yield self[key]}
self
end
def each
+ return to_enum(:each) unless block_given?
@keys.each {|key| yield [key, self[key]]}
self
end
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 7d993d84e2..8edb95b63a 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -38,6 +38,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.utc_time(2039, 2, 21, 10, 11, 12), DateTime.new(2039, 2, 21, 10, 11, 12, 0, 0).to_time
# DateTimes with offsets other than 0 are returned unaltered
assert_equal DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)), DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).to_time
+ # Fractional seconds are preserved
+ assert_equal Time.utc(2005, 2, 21, 10, 11, 12, 256), DateTime.new(2005, 2, 21, 10, 11, 12 + Rational(256, 1000000), 0).to_time
end
def test_civil_from_format
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 53d497013a..891a6badac 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -533,19 +533,9 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
Time::DATE_FORMATS.delete(:custom)
end
- def test_to_date
- assert_equal Date.new(2005, 2, 21), Time.local(2005, 2, 21, 17, 44, 30).to_date
- end
-
- def test_to_datetime
- assert_equal Time.utc(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, 0, 0)
- with_env_tz 'US/Eastern' do
- assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0)
- end
- with_env_tz 'NZ' do
- assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0)
- end
- assert_equal ::Date::ITALY, Time.utc(2005, 2, 21, 17, 44, 30).to_datetime.start # use Ruby's default start value
+ def test_conversion_methods_are_publicized
+ assert Time.public_instance_methods.include?(:to_date) || Time.public_instance_methods.include?('to_date')
+ assert Time.public_instance_methods.include?(:to_datetime) || Time.public_instance_methods.include?('to_datetime')
end
def test_to_time
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 613c7531d9..436861baad 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -43,8 +43,8 @@ class TestJSONDecoding < ActiveSupport::TestCase
[{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'},
{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}],
# tests escaping of "\n" char with Yaml backend
- %q("\n") => "\n",
- %q("\u000a") => "\n",
+ %q({"a":"\n"}) => {"a"=>"\n"},
+ %q({"a":"\u000a"}) => {"a"=>"\n"},
%q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"}
}
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index d2cfd3698f..50168fa78f 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -80,18 +80,24 @@ class OrderedHashTest < Test::Unit::TestCase
keys = []
assert_equal @ordered_hash, @ordered_hash.each_key { |k| keys << k }
assert_equal @keys, keys
+ expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
+ assert_kind_of expected_class, @ordered_hash.each_key
end
def test_each_value
values = []
assert_equal @ordered_hash, @ordered_hash.each_value { |v| values << v }
assert_equal @values, values
+ expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
+ assert_kind_of expected_class, @ordered_hash.each_value
end
def test_each
values = []
assert_equal @ordered_hash, @ordered_hash.each {|key, value| values << value}
assert_equal @values, values
+ expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
+ assert_kind_of expected_class, @ordered_hash.each
end
def test_each_with_index
diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile
index 504cd92d90..4f51c0f859 100644
--- a/railties/guides/source/contributing_to_ruby_on_rails.textile
+++ b/railties/guides/source/contributing_to_ruby_on_rails.textile
@@ -231,7 +231,7 @@ git checkout -b testing_branch
Then you can apply their patch:
<shell>
-git apply their-patch-file.diff
+git am their-patch-file.diff
</shell>
After applying a patch, test it out! Here are some things to think about:
diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb
index 8b2cd1bdba..048af7cbef 100644
--- a/railties/lib/rails/commands/plugin.rb
+++ b/railties/lib/rails/commands/plugin.rb
@@ -276,12 +276,11 @@ end
require 'optparse'
module Commands
class Plugin
- attr_reader :environment, :script_name, :sources
+ attr_reader :environment, :script_name
def initialize
@environment = RailsEnvironment.default
@rails_root = RailsEnvironment.default.root
@script_name = File.basename($0)
- @sources = []
end
def environment=(value)
@@ -301,8 +300,6 @@ module Commands
o.on("-r", "--root=DIR", String,
"Set an explicit rails app directory.",
"Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) }
- o.on("-s", "--source=URL1,URL2", Array,
- "Use the specified plugin repositories instead of the defaults.") { |sources| @sources = sources}
o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose }
o.on("-h", "--help", "Show this help message.") { puts o; exit }
@@ -315,8 +312,6 @@ module Commands
o.separator ""
o.separator "EXAMPLES"
- o.separator " Install a plugin:"
- o.separator " #{@script_name} plugin install continuous_builder\n"
o.separator " Install a plugin from a subversion URL:"
o.separator " #{@script_name} plugin install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
o.separator " Install a plugin from a git URL:"
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 6b2fd8b9ee..3d773b4134 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -177,7 +177,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
run_generator
old_migration = Dir["#{destination_root}/db/migrate/*_create_accounts.rb"].first
error = capture(:stderr) { run_generator ["Account", "--force"] }
- assert_no_match /Another migration is already named create_foos/, error
+ assert_no_match /Another migration is already named create_accounts/, error
assert_no_file old_migration
assert_migration 'db/migrate/create_accounts.rb'
end