diff options
-rw-r--r-- | actionpack/lib/action_controller/abstract/renderer.rb | 12 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/render.rb | 14 | ||||
-rw-r--r-- | actionpack/test/abstract_controller/abstract_controller_test.rb | 18 | ||||
-rw-r--r-- | actionpack/test/activerecord/polymorphic_routes_test.rb | 394 | ||||
-rw-r--r-- | actionpack/test/controller/polymorphic_routes_test.rb | 293 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb | 41 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 10 | ||||
-rwxr-xr-x | activerecord/test/cases/base_test.rb | 4 | ||||
-rw-r--r-- | activerecord/test/cases/copy_table_test_sqlite.rb | 10 | ||||
-rw-r--r-- | activerecord/test/cases/schema_dumper_test.rb | 10 | ||||
-rw-r--r-- | activerecord/test/cases/schema_test_postgresql.rb | 32 | ||||
-rw-r--r-- | activerecord/test/models/project.rb | 2 | ||||
-rw-r--r-- | activerecord/test/schema/schema.rb | 6 | ||||
-rw-r--r-- | ci/ci_setup_notes.txt | 17 | ||||
-rw-r--r-- | railties/lib/tasks/framework.rake | 4 |
15 files changed, 536 insertions, 331 deletions
diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index eb248652a8..2a68f048bd 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -20,7 +20,7 @@ module AbstractController self.response_body = render_to_body(options) end - # Raw rendering of a template. + # Raw rendering of a template to a Rack-compatible body. # ==== # @option _prefix<String> The template's path prefix # @option _layout<String> The relative path to the layout template to use @@ -33,6 +33,16 @@ module AbstractController _render_template(template, options) end + # Raw rendering of a template to a string. + # ==== + # @option _prefix<String> The template's path prefix + # @option _layout<String> The relative path to the layout template to use + # + # :api: plugin + def render_to_string(options = {}) + Rack::Utils.body_to_s(render_to_body(options)).to_ary.join + end + def _render_template(template, options) _action_view._render_template_with_layout(template) end diff --git a/actionpack/lib/action_controller/base/render.rb b/actionpack/lib/action_controller/base/render.rb index 0d24f18633..604dd31930 100644 --- a/actionpack/lib/action_controller/base/render.rb +++ b/actionpack/lib/action_controller/base/render.rb @@ -306,9 +306,9 @@ module ActionController (name.is_a?(String) ? name.sub(/^#{controller_path}\//, '') : name).to_s end - # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead - # of sending it as the response body to the browser. - def render_to_string(options = nil, &block) #:doc: + # Same rules as <tt>render</tt>, but returns a Rack-compatible body + # instead of sending the response. + def render_to_body(options = nil, &block) #:doc: render(options, &block) response.body ensure @@ -316,7 +316,11 @@ module ActionController erase_render_results reset_variables_added_to_assigns end - + + def render_to_string(options = {}) + Rack::Utils.body_to_s(render_to_body(options)).to_ary.join + end + # Clears the rendered results, allowing for another render to be performed. def erase_render_results #:nodoc: response.body = [] @@ -387,4 +391,4 @@ module ActionController render_for_parts(parts, layout, options) end end -end
\ No newline at end of file +end diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb index 8763ded57e..918062c7db 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract_controller/abstract_controller_test.rb @@ -56,6 +56,14 @@ module AbstractController def naked_render render end + + def rendering_to_body + render_to_body "naked_render.erb" + end + + def rendering_to_string + render_to_string "naked_render.erb" + end end class TestRenderer < ActiveSupport::TestCase @@ -73,6 +81,16 @@ module AbstractController result = Me2.process(:naked_render) assert_equal "Hello from naked_render.erb", result.response_obj[:body] end + + test "rendering to a rack body" do + result = Me2.process(:rendering_to_body) + assert_equal "Hello from naked_render.erb", result.response_obj[:body] + end + + test "rendering to a string" do + result = Me2.process(:rendering_to_string) + assert_equal "Hello from naked_render.erb", result.response_obj[:body] + end end # Test rendering with prefixes diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb new file mode 100644 index 0000000000..35139fbb7f --- /dev/null +++ b/actionpack/test/activerecord/polymorphic_routes_test.rb @@ -0,0 +1,394 @@ +require 'active_record_unit' + +class Task < ActiveRecord::Base + set_table_name 'projects' +end + +class Step < ActiveRecord::Base + set_table_name 'projects' +end + +class Bid < ActiveRecord::Base + set_table_name 'projects' +end + +class Tax < ActiveRecord::Base + set_table_name 'projects' +end + +class Fax < ActiveRecord::Base + set_table_name 'projects' +end + +class Series < ActiveRecord::Base + set_table_name 'projects' +end + +class PolymorphicRoutesTest < ActionController::TestCase + include ActionController::UrlWriter + self.default_url_options[:host] = 'example.com' + + def setup + @project = Project.new + @task = Task.new + @step = Step.new + @bid = Bid.new + @tax = Tax.new + @fax = Fax.new + @series = Series.new + end + + def test_with_record + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}", polymorphic_url(@project) + end + end + + def test_with_new_record + with_test_routes do + assert_equal "http://example.com/projects", polymorphic_url(@project) + end + end + + def test_with_record_and_action + with_test_routes do + assert_equal "http://example.com/projects/new", polymorphic_url(@project, :action => 'new') + end + end + + def test_url_helper_prefixed_with_new + with_test_routes do + assert_equal "http://example.com/projects/new", new_polymorphic_url(@project) + end + end + + def test_url_helper_prefixed_with_edit + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}/edit", edit_polymorphic_url(@project) + end + end + + def test_url_helper_prefixed_with_edit_with_url_options + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}/edit?param1=10", edit_polymorphic_url(@project, :param1 => '10') + end + end + + def test_url_helper_with_url_options + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}?param1=10", polymorphic_url(@project, :param1 => '10') + end + end + + def test_formatted_url_helper_is_deprecated + with_test_routes do + assert_deprecated do + formatted_polymorphic_url([@project, :pdf]) + end + end + end + + def test_format_option + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}.pdf", polymorphic_url(@project, :format => :pdf) + end + end + + def test_format_option_with_url_options + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}.pdf?param1=10", polymorphic_url(@project, :format => :pdf, :param1 => '10') + end + end + + def test_id_and_format_option + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}.pdf", polymorphic_url(:id => @project, :format => :pdf) + end + end + + def test_with_nested + with_test_routes do + @project.save + @task.save + assert_equal "http://example.com/projects/#{@project.id}/tasks/#{@task.id}", polymorphic_url([@project, @task]) + end + end + + def test_with_nested_unsaved + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}/tasks", polymorphic_url([@project, @task]) + end + end + + def test_new_with_array_and_namespace + with_admin_test_routes do + assert_equal "http://example.com/admin/projects/new", polymorphic_url([:admin, @project], :action => 'new') + end + end + + def test_unsaved_with_array_and_namespace + with_admin_test_routes do + assert_equal "http://example.com/admin/projects", polymorphic_url([:admin, @project]) + end + end + + def test_nested_unsaved_with_array_and_namespace + with_admin_test_routes do + @project.save + assert_equal "http://example.com/admin/projects/#{@project.id}/tasks", polymorphic_url([:admin, @project, @task]) + end + end + + def test_nested_with_array_and_namespace + with_admin_test_routes do + @project.save + @task.save + assert_equal "http://example.com/admin/projects/#{@project.id}/tasks/#{@task.id}", polymorphic_url([:admin, @project, @task]) + end + end + + def test_ordering_of_nesting_and_namespace + with_admin_and_site_test_routes do + @project.save + @task.save + @step.save + assert_equal "http://example.com/admin/projects/#{@project.id}/site/tasks/#{@task.id}/steps/#{@step.id}", polymorphic_url([:admin, @project, :site, @task, @step]) + end + end + + def test_nesting_with_array_ending_in_singleton_resource + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}/bid", polymorphic_url([@project, :bid]) + end + end + + def test_nesting_with_array_containing_singleton_resource + with_test_routes do + @project.save + @task.save + assert_equal "http://example.com/projects/#{@project.id}/bid/tasks/#{@task.id}", polymorphic_url([@project, :bid, @task]) + end + end + + def test_nesting_with_array_containing_singleton_resource_and_format + with_test_routes do + @project.save + @task.save + assert_equal "http://example.com/projects/#{@project.id}/bid/tasks/#{@task.id}.pdf", polymorphic_url([@project, :bid, @task], :format => :pdf) + end + end + + def test_nesting_with_array_containing_namespace_and_singleton_resource + with_admin_test_routes do + @project.save + @task.save + assert_equal "http://example.com/admin/projects/#{@project.id}/bid/tasks/#{@task.id}", polymorphic_url([:admin, @project, :bid, @task]) + end + end + + def test_nesting_with_array_containing_nil + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}/bid", polymorphic_url([@project, nil, :bid]) + end + end + + def test_with_array_containing_single_object + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}", polymorphic_url([nil, @project]) + end + end + + def test_with_array_containing_single_name + with_test_routes do + @project.save + assert_equal "http://example.com/projects", polymorphic_url([:projects]) + end + end + + def test_with_hash + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}", polymorphic_url(:id => @project) + end + end + + def test_polymorphic_path_accepts_options + with_test_routes do + assert_equal "/projects/new", polymorphic_path(@project, :action => 'new') + end + end + + def test_polymorphic_path_does_not_modify_arguments + with_admin_test_routes do + @project.save + @task.save + object_array = [:admin, @project, @task] + assert_no_difference 'object_array.size' do + polymorphic_url(object_array) + end + end + end + + # Tests for names where .plural.singular doesn't round-trip + def test_with_irregular_plural_record + with_test_routes do + @tax.save + assert_equal "http://example.com/taxes/#{@tax.id}", polymorphic_url(@tax) + end + end + + def test_with_irregular_plural_new_record + with_test_routes do + assert_equal "http://example.com/taxes", polymorphic_url(@tax) + end + end + + def test_with_irregular_plural_record_and_action + with_test_routes do + assert_equal "http://example.com/taxes/new", polymorphic_url(@tax, :action => 'new') + end + end + + def test_irregular_plural_url_helper_prefixed_with_new + with_test_routes do + assert_equal "http://example.com/taxes/new", new_polymorphic_url(@tax) + end + end + + def test_irregular_plural_url_helper_prefixed_with_edit + with_test_routes do + @tax.save + assert_equal "http://example.com/taxes/#{@tax.id}/edit", edit_polymorphic_url(@tax) + end + end + + def test_with_nested_irregular_plurals + with_test_routes do + @tax.save + @fax.save + assert_equal "http://example.com/taxes/#{@tax.id}/faxes/#{@fax.id}", polymorphic_url([@tax, @fax]) + end + end + + def test_with_nested_unsaved_irregular_plurals + with_test_routes do + @tax.save + assert_equal "http://example.com/taxes/#{@tax.id}/faxes", polymorphic_url([@tax, @fax]) + end + end + + def test_new_with_irregular_plural_array_and_namespace + with_admin_test_routes do + assert_equal "http://example.com/admin/taxes/new", polymorphic_url([:admin, @tax], :action => 'new') + end + end + + def test_unsaved_with_irregular_plural_array_and_namespace + with_admin_test_routes do + assert_equal "http://example.com/admin/taxes", polymorphic_url([:admin, @tax]) + end + end + + def test_nesting_with_irregular_plurals_and_array_ending_in_singleton_resource + with_test_routes do + @tax.save + assert_equal "http://example.com/taxes/#{@tax.id}/bid", polymorphic_url([@tax, :bid]) + end + end + + def test_with_array_containing_single_irregular_plural_object + with_test_routes do + @tax.save + assert_equal "http://example.com/taxes/#{@tax.id}", polymorphic_url([nil, @tax]) + end + end + + def test_with_array_containing_single_name_irregular_plural + with_test_routes do + @tax.save + assert_equal "http://example.com/taxes", polymorphic_url([:taxes]) + end + end + + # Tests for uncountable names + def test_uncountable_resource + with_test_routes do + @series.save + assert_equal "http://example.com/series/#{@series.id}", polymorphic_url(@series) + end + end + + def with_test_routes(options = {}) + with_routing do |set| + set.draw do |map| + map.resources :projects do |projects| + projects.resources :tasks + projects.resource :bid do |bid| + bid.resources :tasks + end + end + map.resources :taxes do |taxes| + taxes.resources :faxes + taxes.resource :bid + end + map.resources :series + end + + ActionController::Routing::Routes.install_helpers(self.class) + yield + end + end + + def with_admin_test_routes(options = {}) + with_routing do |set| + set.draw do |map| + map.namespace :admin do |admin| + admin.resources :projects do |projects| + projects.resources :tasks + projects.resource :bid do |bid| + bid.resources :tasks + end + end + admin.resources :taxes do |taxes| + taxes.resources :faxes + end + admin.resources :series + end + end + + ActionController::Routing::Routes.install_helpers(self.class) + yield + end + end + + def with_admin_and_site_test_routes(options = {}) + with_routing do |set| + set.draw do |map| + map.namespace :admin do |admin| + admin.resources :projects do |projects| + projects.namespace :site do |site| + site.resources :tasks do |tasks| + tasks.resources :steps + end + end + end + end + end + + ActionController::Routing::Routes.install_helpers(self.class) + yield + end + end + +end
\ No newline at end of file diff --git a/actionpack/test/controller/polymorphic_routes_test.rb b/actionpack/test/controller/polymorphic_routes_test.rb deleted file mode 100644 index 146d703619..0000000000 --- a/actionpack/test/controller/polymorphic_routes_test.rb +++ /dev/null @@ -1,293 +0,0 @@ -require 'abstract_unit' - -class Article - attr_reader :id - def save; @id = 1 end - def new_record?; @id.nil? end - def name - model = self.class.name.downcase - @id.nil? ? "new #{model}" : "#{model} ##{@id}" - end -end - -class Response < Article - def post_id; 1 end -end - -class Tag < Article - def response_id; 1 end -end - -class Tax - attr_reader :id - def save; @id = 1 end - def new_record?; @id.nil? end - def name - model = self.class.name.downcase - @id.nil? ? "new #{model}" : "#{model} ##{@id}" - end -end - -class Fax < Tax - def store_id; 1 end -end - -# TODO: test nested models -class Response::Nested < Response; end - -class PolymorphicRoutesTest < ActiveSupport::TestCase - include ActionController::PolymorphicRoutes - - def setup - @article = Article.new - @response = Response.new - @tax = Tax.new - @fax = Fax.new - end - - def test_with_record - @article.save - expects(:article_url).with(@article) - polymorphic_url(@article) - end - - def test_with_new_record - expects(:articles_url).with() - @article.expects(:new_record?).returns(true) - polymorphic_url(@article) - end - - def test_with_record_and_action - expects(:new_article_url).with() - @article.expects(:new_record?).never - polymorphic_url(@article, :action => 'new') - end - - def test_url_helper_prefixed_with_new - expects(:new_article_url).with() - new_polymorphic_url(@article) - end - - def test_url_helper_prefixed_with_edit - @article.save - expects(:edit_article_url).with(@article) - edit_polymorphic_url(@article) - end - - def test_url_helper_prefixed_with_edit_with_url_options - @article.save - expects(:edit_article_url).with(@article, :param1 => '10') - edit_polymorphic_url(@article, :param1 => '10') - end - - def test_url_helper_with_url_options - @article.save - expects(:article_url).with(@article, :param1 => '10') - polymorphic_url(@article, :param1 => '10') - end - - def test_formatted_url_helper_is_deprecated - expects(:articles_url).with(:format => :pdf) - assert_deprecated do - formatted_polymorphic_url([@article, :pdf]) - end - end - - def test_format_option - @article.save - expects(:article_url).with(@article, :format => :pdf) - polymorphic_url(@article, :format => :pdf) - end - - def test_format_option_with_url_options - @article.save - expects(:article_url).with(@article, :format => :pdf, :param1 => '10') - polymorphic_url(@article, :format => :pdf, :param1 => '10') - end - - def test_id_and_format_option - @article.save - expects(:article_url).with(:id => @article, :format => :pdf) - polymorphic_url(:id => @article, :format => :pdf) - end - - def test_with_nested - @response.save - expects(:article_response_url).with(@article, @response) - polymorphic_url([@article, @response]) - end - - def test_with_nested_unsaved - expects(:article_responses_url).with(@article) - polymorphic_url([@article, @response]) - end - - def test_new_with_array_and_namespace - expects(:new_admin_article_url).with() - polymorphic_url([:admin, @article], :action => 'new') - end - - def test_unsaved_with_array_and_namespace - expects(:admin_articles_url).with() - polymorphic_url([:admin, @article]) - end - - def test_nested_unsaved_with_array_and_namespace - @article.save - expects(:admin_article_url).with(@article) - polymorphic_url([:admin, @article]) - expects(:admin_article_responses_url).with(@article) - polymorphic_url([:admin, @article, @response]) - end - - def test_nested_with_array_and_namespace - @response.save - expects(:admin_article_response_url).with(@article, @response) - polymorphic_url([:admin, @article, @response]) - - # a ridiculously long named route tests correct ordering of namespaces and nesting: - @tag = Tag.new - @tag.save - expects(:site_admin_article_response_tag_url).with(@article, @response, @tag) - polymorphic_url([:site, :admin, @article, @response, @tag]) - end - - def test_nesting_with_array_ending_in_singleton_resource - expects(:article_response_url).with(@article) - polymorphic_url([@article, :response]) - end - - def test_nesting_with_array_containing_singleton_resource - @tag = Tag.new - @tag.save - expects(:article_response_tag_url).with(@article, @tag) - polymorphic_url([@article, :response, @tag]) - end - - def test_nesting_with_array_containing_namespace_and_singleton_resource - @tag = Tag.new - @tag.save - expects(:admin_article_response_tag_url).with(@article, @tag) - polymorphic_url([:admin, @article, :response, @tag]) - end - - def test_nesting_with_array_containing_singleton_resource_and_format - @tag = Tag.new - @tag.save - expects(:article_response_tag_url).with(@article, @tag, :format => :pdf) - polymorphic_url([@article, :response, @tag], :format => :pdf) - end - - def test_nesting_with_array_containing_singleton_resource_and_format_option - @tag = Tag.new - @tag.save - expects(:article_response_tag_url).with(@article, @tag, :format => :pdf) - polymorphic_url([@article, :response, @tag], :format => :pdf) - end - - def test_nesting_with_array_containing_nil - expects(:article_response_url).with(@article) - polymorphic_url([@article, nil, :response]) - end - - def test_with_array_containing_single_object - @article.save - expects(:article_url).with(@article) - polymorphic_url([nil, @article]) - end - - def test_with_array_containing_single_name - @article.save - expects(:articles_url) - polymorphic_url([:articles]) - end - - # TODO: Needs to be updated to correctly know about whether the object is in a hash or not - def xtest_with_hash - expects(:article_url).with(@article) - @article.save - polymorphic_url(:id => @article) - end - - def test_polymorphic_path_accepts_options - expects(:new_article_path).with() - polymorphic_path(@article, :action => :new) - end - - def test_polymorphic_path_does_not_modify_arguments - expects(:admin_article_responses_url).with(@article) - path = [:admin, @article, @response] - assert_no_difference 'path.size' do - polymorphic_url(path) - end - end - - # Tests for names where .plural.singular doesn't round-trip - def test_with_irregular_plural_record - @tax.save - expects(:taxis_url).with(@tax) - polymorphic_url(@tax) - end - - def test_with_irregular_plural_new_record - expects(:taxes_url).with() - @tax.expects(:new_record?).returns(true) - polymorphic_url(@tax) - end - - def test_with_irregular_plural_record_and_action - expects(:new_taxis_url).with() - @tax.expects(:new_record?).never - polymorphic_url(@tax, :action => 'new') - end - - def test_irregular_plural_url_helper_prefixed_with_new - expects(:new_taxis_url).with() - new_polymorphic_url(@tax) - end - - def test_irregular_plural_url_helper_prefixed_with_edit - @tax.save - expects(:edit_taxis_url).with(@tax) - edit_polymorphic_url(@tax) - end - - def test_with_nested_irregular_plurals - @fax.save - expects(:taxis_faxis_url).with(@tax, @fax) - polymorphic_url([@tax, @fax]) - end - - def test_with_nested_unsaved_irregular_plurals - expects(:taxis_faxes_url).with(@tax) - polymorphic_url([@tax, @fax]) - end - - def test_new_with_irregular_plural_array_and_namespace - expects(:new_admin_taxis_url).with() - polymorphic_url([:admin, @tax], :action => 'new') - end - - def test_unsaved_with_irregular_plural_array_and_namespace - expects(:admin_taxes_url).with() - polymorphic_url([:admin, @tax]) - end - - def test_nesting_with_irregular_plurals_and_array_ending_in_singleton_resource - expects(:taxis_faxis_url).with(@tax) - polymorphic_url([@tax, :faxis]) - end - - def test_with_array_containing_single_irregular_plural_object - @tax.save - expects(:taxis_url).with(@tax) - polymorphic_url([nil, @tax]) - end - - def test_with_array_containing_single_name_irregular_plural - @tax.save - expects(:taxes_url) - polymorphic_url([:taxes]) - end - -end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ec204d0f03..4961793866 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -640,33 +640,36 @@ module ActiveRecord def indexes(table_name, name = nil) schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') result = query(<<-SQL, name) - SELECT distinct i.relname, d.indisunique, a.attname - FROM pg_class t, pg_class i, pg_index d, pg_attribute a + SELECT distinct i.relname, d.indisunique, d.indkey, t.oid + FROM pg_class t, pg_class i, pg_index d WHERE i.relkind = 'i' AND d.indexrelid = i.oid AND d.indisprimary = 'f' AND t.oid = d.indrelid AND t.relname = '#{table_name}' AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) ) - AND a.attrelid = t.oid - AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum - OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum - OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum - OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum - OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum ) ORDER BY i.relname SQL - current_index = nil + indexes = [] - result.each do |row| - if current_index != row[0] - indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", []) - current_index = row[0] - end + indexes = result.map do |row| + index_name = row[0] + unique = row[1] == 't' + indkey = row[2].split(" ") + oid = row[3] + + columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist} + SELECT a.attname, a.attnum + FROM pg_attribute a + WHERE a.attrelid = #{oid} + AND a.attnum IN (#{indkey.join(",")}) + SQL + + column_names = indkey.map {|attnum| columns[attnum] } + IndexDefinition.new(table_name, index_name, unique, column_names) - indexes.last.columns << row[2] end indexes @@ -764,7 +767,7 @@ module ActiveRecord AND attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] AND cons.contype = 'p' - AND dep.refobjid = '#{table}'::regclass + AND dep.refobjid = '#{quote_table_name(table)}'::regclass end_sql if result.nil? or result.empty? @@ -783,7 +786,7 @@ module ActiveRecord JOIN pg_attribute attr ON (t.oid = attrelid) JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum) JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1]) - WHERE t.oid = '#{table}'::regclass + WHERE t.oid = '#{quote_table_name(table)}'::regclass AND cons.contype = 'p' AND def.adsrc ~* 'nextval' end_sql @@ -858,7 +861,7 @@ module ActiveRecord # Drops an index from a table. def remove_index(table_name, options = {}) - execute "DROP INDEX #{index_name(table_name, options)}" + execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}" end # Maps logical Rails types to PostgreSQL-specific data types. @@ -1059,7 +1062,7 @@ module ActiveRecord 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 - WHERE a.attrelid = '#{table_name}'::regclass + WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum end_sql diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index afd6472db8..05334a830a 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -150,6 +150,16 @@ module ActiveRecord %Q("#{name}") end + # Quote date/time values for use in SQL input. Includes microseconds + # if the value is a Time responding to usec. + def quoted_date(value) #:nodoc: + if value.acts_like?(:time) && value.respond_to?(:usec) + "#{super}.#{sprintf("%06d", value.usec)}" + else + super + end + end + # DATABASE STATEMENTS ====================================== diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 99d77961fc..d97cd17d75 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -456,7 +456,7 @@ class BasicsTest < ActiveRecord::TestCase ) # For adapters which support microsecond resolution. - if current_adapter?(:PostgreSQLAdapter) + if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter) assert_equal 11, Topic.find(1).written_on.sec assert_equal 223300, Topic.find(1).written_on.usec assert_equal 9900, Topic.find(2).written_on.usec @@ -1756,7 +1756,7 @@ class BasicsTest < ActiveRecord::TestCase end def test_scoped_find_with_group_and_having - developers = Developer.with_scope(:find => { :group => 'salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do + developers = Developer.with_scope(:find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do Developer.find(:all) end assert_equal 3, developers.size diff --git a/activerecord/test/cases/copy_table_test_sqlite.rb b/activerecord/test/cases/copy_table_test_sqlite.rb index 72bd7e2dab..de8af30997 100644 --- a/activerecord/test/cases/copy_table_test_sqlite.rb +++ b/activerecord/test/cases/copy_table_test_sqlite.rb @@ -10,7 +10,7 @@ class CopyTableTest < ActiveRecord::TestCase end end - def test_copy_table(from = 'companies', to = 'companies2', options = {}) + def test_copy_table(from = 'customers', to = 'customers2', options = {}) assert_nothing_raised {copy_table(from, to, options)} assert_equal row_count(from), row_count(to) @@ -24,11 +24,11 @@ class CopyTableTest < ActiveRecord::TestCase end def test_copy_table_renaming_column - test_copy_table('companies', 'companies2', - :rename => {'client_of' => 'fan_of'}) do |from, to, options| - expected = column_values(from, 'client_of') + test_copy_table('customers', 'customers2', + :rename => {'name' => 'person_name'}) do |from, to, options| + expected = column_values(from, 'name') assert expected.any?, 'only nils in resultset; real values are needed' - assert_equal expected, column_values(to, 'fan_of') + assert_equal expected, column_values(to, 'person_name') end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 17e4c755ce..f9ad7f3ba3 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -22,6 +22,11 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "sqlite_sequence"}, output end + def test_schema_dump_includes_camelcase_table_name + output = standard_dump + assert_match %r{create_table "CamelCase"}, output + end + def assert_line_up(lines, pattern, required = false) return assert(true) if lines.empty? matches = lines.map { |line| line.match(pattern) } @@ -147,6 +152,11 @@ class SchemaDumperTest < ActiveRecord::TestCase end end + def test_schema_dumps_index_columns_in_right_order + index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip + assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition + end + if current_adapter?(:MysqlAdapter) def test_schema_dump_should_not_add_default_value_for_mysql_text_field output = standard_dump diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index 2d36bd0b22..a294848fa3 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -6,6 +6,7 @@ class SchemaTest < ActiveRecord::TestCase SCHEMA_NAME = 'test_schema' SCHEMA2_NAME = 'test_schema2' TABLE_NAME = 'things' + CAPITALIZED_TABLE_NAME = 'Things' INDEX_A_NAME = 'a_index_things_on_name' INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema' INDEX_A_COLUMN = 'name' @@ -30,10 +31,15 @@ class SchemaTest < ActiveRecord::TestCase set_table_name 'test_schema."things.table"' end + class Thing4 < ActiveRecord::Base + set_table_name 'test_schema."Things"' + end + def setup @connection = ActiveRecord::Base.connection @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})" @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @@ -52,6 +58,12 @@ class SchemaTest < ActiveRecord::TestCase end end + def test_with_schema_prefixed_capitalized_table_name + assert_nothing_raised do + assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}") + end + end + def test_with_schema_search_path assert_nothing_raised do with_schema_search_path(SCHEMA_NAME) do @@ -74,21 +86,31 @@ class SchemaTest < ActiveRecord::TestCase assert_equal 0, Thing1.count assert_equal 0, Thing2.count assert_equal 0, Thing3.count + assert_equal 0, Thing4.count Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) assert_equal 1, Thing1.count assert_equal 0, Thing2.count assert_equal 0, Thing3.count + assert_equal 0, Thing4.count Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) assert_equal 1, Thing1.count assert_equal 1, Thing2.count assert_equal 0, Thing3.count + assert_equal 0, Thing4.count Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) assert_equal 1, Thing1.count assert_equal 1, Thing2.count assert_equal 1, Thing3.count + assert_equal 0, Thing4.count + + Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + assert_equal 1, Thing4.count end def test_raise_on_unquoted_schema_name @@ -113,6 +135,16 @@ class SchemaTest < ActiveRecord::TestCase do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2) end + def test_with_uppercase_index_name + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "#{SCHEMA_NAME}.things_Index"} + + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME + assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "things_Index"} + ActiveRecord::Base.connection.schema_search_path = "public" + end + private def columns(table_name) @connection.send(:column_definitions, table_name).map do |name, type, default| diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 550d4ae23c..f25b2ddf77 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -13,7 +13,7 @@ class Project < ActiveRecord::Base :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"}, :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"}, :after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"} - has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" + has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" attr_accessor :developers_log diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 5640510c96..98e6d192a5 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -68,6 +68,10 @@ ActiveRecord::Schema.define do t.boolean :value end + create_table "CamelCase", :force => true do |t| + t.string :name + end + create_table :categories, :force => true do |t| t.string :name, :null => false t.string :type @@ -114,6 +118,8 @@ ActiveRecord::Schema.define do t.integer :rating, :default => 1 end + add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index" + create_table :computers, :force => true do |t| t.integer :developer, :null => false t.integer :extendedWarranty, :null => false diff --git a/ci/ci_setup_notes.txt b/ci/ci_setup_notes.txt index b3c5936797..63f1851b19 100644 --- a/ci/ci_setup_notes.txt +++ b/ci/ci_setup_notes.txt @@ -54,12 +54,21 @@ ci ALL=NOPASSWD: /usr/local/bin/geminstaller, /usr/local/bin/ruby, /usr/loc * Install/setup nginx: $ sudo aptitude install nginx $ sudo vi /etc/nginx/sites-available/default +# Add the following entry at the top of the file above the 'server {' line: +upstream mongrel { + server 127.0.0.1:3333; +} + # Change server_name entry to match server name -# comment two lines and add one to proxy to ccrb: -# root /var/www/nginx-default; -# index index.html index.htm; - proxy_pass http://127.0.0.1:3333; +# replace the contents of the root 'location / {}' block with the following entries: + proxy_pass http://mongrel; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Client-Verify SUCCESS; + proxy_read_timeout 65; # also comment default locations for /doc and /images $ sudo /etc/init.d/nginx start diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake index 191c9361ff..b6f2f2bc18 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/tasks/framework.rake @@ -83,7 +83,9 @@ namespace :rails do desc "Applies the template supplied by LOCATION=/path/to/template" task :template do require 'rails_generator/generators/applications/app/template_runner' - Rails::TemplateRunner.new(ENV["LOCATION"]) + template = ENV["LOCATION"] + template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} + Rails::TemplateRunner.new(template) end namespace :update do |