aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--Gemfile4
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/tag.rb2
-rw-r--r--actionview/lib/action_view/test_case.rb3
-rw-r--r--actionview/test/template/test_case_test.rb11
-rw-r--r--activejob/.gitignore1
-rw-r--r--activejob/Rakefile31
-rw-r--r--activejob/lib/active_job/queue_adapters/sneakers_adapter.rb2
-rw-r--r--activejob/test/helper.rb8
-rw-r--r--activejob/test/integration/queuing_test.rb46
-rw-r--r--activejob/test/support/integration/adapters/backburner.rb38
-rw-r--r--activejob/test/support/integration/adapters/delayed_job.rb20
-rw-r--r--activejob/test/support/integration/adapters/inline.rb15
-rw-r--r--activejob/test/support/integration/adapters/qu.rb38
-rw-r--r--activejob/test/support/integration/adapters/que.rb37
-rw-r--r--activejob/test/support/integration/adapters/queue_classic.rb45
-rw-r--r--activejob/test/support/integration/adapters/resque.rb47
-rw-r--r--activejob/test/support/integration/adapters/sidekiq.rb58
-rw-r--r--activejob/test/support/integration/adapters/sneakers.rb90
-rw-r--r--activejob/test/support/integration/adapters/sucker_punch.rb5
-rw-r--r--activejob/test/support/integration/dummy_app_template.rb21
-rw-r--r--activejob/test/support/integration/helper.rb32
-rw-r--r--activejob/test/support/integration/jobs_manager.rb27
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb48
-rw-r--r--activerecord/CHANGELOG.md28
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb55
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb6
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb4
-rw-r--r--activerecord/test/cases/ar_schema_test.rb5
-rw-r--r--activerecord/test/cases/associations/required_test.rb4
-rw-r--r--activerecord/test/cases/associations_test.rb14
-rw-r--r--activerecord/test/cases/attribute_decorators_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb1
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb4
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb46
-rw-r--r--activerecord/test/cases/view_test.rb95
-rwxr-xr-xci/travis.rb13
-rw-r--r--guides/source/asset_pipeline.md4
-rw-r--r--guides/source/i18n.md16
-rw-r--r--guides/source/upgrading_ruby_on_rails.md25
-rw-r--r--railties/lib/rails/application/configuration.rb7
-rw-r--r--railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb1
-rw-r--r--railties/lib/rails/templates/rails/mailers/email.html.erb6
-rw-r--r--railties/test/application/configuration_test.rb9
52 files changed, 943 insertions, 91 deletions
diff --git a/.travis.yml b/.travis.yml
index 6e43023365..985956363a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,6 +21,7 @@ env:
- "GEM=ar:mysql2"
- "GEM=ar:sqlite3"
- "GEM=ar:postgresql"
+ - "GEM=aj:integration"
matrix:
allow_failures:
- rvm: 1.9.3
@@ -46,3 +47,8 @@ notifications:
bundler_args: --path vendor/bundle --without test
services:
- memcached
+ - redis
+ - rabbitmq
+addons:
+ postgresql: "9.3"
+
diff --git a/Gemfile b/Gemfile
index 61711a2339..eb7bdc2bdb 100644
--- a/Gemfile
+++ b/Gemfile
@@ -45,12 +45,14 @@ group :job do
gem 'sidekiq', require: false
gem 'sucker_punch', require: false
gem 'delayed_job', require: false
- gem 'queue_classic', require: false, platforms: :ruby
+ gem 'queue_classic', "< 3.0.0", require: false, platforms: :ruby
gem 'sneakers', '0.1.1.pre', require: false
gem 'que', require: false
gem 'backburner', require: false
gem 'qu-rails', github: "bkeepers/qu", branch: "master", require: false
gem 'qu-redis', require: false
+ gem 'delayed_job_active_record', require: false
+ gem 'sequel', require: false
end
# Add your own local bundler stuff
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 9450be838c..b9d5009683 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -45,8 +45,8 @@ module Mime
#
# respond_to do |format|
# format.html
- # format.ics { render text: post.to_ics, mime_type: Mime::Type["text/calendar"] }
- # format.xml { render xml: @people }
+ # format.ics { render text: @post.to_ics, mime_type: Mime::Type["text/calendar"] }
+ # format.xml { render xml: @post }
# end
# end
# end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 19eca60f70..7361e6c44b 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -1,3 +1,3 @@
require 'active_support/deprecation'
-ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::SelectorAssertions has been has been extracted to the rails-dom-testing gem.") \ No newline at end of file
+ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::SelectorAssertions has been extracted to the rails-dom-testing gem.")
diff --git a/actionpack/lib/action_dispatch/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
index d5348d80e1..5c2905d1ac 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/tag.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
@@ -1,3 +1,3 @@
require 'active_support/deprecation'
-ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::TagAssertions has been has been extracted to the rails-dom-testing gem.")
+ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::TagAssertions has been extracted to the rails-dom-testing gem.")
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index 7edfc436a6..af34d2ce5a 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -158,8 +158,7 @@ module ActionView
# Need to experiment if this priority is the best one: rendered => output_buffer
def document_root_element
- @html_document ||= Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered)
- @html_document.root
+ Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
end
def say_no_to_protect_against_forgery!
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index 4582fa13ee..5697ffa317 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -293,6 +293,17 @@ module ActionView
assert_select 'li', :text => 'foo'
end
end
+
+ test "do not memoize the document_root_element in view tests" do
+ concat form_tag('/foo')
+
+ assert_select 'form'
+
+ concat content_tag(:b, 'Strong', class: 'foo')
+
+ assert_select 'form'
+ assert_select 'b.foo'
+ end
end
class RenderTemplateTest < ActionView::TestCase
diff --git a/activejob/.gitignore b/activejob/.gitignore
new file mode 100644
index 0000000000..b3aaf55871
--- /dev/null
+++ b/activejob/.gitignore
@@ -0,0 +1 @@
+test/dummy
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 484cd1d0b8..e918428459 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -34,6 +34,12 @@ namespace :test do
tasks = ACTIVEJOB_ADAPTERS.map{|a| "isolated_test_#{a}" }
run_without_aborting(*tasks)
end
+
+ desc 'Run all adapter integration tests'
+ task :integration do
+ tasks = ACTIVEJOB_ADAPTERS.map{|a| "integration_test_#{a}" }
+ run_without_aborting(*tasks)
+ end
end
@@ -53,6 +59,15 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
end or raise 'Failures'
end
end
+
+ namespace :integration do
+ Rake::TestTask.new(adapter => "#{adapter}:env") do |t|
+ t.description = ""
+ t.libs << 'test'
+ t.test_files = FileList['test/integration/**/*_test.rb']
+ t.verbose = true
+ end
+ end
end
namespace adapter do
@@ -60,6 +75,17 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
task isolated_test: "isolated_test_#{adapter}"
task(:env) { ENV['AJADAPTER'] = adapter }
+
+ namespace :isolated do
+ task(:env) { ENV['AJADAPTER'] = adapter }
+ end
+
+ namespace :integration do
+ task(:env) do
+ ENV['AJADAPTER'] = adapter
+ ENV['AJ_INTEGRATION_TESTS'] = "1"
+ end
+ end
end
@@ -67,7 +93,10 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
desc "Run #{adapter} tests in isolation"
- task "isolated_test_#{adapter}" => ["#{adapter}:env", "test:isolated:#{adapter}"]
+ task "isolated_test_#{adapter}" => ["#{adapter}:isolated:env", "test:isolated:#{adapter}"]
+
+ desc "Run #{adapter} integration tests"
+ task "integration_test_#{adapter}" => ["#{adapter}:integration:env", "test:integration:#{adapter}"]
end
diff --git a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
index 60633639f4..1ab0a87485 100644
--- a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
@@ -21,7 +21,7 @@ module ActiveJob
class JobWrapper
include Sneakers::Worker
- from_queue 'active_jobs_default'
+ from_queue 'default'
def work(msg)
job_data = ActiveSupport::JSON.decode(msg)
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index 85094387ef..3386cef6f3 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -1,6 +1,7 @@
require File.expand_path('../../../load_paths', __FILE__)
require 'active_job'
+require 'support/job_buffer'
GlobalID.app = 'aj'
@@ -17,8 +18,13 @@ end
# Sidekiq doesn't work with MRI 1.9.3
exit if sidekiq? && ruby_193?
-require "adapters/#{@adapter}"
+if ENV['AJ_INTEGRATION_TESTS']
+ require 'support/integration/helper'
+else
+ require "adapters/#{@adapter}"
+end
require 'active_support/testing/autorun'
ActiveJob::Base.logger.level = Logger::DEBUG
+
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
new file mode 100644
index 0000000000..779dedb53f
--- /dev/null
+++ b/activejob/test/integration/queuing_test.rb
@@ -0,0 +1,46 @@
+require 'helper'
+require 'jobs/logging_job'
+require 'active_support/core_ext/numeric/time'
+
+class QueuingTest < ActiveSupport::TestCase
+ test 'should run jobs enqueued on a listenting queue' do
+ TestJob.perform_later @id
+ wait_for_jobs_to_finish_for(5.seconds)
+ assert job_executed
+ end
+
+ test 'should not run jobs queued on a non-listenting queue' do
+ begin
+ skip if adapter_is?(:inline) || adapter_is?(:sucker_punch)
+ old_queue = TestJob.queue_name
+ TestJob.queue_as :some_other_queue
+ TestJob.perform_later @id
+ wait_for_jobs_to_finish_for(2.seconds)
+ assert_not job_executed
+ ensure
+ TestJob.queue_name = old_queue
+ end
+ end
+
+ test 'should not run job enqueued in the future' do
+ begin
+ TestJob.set(wait: 10.minutes).perform_later @id
+ wait_for_jobs_to_finish_for(5.seconds)
+ assert_not job_executed
+ rescue NotImplementedError
+ skip
+ end
+ end
+
+ test 'should run job enqueued in the future at the specified time' do
+ begin
+ TestJob.set(wait: 3.seconds).perform_later @id
+ wait_for_jobs_to_finish_for(2.seconds)
+ assert_not job_executed
+ wait_for_jobs_to_finish_for(10.seconds)
+ assert job_executed
+ rescue NotImplementedError
+ skip
+ end
+ end
+end
diff --git a/activejob/test/support/integration/adapters/backburner.rb b/activejob/test/support/integration/adapters/backburner.rb
new file mode 100644
index 0000000000..0cda36a273
--- /dev/null
+++ b/activejob/test/support/integration/adapters/backburner.rb
@@ -0,0 +1,38 @@
+module BackburnerJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :backburner
+ Backburner.configure do |config|
+ config.logger = Rails.logger
+ end
+ unless can_run?
+ puts "Cannot run integration tests for backburner. To be able to run integration tests for backburner you need to install and start beanstalkd.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ tube.clear
+ end
+
+ def start_workers
+ @thread = Thread.new { Backburner.work "integration-tests" } # backburner dasherizes the queue name
+ end
+
+ def stop_workers
+ @thread.kill
+ end
+
+ def tube
+ @tube ||= Beaneater::Tube.new(Backburner::Worker.connection, "backburner.worker.queue.integration-tests") # backburner dasherizes the queue name
+ end
+
+ def can_run?
+ begin
+ Backburner::Worker.connection.send :connect!
+ rescue => e
+ return false
+ end
+ true
+ end
+end
+
diff --git a/activejob/test/support/integration/adapters/delayed_job.rb b/activejob/test/support/integration/adapters/delayed_job.rb
new file mode 100644
index 0000000000..dbd0d1a4db
--- /dev/null
+++ b/activejob/test/support/integration/adapters/delayed_job.rb
@@ -0,0 +1,20 @@
+require 'delayed_job'
+require 'delayed_job_active_record'
+
+module DelayedJobJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :delayed_job
+ end
+ def clear_jobs
+ Delayed::Job.delete_all
+ end
+
+ def start_workers
+ @worker = Delayed::Worker.new(quiet: false, sleep_delay: 0.5, queues: %w(integration_tests))
+ @thread = Thread.new { @worker.start }
+ end
+
+ def stop_workers
+ @worker.stop
+ end
+end
diff --git a/activejob/test/support/integration/adapters/inline.rb b/activejob/test/support/integration/adapters/inline.rb
new file mode 100644
index 0000000000..83c38f706f
--- /dev/null
+++ b/activejob/test/support/integration/adapters/inline.rb
@@ -0,0 +1,15 @@
+module InlineJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :inline
+ end
+
+ def clear_jobs
+ end
+
+ def start_workers
+ end
+
+ def stop_workers
+ end
+end
+
diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb
new file mode 100644
index 0000000000..e913f04a24
--- /dev/null
+++ b/activejob/test/support/integration/adapters/qu.rb
@@ -0,0 +1,38 @@
+module QuJobsManager
+ def setup
+ require 'qu-rails'
+ require 'qu-redis'
+ ActiveJob::Base.queue_adapter = :qu
+ ENV['REDISTOGO_URL'] = "tcp://127.0.0.1:6379/12"
+ backend = Qu::Backend::Redis.new
+ backend.namespace = "active_jobs_int_test"
+ Qu.backend = backend
+ Qu.logger = Rails.logger
+ Qu.interval = 0.5
+ unless can_run?
+ puts "Cannot run integration tests for qu. To be able to run integration tests for qu you need to install and start redis.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ Qu.clear "integration_tests"
+ end
+
+ def start_workers
+ @thread = Thread.new { Qu::Worker.new("integration_tests").start }
+ end
+
+ def stop_workers
+ @thread.kill
+ end
+
+ def can_run?
+ begin
+ Qu.backend.connection.client.connect
+ rescue => e
+ return false
+ end
+ true
+ end
+end
diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb
new file mode 100644
index 0000000000..a5b9b3ec0a
--- /dev/null
+++ b/activejob/test/support/integration/adapters/que.rb
@@ -0,0 +1,37 @@
+module QueJobsManager
+ def setup
+ require 'sequel'
+ ActiveJob::Base.queue_adapter = :que
+ que_url = ENV['QUE_DATABASE_URL'] || 'postgres://localhost/active_jobs_que_int_test'
+ uri = URI.parse(que_url)
+ user = uri.user||ENV['USER']
+ pass = uri.password
+ db = uri.path[1..-1]
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1}
+ Que.connection = Sequel.connect(que_url)
+ Que.migrate!
+ Que.mode = :off
+ Que.worker_count = 1
+ rescue Sequel::DatabaseConnectionError
+ puts "Cannot run integration tests for que. To be able to run integration tests for que you need to install and start postgresql.\n"
+ exit
+ end
+
+ def clear_jobs
+ Que.clear!
+ end
+
+ def start_workers
+ @thread = Thread.new do
+ loop do
+ Que::Job.work("integration_tests")
+ sleep 0.5
+ end
+ end
+ end
+
+ def stop_workers
+ @thread.kill
+ end
+end
diff --git a/activejob/test/support/integration/adapters/queue_classic.rb b/activejob/test/support/integration/adapters/queue_classic.rb
new file mode 100644
index 0000000000..81d1935132
--- /dev/null
+++ b/activejob/test/support/integration/adapters/queue_classic.rb
@@ -0,0 +1,45 @@
+module QueueClassicJobsManager
+ def setup
+ ENV['QC_DATABASE_URL'] ||= 'postgres://localhost/active_jobs_qc_int_test'
+ ENV['QC_LISTEN_TIME'] = "0.5"
+ uri = URI.parse(ENV['QC_DATABASE_URL'])
+ user = uri.user||ENV['USER']
+ pass = uri.password
+ db = uri.path[1..-1]
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1}
+ ActiveJob::Base.queue_adapter = :queue_classic
+ QC::Setup.create
+ rescue PG::ConnectionBad
+ puts "Cannot run integration tests for queue_classic. To be able to run integration tests for queue_classic you need to install and start postgresql.\n"
+ exit
+ end
+
+ def clear_jobs
+ QC::Queue.new("integration_tests").delete_all
+ retried = false
+ rescue => e
+ puts "Got exception while trying to clear jobs: #{e.inspect}"
+ if retried
+ puts "Already retried. Raising exception"
+ raise e
+ else
+ puts "Retrying"
+ retried = true
+ QC::Conn.connection = QC::Conn.connect
+ retry
+ end
+ end
+
+ def start_workers
+ @pid = fork do
+ QC::Conn.connection = QC::Conn.connect
+ worker = QC::Worker.new(q_name: 'integration_tests')
+ worker.start
+ end
+ end
+
+ def stop_workers
+ Process.kill 'HUP', @pid
+ end
+end
diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb
new file mode 100644
index 0000000000..03ffd3fd62
--- /dev/null
+++ b/activejob/test/support/integration/adapters/resque.rb
@@ -0,0 +1,47 @@
+module ResqueJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :resque
+ Resque.redis = Redis::Namespace.new 'active_jobs_int_test', redis: Redis.connect(url: "tcp://127.0.0.1:6379/12", :thread_safe => true)
+ Resque.logger = Rails.logger
+ unless can_run?
+ puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ Resque.queues.each { |queue_name| Resque.redis.del "queue:#{queue_name}" }
+ Resque.redis.keys("delayed:*").each { |key| Resque.redis.del "#{key}" }
+ Resque.redis.del "delayed_queue_schedule"
+ end
+
+ def start_workers
+ @resque_thread = Thread.new do
+ Resque::Worker.new("integration_tests").work(0.5)
+ end
+ @scheduler_thread = Thread.new do
+ Resque::Scheduler.configure do |c|
+ c.poll_sleep_amount = 0.5
+ c.dynamic = true
+ c.verbose = true
+ c.logfile = nil
+ end
+ Resque::Scheduler.master_lock.release!
+ Resque::Scheduler.run
+ end
+ end
+
+ def stop_workers
+ @resque_thread.kill
+ @scheduler_thread.kill
+ end
+
+ def can_run?
+ begin
+ Resque.redis.client.connect
+ rescue => e
+ return false
+ end
+ true
+ end
+end
diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb
new file mode 100644
index 0000000000..b3c3dcff22
--- /dev/null
+++ b/activejob/test/support/integration/adapters/sidekiq.rb
@@ -0,0 +1,58 @@
+require 'sidekiq/cli'
+require 'sidekiq/api'
+
+module SidekiqJobsManager
+
+ def setup
+ ActiveJob::Base.queue_adapter = :sidekiq
+ unless can_run?
+ puts "Cannot run integration tests for sidekiq. To be able to run integration tests for sidekiq you need to install and start redis.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ Sidekiq::ScheduledSet.new.clear
+ Sidekiq::Queue.new("integration_tests").clear
+ end
+
+ def start_workers
+ fork do
+ sidekiq = Sidekiq::CLI.instance
+ logfile = Rails.root.join("log/sidekiq.log").to_s
+ pidfile = Rails.root.join("tmp/sidekiq.pid").to_s
+ sidekiq.parse([ "--require", Rails.root.to_s,
+ "--queue", "integration_tests",
+ "--logfile", logfile,
+ "--pidfile", pidfile,
+ "--environment", "test",
+ "--concurrency", "1",
+ "--timeout", "1",
+ "--daemon",
+ "--verbose"
+ ])
+ require 'celluloid'
+ require 'sidekiq/scheduled'
+ Sidekiq.poll_interval = 0.5
+ Sidekiq::Scheduled.const_set :INITIAL_WAIT, 1
+ sidekiq.run
+ end
+ sleep 1
+ end
+
+ def stop_workers
+ pidfile = Rails.root.join("tmp/sidekiq.pid").to_s
+ Process.kill 'TERM', File.open(pidfile).read.to_i
+ FileUtils.rm_f pidfile
+ rescue
+ end
+
+ def can_run?
+ begin
+ Sidekiq.redis { |conn| conn.connect }
+ rescue => e
+ return false
+ end
+ true
+ end
+end
diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb
new file mode 100644
index 0000000000..f21bb38a32
--- /dev/null
+++ b/activejob/test/support/integration/adapters/sneakers.rb
@@ -0,0 +1,90 @@
+require 'sneakers/runner'
+require 'sneakers/publisher'
+require 'timeout'
+
+module Sneakers
+ class Publisher
+ def safe_ensure_connected
+ @mutex.synchronize do
+ ensure_connection! unless connected?
+ end
+ end
+ end
+end
+
+
+module SneakersJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :sneakers
+ Sneakers.configure :heartbeat => 2,
+ :amqp => 'amqp://guest:guest@localhost:5672',
+ :vhost => '/',
+ :exchange => 'active_jobs_sneakers_int_test',
+ :exchange_type => :direct,
+ :daemonize => true,
+ :threads => 1,
+ :workers => 1,
+ :pid_path => Rails.root.join("tmp/sneakers.pid").to_s,
+ :log => Rails.root.join("log/sneakers.log").to_s
+ unless can_run?
+ puts "Cannot run integration tests for sneakers. To be able to run integration tests for sneakers you need to install and start rabbitmq.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ bunny_queue.purge
+ end
+
+ def start_workers
+ @pid = fork do
+ queues = %w(integration_tests)
+ workers = queues.map do |q|
+ worker_klass = "ActiveJobWorker"+Digest::MD5.hexdigest(q)
+ Sneakers.const_set(worker_klass, Class.new(ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper) do
+ from_queue q
+ end)
+ end
+ Sneakers::Runner.new(workers).run
+ end
+ begin
+ Timeout.timeout(10) do
+ while bunny_queue.status[:consumer_count] == 0
+ sleep 0.5
+ end
+ end
+ rescue Timeout::Error
+ stop_workers
+ raise "Failed to start sneakers worker"
+ end
+ end
+
+ def stop_workers
+ Process.kill 'TERM', @pid
+ Process.kill 'TERM', File.open(Rails.root.join("tmp/sneakers.pid").to_s).read.to_i
+ rescue
+ end
+
+ def can_run?
+ begin
+ bunny_publisher
+ rescue => e
+ return false
+ end
+ true
+ end
+
+ protected
+ def bunny_publisher
+ @bunny_publisher ||= begin
+ p = ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper.send(:publisher)
+ p.safe_ensure_connected
+ p
+ end
+ end
+
+ def bunny_queue
+ @queue ||= bunny_publisher.exchange.channel.queue "integration_tests", durable: true
+ end
+
+end
diff --git a/activejob/test/support/integration/adapters/sucker_punch.rb b/activejob/test/support/integration/adapters/sucker_punch.rb
new file mode 100644
index 0000000000..691ba35c90
--- /dev/null
+++ b/activejob/test/support/integration/adapters/sucker_punch.rb
@@ -0,0 +1,5 @@
+module SuckerPunchJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :sucker_punch
+ end
+end
diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb
new file mode 100644
index 0000000000..28aae0f884
--- /dev/null
+++ b/activejob/test/support/integration/dummy_app_template.rb
@@ -0,0 +1,21 @@
+if ENV['AJADAPTER'] == 'delayed_job'
+ generate "delayed_job:active_record"
+ rake("db:migrate")
+end
+
+initializer 'activejob.rb', <<-CODE
+require "#{File.expand_path("../jobs_manager.rb", __FILE__)}"
+JobsManager.current_manager.setup
+CODE
+
+file 'app/jobs/test_job.rb', <<-CODE
+class TestJob < ActiveJob::Base
+ queue_as :integration_tests
+
+ def perform(x)
+ File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f|
+ f.write x
+ end
+ end
+end
+CODE
diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb
new file mode 100644
index 0000000000..ccd5036b36
--- /dev/null
+++ b/activejob/test/support/integration/helper.rb
@@ -0,0 +1,32 @@
+puts "\n\n"
+puts "*** Running integration tests for #{ENV['AJADAPTER']} ***"
+puts "\n\n"
+
+ENV["RAILS_ENV"] = "test"
+ActiveJob::Base.queue_name_prefix = nil
+
+require 'rails/generators/rails/app/app_generator'
+
+dummy_app_path = Dir.mktmpdir + "/dummy"
+dummy_app_template = File.expand_path("../dummy_app_template.rb", __FILE__)
+args = Rails::Generators::ARGVScrubber.new(["new", dummy_app_path, "--skip-gemfile", "--skip-bundle",
+ "--skip-git", "--skip-spring", "-d", "sqlite3", "--skip-javascript", "--force", "--quite",
+ "--template", dummy_app_template]).prepare!
+Rails::Generators::AppGenerator.start args
+
+require "#{dummy_app_path}/config/environment.rb"
+
+ActiveRecord::Migrator.migrations_paths = [ Rails.root.join('db/migrate').to_s ]
+require 'rails/test_help'
+
+Rails.backtrace_cleaner.remove_silencers!
+
+require_relative 'test_case_helpers'
+ActiveSupport::TestCase.send(:include, TestCaseHelpers)
+
+JobsManager.current_manager.start_workers
+
+Minitest.after_run do
+ JobsManager.current_manager.stop_workers
+ JobsManager.current_manager.clear_jobs
+end
diff --git a/activejob/test/support/integration/jobs_manager.rb b/activejob/test/support/integration/jobs_manager.rb
new file mode 100644
index 0000000000..4df34aaeb1
--- /dev/null
+++ b/activejob/test/support/integration/jobs_manager.rb
@@ -0,0 +1,27 @@
+class JobsManager
+ @@managers = {}
+ attr :adapter_name
+
+ def self.current_manager
+ @@managers[ENV['AJADAPTER']] ||= new(ENV['AJADAPTER'])
+ end
+
+ def initialize(adapter_name)
+ @adapter_name = adapter_name
+ require_relative "adapters/#{adapter_name}"
+ extend "#{adapter_name.camelize}JobsManager".constantize
+ end
+
+ def setup
+ ActiveJob::Base.queue_adapter = nil
+ end
+
+ def clear_jobs
+ end
+
+ def start_workers
+ end
+
+ def stop_workers
+ end
+end
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
new file mode 100644
index 0000000000..ee2f6aebea
--- /dev/null
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -0,0 +1,48 @@
+require 'active_support/concern'
+require 'support/integration/jobs_manager'
+
+module TestCaseHelpers
+ extend ActiveSupport::Concern
+
+ included do
+ self.use_transactional_fixtures = false
+
+ setup do
+ clear_jobs
+ @id = "AJ-#{SecureRandom.uuid}"
+ end
+
+ teardown do
+ clear_jobs
+ end
+ end
+
+ protected
+
+ def jobs_manager
+ JobsManager.current_manager
+ end
+
+ def clear_jobs
+ jobs_manager.clear_jobs
+ end
+
+ def adapter_is?(adapter)
+ ActiveJob::Base.queue_adapter.name.split("::").last.gsub(/Adapter$/, '').underscore==adapter.to_s
+ end
+
+ def wait_for_jobs_to_finish_for(seconds=60)
+ begin
+ Timeout.timeout(seconds) do
+ while !job_executed do
+ sleep 0.25
+ end
+ end
+ rescue Timeout::Error
+ end
+ end
+
+ def job_executed
+ Dummy::Application.root.join("tmp/#{@id}").exist?
+ end
+end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 001338b46c..7d4869e113 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,31 @@
+* Include default column limits in schema.rb. Allows defaults to be changed
+ in the future without affecting old migrations that assumed old defaults.
+
+ *Jeremy Kemper*
+
+* MySQL: schema.rb now includes TEXT and BLOB column limits.
+
+ *Jeremy Kemper*
+
+* MySQL: correct LONGTEXT and LONGBLOB limits from 2GB to their true 4GB.
+
+ *Jeremy Kemper*
+
+* SQLite3Adapter now checks for views in `table_exists?`. Fixes #14041.
+
+ *Girish Sonawane*
+
+* Introduce `connection.supports_views?` to check wether the current adapter
+ has support for SQL views. Connection adapters should define this method.
+
+ *Yves Senn*
+
+* Allow included modules to override association methods.
+
+ Fixes #16684.
+
+ *Yves Senn*
+
* Schema loading rake tasks (like `db:schema:load` and `db:setup`) maintain
the database connection to the current environment.
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 7519fec10a..46bccbf15a 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -2,33 +2,42 @@ module ActiveRecord
module Associations
# Implements the details of eager loading of Active Record associations.
#
- # Note that 'eager loading' and 'preloading' are actually the same thing.
- # However, there are two different eager loading strategies.
+ # Suppose that you have the following two Active Record models:
#
- # The first one is by using table joins. This was only strategy available
- # prior to Rails 2.1. Suppose that you have an Author model with columns
- # 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using
- # this strategy, Active Record would try to retrieve all data for an author
- # and all of its books via a single query:
+ # class Author < ActiveRecord::Base
+ # # columns: name, age
+ # has_many :books
+ # end
#
- # SELECT * FROM authors
- # LEFT OUTER JOIN books ON authors.id = books.author_id
- # WHERE authors.name = 'Ken Akamatsu'
+ # class Book < ActiveRecord::Base
+ # # columns: title, sales
+ # end
#
- # However, this could result in many rows that contain redundant data. After
- # having received the first row, we already have enough data to instantiate
- # the Author object. In all subsequent rows, only the data for the joined
- # 'books' table is useful; the joined 'authors' data is just redundant, and
- # processing this redundant data takes memory and CPU time. The problem
- # quickly becomes worse and worse as the level of eager loading increases
- # (i.e. if Active Record is to eager load the associations' associations as
- # well).
+ # When you load an author with all associated books Active Record will make
+ # multiple queries like this:
+ #
+ # Author.includes(:books).where(:name => ['bell hooks', 'Homer').to_a
+ #
+ # => SELECT `authors`.* FROM `authors` WHERE `name` IN ('bell hooks', 'Homer')
+ # => SELECT `books`.* FROM `books` WHERE `author_id` IN (2, 5)
+ #
+ # Active Record saves the ids of the records from the first query to use in
+ # the second. Depending on the number of associations involved there can be
+ # arbitrarily many SQL queries made.
+ #
+ # However, if there is a WHERE clause that spans across tables Active
+ # Record will fall back to a slightly more resource-intensive single query:
+ #
+ # Author.includes(:books).where(books: {title: 'Illiad'}).to_a
+ # => SELECT `authors`.`id` AS t0_r0, `authors`.`name` AS t0_r1, `authors`.`age` AS t0_r2,
+ # `books`.`id` AS t1_r0, `books`.`title` AS t1_r1, `books`.`sales` AS t1_r2
+ # FROM `authors`
+ # LEFT OUTER JOIN `books` ON `authors`.`id` = `books`.`author_id`
+ # WHERE `books`.`title` = 'Illiad'
+ #
+ # This could result in many rows that contain redundant data and it performs poorly at scale
+ # and is therefore only used when necessary.
#
- # The second strategy is to use multiple database queries, one for each
- # level of association. Since Rails 2.1, this is the default strategy. In
- # situations where a table join is necessary (e.g. when the +:conditions+
- # option references an association's column), it will fallback to the table
- # join strategy.
class Preloader #:nodoc:
extend ActiveSupport::Autoload
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 43a3993898..36d0d63c71 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -69,6 +69,8 @@ module ActiveRecord
@generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
@attribute_methods_generated = false
include @generated_attribute_methods
+
+ super
end
# Generates all the attribute related methods for columns in the database
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 9bd0401e40..b05a4f8440 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -19,12 +19,16 @@ module ActiveRecord
spec = {}
spec[:name] = column.name.inspect
spec[:type] = column.type.to_s
- spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit]
+ spec[:null] = 'false' unless column.null
+
+ limit = column.limit || types[column.type][:limit]
+ spec[:limit] = limit.inspect if limit
spec[:precision] = column.precision.inspect if column.precision
spec[:scale] = column.scale.inspect if column.scale
- spec[:null] = 'false' unless column.null
- spec[:default] = schema_default(column) if column.has_default?
- spec.delete(:default) if spec[:default].nil?
+
+ default = schema_default(column) if column.has_default?
+ spec[:default] = default unless default.nil?
+
spec
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 2ee5a88f2a..a0d9086875 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -240,6 +240,11 @@ module ActiveRecord
false
end
+ # Does this adapter support views?
+ def supports_views?
+ false
+ end
+
# This is meant to be implemented by the adapters that support extensions
def disable_extension(name)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 3a24ecac77..037fb69dfb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -197,6 +197,10 @@ module ActiveRecord
true
end
+ def supports_views?
+ version[0] >= 5
+ end
+
def native_database_types
NATIVE_DATABASE_TYPES
end
@@ -636,18 +640,21 @@ module ActiveRecord
def initialize_type_map(m) # :nodoc:
super
+
m.register_type(%r(enum)i) do |sql_type|
limit = sql_type[/^enum\((.+)\)/i, 1]
.split(',').map{|enum| enum.strip.length - 2}.max
Type::String.new(limit: limit)
end
- m.register_type %r(tinytext)i, Type::Text.new(limit: 255)
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 255)
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 16777215)
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 16777215)
- m.register_type %r(longtext)i, Type::Text.new(limit: 2147483647)
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2147483647)
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
m.register_type %r(^bigint)i, Type::Integer.new(limit: 8)
m.register_type %r(^int)i, Type::Integer.new(limit: 4)
m.register_type %r(^mediumint)i, Type::Integer.new(limit: 3)
@@ -779,10 +786,6 @@ module ActiveRecord
full_version =~ /mariadb/i
end
- def supports_views?
- version[0] >= 5
- end
-
def supports_rename_index?
mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 52a03d0e6d..80461f3910 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -158,6 +158,10 @@ module ActiveRecord
true
end
+ def supports_views?
+ true
+ end
+
def index_algorithms
{ concurrently: 'CONCURRENTLY' }
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 4481df974d..ebb311df57 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -183,6 +183,10 @@ module ActiveRecord
true
end
+ def supports_views?
+ true
+ end
+
def active?
@active != false
end
@@ -369,7 +373,7 @@ module ActiveRecord
sql = <<-SQL
SELECT name
FROM sqlite_master
- WHERE type = 'table' AND NOT name = 'sqlite_sequence'
+ WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
SQL
sql << " AND name = #{quote_table_name(table_name)}" if table_name
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 2e9c9e3197..83859e474a 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -183,8 +183,6 @@ module ActiveRecord
end
def initialize_generated_modules
- super
-
generated_association_methods
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index b2bf9480dd..8c1c22d3bf 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -297,7 +297,7 @@ module ActiveRecord
def test_tables_logs_name
sql = <<-SQL
SELECT name FROM sqlite_master
- WHERE type = 'table' AND NOT name = 'sqlite_sequence'
+ WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
SQL
assert_logged [[sql.squish, 'SCHEMA', []]] do
@conn.tables('hello')
@@ -316,7 +316,7 @@ module ActiveRecord
with_example_table do
sql = <<-SQL
SELECT name FROM sqlite_master
- WHERE type = 'table'
+ WHERE (type = 'table' OR type = 'view')
AND NOT name = 'sqlite_sequence' AND name = \"ex\"
SQL
assert_logged [[sql.squish, 'SCHEMA', []]] do
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 3f5858714a..b6333240a8 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -5,7 +5,9 @@ if ActiveRecord::Base.connection.supports_migrations?
class ActiveRecordSchemaTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
- def setup
+ setup do
+ @original_verbose = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
@connection = ActiveRecord::Base.connection
ActiveRecord::SchemaMigration.drop_table
end
@@ -16,6 +18,7 @@ if ActiveRecord::Base.connection.supports_migrations?
@connection.drop_table :nep_schema_migrations rescue nil
@connection.drop_table :has_timestamps rescue nil
ActiveRecord::SchemaMigration.delete_all rescue nil
+ ActiveRecord::Migration.verbose = @original_verbose
end
def test_has_no_primary_key
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index 24a332bb4a..321fb6c8dd 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -18,8 +18,8 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
end
teardown do
- @connection.execute("DROP TABLE parents") if @connection.table_exists? 'parents'
- @connection.execute("DROP TABLE children") if @connection.table_exists? 'children'
+ @connection.drop_table 'parents' if @connection.table_exists? 'parents'
+ @connection.drop_table 'children' if @connection.table_exists? 'children'
end
test "belongs_to associations are not required by default" do
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 635c657d9b..9b0cf4c18f 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -357,4 +357,18 @@ class GeneratedMethodsTest < ActiveRecord::TestCase
def test_model_method_overrides_association_method
assert_equal(comments(:greetings).body, posts(:welcome).first_comment)
end
+
+ module MyModule
+ def comments; :none end
+ end
+
+ class MyArticle < ActiveRecord::Base
+ self.table_name = "articles"
+ include MyModule
+ has_many :comments, inverse_of: false
+ end
+
+ def test_included_module_overwrites_association_methods
+ assert_equal :none, MyArticle.new.comments
+ end
end
diff --git a/activerecord/test/cases/attribute_decorators_test.rb b/activerecord/test/cases/attribute_decorators_test.rb
index 644876752e..53bd58e22e 100644
--- a/activerecord/test/cases/attribute_decorators_test.rb
+++ b/activerecord/test/cases/attribute_decorators_test.rb
@@ -28,7 +28,7 @@ module ActiveRecord
teardown do
return unless @connection
- @connection.execute 'DROP TABLE attribute_decorators_model' if @connection.table_exists? 'attribute_decorators_model'
+ @connection.drop_table 'attribute_decorators_model' if @connection.table_exists? 'attribute_decorators_model'
Model.attribute_type_decorations.clear
Model.reset_column_information
end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 4741ee8799..e38b32d7fc 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -13,6 +13,7 @@ module ActiveRecord
def self.superclass; Base; end
def self.base_class; self; end
def self.decorate_matching_attribute_types(*); end
+ def self.initialize_generated_modules; end
include ActiveRecord::AttributeMethods
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index ba28d55e4c..406dd70c37 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -29,8 +29,8 @@ module ActiveRecord
teardown do
if defined?(@connection)
- @connection.execute "DROP TABLE astronauts" if @connection.table_exists? 'astronauts'
- @connection.execute "DROP TABLE rockets" if @connection.table_exists? 'rockets'
+ @connection.drop_table "astronauts" if @connection.table_exists? 'astronauts'
+ @connection.drop_table "rockets" if @connection.table_exists? 'rockets'
end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index d5584ad06c..d7ee56374d 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -10,12 +10,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
def standard_dump
- @stream = StringIO.new
- old_ignore_tables, ActiveRecord::SchemaDumper.ignore_tables = ActiveRecord::SchemaDumper.ignore_tables, []
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, @stream)
- @stream.string
- ensure
- ActiveRecord::SchemaDumper.ignore_tables = old_ignore_tables
+ @@standard_dump ||= perform_schema_dump
+ end
+
+ def perform_schema_dump
+ dump_all_table_schema []
end
def test_dump_schema_information_outputs_lexically_ordered_versions
@@ -31,8 +30,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
def test_magic_comment
- output = standard_dump
- assert_match "# encoding: #{@stream.external_encoding.name}", output
+ assert_match "# encoding: #{Encoding.default_external.name}", standard_dump
end
def test_schema_dump
@@ -100,7 +98,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_limit_constraint_for_integer_columns
output = dump_all_table_schema([/^(?!integer_limits)/])
+ assert_match %r{c_int_without_limit}, output
+
if current_adapter?(:PostgreSQLAdapter)
+ assert_no_match %r{c_int_without_limit.*limit:}, output
+
assert_match %r{c_int_1.*limit: 2}, output
assert_match %r{c_int_2.*limit: 2}, output
@@ -111,6 +113,8 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_4.*}, output
assert_no_match %r{c_int_4.*limit:}, output
elsif current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+ assert_match %r{c_int_without_limit.*limit: 4}, output
+
assert_match %r{c_int_1.*limit: 1}, output
assert_match %r{c_int_2.*limit: 2}, output
assert_match %r{c_int_3.*limit: 3}, output
@@ -118,13 +122,13 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_4.*}, output
assert_no_match %r{c_int_4.*:limit}, output
elsif current_adapter?(:SQLite3Adapter)
+ assert_no_match %r{c_int_without_limit.*limit:}, output
+
assert_match %r{c_int_1.*limit: 1}, output
assert_match %r{c_int_2.*limit: 2}, output
assert_match %r{c_int_3.*limit: 3}, output
assert_match %r{c_int_4.*limit: 4}, output
end
- assert_match %r{c_int_without_limit.*}, output
- assert_no_match %r{c_int_without_limit.*limit:}, output
if current_adapter?(:SQLite3Adapter)
assert_match %r{c_int_5.*limit: 5}, output
@@ -203,9 +207,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- def test_schema_dump_should_not_add_default_value_for_mysql_text_field
+ def test_schema_dump_should_add_default_value_for_mysql_text_field
output = standard_dump
- assert_match %r{t.text\s+"body",\s+null: false$}, output
+ assert_match %r{t.text\s+"body",\s+limit: 65535,\s+null: false$}, output
end
def test_schema_dump_includes_length_for_mysql_binary_fields
@@ -217,13 +221,13 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
output = standard_dump
assert_match %r{t.binary\s+"tiny_blob",\s+limit: 255$}, output
- assert_match %r{t.binary\s+"normal_blob"$}, output
+ assert_match %r{t.binary\s+"normal_blob",\s+limit: 65535$}, output
assert_match %r{t.binary\s+"medium_blob",\s+limit: 16777215$}, output
- assert_match %r{t.binary\s+"long_blob",\s+limit: 2147483647$}, output
+ assert_match %r{t.binary\s+"long_blob",\s+limit: 4294967295$}, output
assert_match %r{t.text\s+"tiny_text",\s+limit: 255$}, output
- assert_match %r{t.text\s+"normal_text"$}, output
+ assert_match %r{t.text\s+"normal_text",\s+limit: 65535$}, output
assert_match %r{t.text\s+"medium_text",\s+limit: 16777215$}, output
- assert_match %r{t.text\s+"long_text",\s+limit: 2147483647$}, output
+ assert_match %r{t.text\s+"long_text",\s+limit: 4294967295$}, output
end
def test_schema_dumps_index_type
@@ -249,12 +253,12 @@ class SchemaDumperTest < ActiveRecord::TestCase
connection = ActiveRecord::Base.connection
connection.stubs(:extensions).returns(['hstore'])
- output = standard_dump
+ output = perform_schema_dump
assert_match "# These are extensions that must be enabled", output
assert_match %r{enable_extension "hstore"}, output
connection.stubs(:extensions).returns([])
- output = standard_dump
+ output = perform_schema_dump
assert_no_match "# These are extensions that must be enabled", output
assert_no_match %r{enable_extension}, output
end
@@ -354,7 +358,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
match = output.match(%r{create_table "goofy_string_id"(.*)do.*\n(.*)\n})
assert_not_nil(match, "goofy_string_id table not found")
assert_match %r(id: false), match[1], "no table id not preserved"
- assert_match %r{t.string[[:space:]]+"id",[[:space:]]+null: false$}, match[2], "non-primary key id column not preserved"
+ assert_match %r{t.string\s+"id",.*?null: false$}, match[2], "non-primary key id column not preserved"
end
def test_schema_dump_keeps_id_false_when_id_is_false_and_unique_not_null_column_added
@@ -395,7 +399,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
migration = CreateDogMigration.new
migration.migrate(:up)
- output = standard_dump
+ output = perform_schema_dump
assert_no_match %r{create_table "foo_.+_bar"}, output
assert_no_match %r{add_index "foo_.+_bar"}, output
assert_no_match %r{create_table "schema_migrations"}, output
@@ -433,7 +437,7 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
def test_schema_dump_defaults_with_universally_supported_types
output = dump_table_schema('defaults')
- assert_match %r{t\.string\s+"string_with_default",\s+default: "Hello!"}, output
+ assert_match %r{t\.string\s+"string_with_default",.*?default: "Hello!"}, output
assert_match %r{t\.date\s+"date_with_default",\s+default: '2014-06-05'}, output
assert_match %r{t\.datetime\s+"datetime_with_default",\s+default: '2014-06-05 07:17:04'}, output
assert_match %r{t\.time\s+"time_with_default",\s+default: '2000-01-01 07:17:04'}, output
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
new file mode 100644
index 0000000000..357265aa05
--- /dev/null
+++ b/activerecord/test/cases/view_test.rb
@@ -0,0 +1,95 @@
+require "cases/helper"
+require "models/book"
+
+if ActiveRecord::Base.connection.supports_views?
+class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
+ fixtures :books
+
+ class Ebook < ActiveRecord::Base
+ self.primary_key = "id"
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.execute <<-SQL
+ CREATE VIEW ebooks
+ AS SELECT id, name, status FROM books WHERE format = 'ebook'
+ SQL
+ end
+
+ teardown do
+ @connection.execute "DROP VIEW ebooks" if @connection.table_exists? "ebooks"
+ end
+
+ def test_reading
+ books = Ebook.all
+ assert_equal [books(:rfr).id], books.map(&:id)
+ assert_equal ["Ruby for Rails"], books.map(&:name)
+ end
+
+ def test_table_exists
+ view_name = Ebook.table_name
+ assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
+ end
+
+ def test_column_definitions
+ assert_equal([["id", :integer],
+ ["name", :string],
+ ["status", :integer]], Ebook.columns.map { |c| [c.name, c.type] })
+ end
+
+ def test_attributes
+ assert_equal({"id" => 2, "name" => "Ruby for Rails", "status" => 0},
+ Ebook.first.attributes)
+ end
+
+ def test_does_not_assume_id_column_as_primary_key
+ model = Class.new(ActiveRecord::Base) do
+ self.table_name = "ebooks"
+ end
+ assert_nil model.primary_key
+ end
+end
+
+class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
+ fixtures :books
+
+ class Paperback < ActiveRecord::Base; end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.execute <<-SQL
+ CREATE VIEW paperbacks
+ AS SELECT name, status FROM books WHERE format = 'paperback'
+ SQL
+ end
+
+ teardown do
+ @connection.execute "DROP VIEW paperbacks" if @connection.table_exists? "paperbacks"
+ end
+
+ def test_reading
+ books = Paperback.all
+ assert_equal ["Agile Web Development with Rails"], books.map(&:name)
+ end
+
+ def test_table_exists
+ view_name = Paperback.table_name
+ assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
+ end
+
+ def test_column_definitions
+ assert_equal([["name", :string],
+ ["status", :integer]], Paperback.columns.map { |c| [c.name, c.type] })
+ end
+
+ def test_attributes
+ assert_equal({"name" => "Agile Web Development with Rails", "status" => 0},
+ Paperback.first.attributes)
+ end
+
+ def test_does_not_have_a_primary_key
+ assert_nil Paperback.primary_key
+ end
+end
+end
diff --git a/ci/travis.rb b/ci/travis.rb
index db6e41ecfa..3ccbd1dad4 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -48,6 +48,7 @@ class Build
heading = [gem]
heading << "with #{adapter}" if activerecord?
heading << "in isolation" if isolated?
+ heading << "integration" if integration?
heading.join(' ')
end
@@ -55,7 +56,7 @@ class Build
if activerecord?
['db:mysql:rebuild', "#{adapter}:#{'isolated_' if isolated?}test"]
else
- ["test#{':isolated' if isolated?}"]
+ ["test", ('isolated' if isolated?), ('integration' if integration?)].compact.join(":")
end
end
@@ -74,6 +75,10 @@ class Build
options[:isolated]
end
+ def integration?
+ component.split(':').last == 'integration'
+ end
+
def gem
MAP[component.split(':').first]
end
@@ -93,11 +98,17 @@ class Build
end
end
+if ENV['GEM']=='aj:integration'
+ ENV['QC_DATABASE_URL'] = 'postgres://postgres@localhost/active_jobs_qc_int_test'
+ ENV['QUE_DATABASE_URL'] = 'postgres://postgres@localhost/active_jobs_que_int_test'
+end
+
results = {}
ENV['GEM'].split(',').each do |gem|
[false, true].each do |isolated|
next if gem == 'railties' && isolated
+ next if gem == 'aj:integration' && isolated
build = Build.new(gem, :isolated => isolated)
results[build.key] = build.run!
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 15d743fb33..2a8940684f 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -208,9 +208,7 @@ precompiling works.
NOTE: You must have an ExecJS supported runtime in order to use CoffeeScript.
If you are using Mac OS X or Windows, you have a JavaScript runtime installed in
-your operating system. Check
-[ExecJS](https://github.com/sstephenson/execjs#readme) documentation to know all
-supported JavaScript runtimes.
+your operating system. Check [ExecJS](https://github.com/sstephenson/execjs#readme) documentation to know all supported JavaScript runtimes.
You can also disable generation of controller specific asset files by adding the
following to your `config/application.rb` configuration:
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 1023598aa4..0761d5b39c 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -676,6 +676,22 @@ en:
<div><%= t('title.html') %></div>
```
+Interpolation escapes as needed though. For example, given:
+
+```yaml
+en:
+ welcome_html: "<b>Welcome %{username}!</b>"
+```
+
+you can safely pass the username as set by the user:
+
+```erb
+<%# This is safe, it is going to be escaped if needed. %>
+<%= t('welcome_html', username: @current_user.username %>
+```
+
+Safe strings on the other hand are interpolated verbatim.
+
NOTE: Automatic conversion to HTML safe translate text is only available from the `translate` view helper method.
![i18n demo html safe](images/i18n/demo_html_safe.png)
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 71358ad3f5..120dea51e9 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -8,7 +8,7 @@ This guide provides steps to be followed when you upgrade your applications to a
General Advice
--------------
-Before attempting to upgrade an existing application, you should be sure you have a good reason to upgrade. You need to balance out several factors: the need for new features, the increasing difficulty of finding support for old code, and your available time and skills, to name a few.
+Before attempting to upgrade an existing application, you should be sure you have a good reason to upgrade. You need to balance several factors: the need for new features, the increasing difficulty of finding support for old code, and your available time and skills, to name a few.
### Test Coverage
@@ -55,7 +55,11 @@ a [pull request](https://github.com/rails/rails/edit/master/guides/source/upgrad
### Web Console
-TODO: setup instructions for web console on existing apps.
+First, add `gem 'web-console', '~> 2.0.0.beta3'` to the `:development` group in your Gemfile and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment.
+
+Additionally, you can tell Rails to automatically mount a VT100-compatible console on a predetermined path by setting `config.web_console.automount = true` in your `config/environments/development.rb`. You can specify the path by setting `config.web_console.default_mount_path` (note that this defaults to `/console`).
+
+TODO: Update `web-console` version to release version.
### Responders
@@ -63,7 +67,22 @@ TODO: mention https://github.com/rails/rails/pull/16526
### Error handling in transaction callbacks
-TODO: mention https://github.com/rails/rails/pull/16537
+Currently, Active Record suppresses errors raised
+within `after_rollback` or `after_commit` callbacks and only prints them to
+the logs. In the next version, these errors will no longer be suppressed.
+Instead, the errors will propagate normally just like in other Active
+Record callbacks.
+
+When you define a `after_rollback` or `after_commit` callback, you
+will receive a deprecation warning about this upcoming change. When
+you are ready, you can opt into the new behvaior and remove the
+deprecation warning by adding following configuration to your
+`config/application.rb`:
+
+ config.active_record.raise_in_transactional_callbacks = true
+
+See [#14488](https://github.com/rails/rails/pull/14488) and
+[#16537](https://github.com/rails/rails/pull/16537) for more details.
### Ordering of test cases
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 2c16b83f8a..786dcee007 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -93,9 +93,10 @@ module Rails
# Loads and returns the entire raw configuration of database from
# values stored in `config/database.yml`.
def database_configuration
- yaml = Pathname.new(paths["config/database"].existent.first || "")
+ path = paths["config/database"].existent.first
+ yaml = Pathname.new(path) if path
- config = if yaml.exist?
+ config = if yaml && yaml.exist?
require "yaml"
require "erb"
YAML.load(ERB.new(yaml.read).result) || {}
@@ -104,7 +105,7 @@ module Rails
# by Active Record.
{}
else
- raise "Could not load database configuration. No such file - #{yaml}"
+ raise "Could not load database configuration. No such file - #{paths["config/database"].instance_variable_get(:@paths)}"
end
config
diff --git a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
index feb250e89a..f5351d0ec6 100644
--- a/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
+++ b/railties/lib/rails/generators/test_unit/job/templates/unit_test.rb.erb
@@ -2,7 +2,6 @@ require 'test_helper'
<% module_namespacing do -%>
class <%= class_name %>JobTest < ActiveJob::TestCase
-
# test "the truth" do
# assert true
# end
diff --git a/railties/lib/rails/templates/rails/mailers/email.html.erb b/railties/lib/rails/templates/rails/mailers/email.html.erb
index 977feb922b..1dc1d70f8d 100644
--- a/railties/lib/rails/templates/rails/mailers/email.html.erb
+++ b/railties/lib/rails/templates/rails/mailers/email.html.erb
@@ -2,6 +2,10 @@
<html><head>
<meta name="viewport" content="width=device-width" />
<style type="text/css">
+ body {
+ margin: 0;
+ }
+
header {
width: 100%;
padding: 10px 0 0 0;
@@ -95,4 +99,4 @@
<iframe seamless name="messageBody" src="?part=<%= Rack::Utils.escape(@part.mime_type) %>"></iframe>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index db2106aed3..f8b4ee30d8 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -980,6 +980,15 @@ module ApplicationTests
assert_kind_of Hash, Rails.application.config.database_configuration
end
+ test 'raises with proper error message if no database configuration found' do
+ FileUtils.rm("#{app_path}/config/database.yml")
+ require "#{app_path}/config/environment"
+ err = assert_raises RuntimeError do
+ Rails.application.config.database_configuration
+ end
+ assert_match 'config/database', err.message
+ end
+
test 'config.action_mailer.show_previews defaults to true in development' do
Rails.env = "development"
require "#{app_path}/config/environment"