diff options
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 |