Skip to content
Permalink
Browse files
Fix backwards compatibility with Rails 3 apps.
When we removed script/rails and introduced bin/rails, we accidentally
introduced a regression. If you install Rails 4 as a gem, then try to do
something in a Rails 3 application:

    $ rails g

This will throw the 'please type rails new foo' message rather than the
proper generator documentation message. This is because older apps don't
have bin/rails.

Therefore, we now *prefer* bin/rails, but still search for script/rails,
and exec the one we find.
  • Loading branch information
steveklabnik committed Feb 22, 2013
1 parent 31f807c commit c5a9c02e01040831f7e00d3dfa0cfe423f7eb1f9
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 40 deletions.
@@ -3,12 +3,16 @@
module Rails
module AppRailsLoader
RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
EXECUTABLE = 'bin/rails'
EXECUTABLES = ['bin/rails', 'script/rails']

def self.exec_app_rails
cwd = Dir.pwd
return unless in_rails_application_or_engine? || in_rails_application_or_engine_subdirectory?
exec RUBY, EXECUTABLE, *ARGV if in_rails_application_or_engine?
cwd = Dir.pwd

exe = find_executable
exe ||= find_executable_in_parent_path
return unless exe

exec RUBY, exe, *ARGV if find_executable
Dir.chdir("..") do
# Recurse in a chdir block: if the search fails we want to be sure
# the application is generated in the original working directory.
@@ -18,12 +22,16 @@ def self.exec_app_rails
# could not chdir, no problem just return
end

def self.in_rails_application_or_engine?
File.exists?(EXECUTABLE) && File.read(EXECUTABLE) =~ /(APP|ENGINE)_PATH/
def self.find_executable
EXECUTABLES.find do |exe|
File.exists?(exe) && File.read(exe) =~ /(APP|ENGINE)_PATH/
end
end

def self.in_rails_application_or_engine_subdirectory?(path = Pathname.new(Dir.pwd))
File.exists?(File.join(path, EXECUTABLE)) || !path.root? && in_rails_application_or_engine_subdirectory?(path.parent)
def self.find_executable_in_parent_path(path = Pathname.new(Dir.pwd))
EXECUTABLES.find do |exe|
File.exists?(File.join(path, exe)) || !path.root? && find_executable_in_parent_path(path.parent)
end
end
end
end
@@ -3,9 +3,6 @@

# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
#
# TODO: when we hit this, advise adding ./bin to $PATH instead. Then the
# app's `rails` executable is run immediately.
Rails::AppRailsLoader.exec_app_rails

require 'rails/ruby_version_check'
@@ -2,40 +2,47 @@
require 'rails/app_rails_loader'

class AppRailsLoaderTest < ActiveSupport::TestCase
test "is in a rails application if bin/rails exists and contains APP_PATH" do
File.stubs(:exists?).returns(true)
File.stubs(:read).with('bin/rails').returns('APP_PATH')
assert Rails::AppRailsLoader.in_rails_application_or_engine?
end

test "is not in a rails application if bin/rails exists but doesn't contain APP_PATH" do
File.stubs(:exists?).returns(true)
File.stubs(:read).with('bin/rails').returns('railties bin/rails')
assert !Rails::AppRailsLoader.in_rails_application_or_engine?
setup do
File.stubs(:exists?).returns(false)
end

test "is in a rails application if parent directory has bin/rails containing APP_PATH" do
File.stubs(:exists?).with("/foo/bar/bin/rails").returns(false)
File.stubs(:exists?).with("/foo/bin/rails").returns(true)
File.stubs(:read).with('/foo/bin/rails').returns('APP_PATH')
assert Rails::AppRailsLoader.in_rails_application_or_engine_subdirectory?(Pathname.new("/foo/bar"))
end
['bin/rails', 'script/rails'].each do |exe|
test "is in a rails application if #{exe} exists and contains APP_PATH" do
File.stubs(:exists?).with(exe).returns(true)
File.stubs(:read).with(exe).returns('APP_PATH')
assert Rails::AppRailsLoader.find_executable
end

test "is not in a rails application if at the root directory and doesn't have bin/rails" do
Pathname.any_instance.stubs(:root?).returns true
assert !Rails::AppRailsLoader.in_rails_application_or_engine?
end
test "is not in a rails application if #{exe} exists but doesn't contain APP_PATH" do
File.stubs(:exists?).with(exe).returns(true)
File.stubs(:read).with(exe).returns("railties #{exe}")
assert !Rails::AppRailsLoader.find_executable
end

test "is in a rails engine if parent directory has bin/rails containing ENGINE_PATH" do
File.stubs(:exists?).with("/foo/bar/bin/rails").returns(false)
File.stubs(:exists?).with("/foo/bin/rails").returns(true)
File.stubs(:read).with('/foo/bin/rails').returns('ENGINE_PATH')
assert Rails::AppRailsLoader.in_rails_application_or_engine_subdirectory?(Pathname.new("/foo/bar"))
end
test "is in a rails application if parent directory has #{exe} containing APP_PATH" do
File.stubs(:exists?).with("/foo/bar/#{exe}").returns(false)
File.stubs(:exists?).with("/foo/#{exe}").returns(true)
File.stubs(:read).with("/foo/#{exe}").returns('APP_PATH')
assert Rails::AppRailsLoader.find_executable_in_parent_path(Pathname.new("/foo/bar"))
end

test "is not in a rails application if at the root directory and doesn't have #{exe}" do
Pathname.any_instance.stubs(:root?).returns true
assert !Rails::AppRailsLoader.find_executable
end

test "is in a rails engine if parent directory has #{exe} containing ENGINE_PATH" do
File.stubs(:exists?).with("/foo/bar/#{exe}").returns(false)
File.stubs(:exists?).with("/foo/#{exe}").returns(true)
File.stubs(:read).with("/foo/#{exe}").returns('ENGINE_PATH')
assert Rails::AppRailsLoader.find_executable_in_parent_path(Pathname.new("/foo/bar"))
end

test "is in a rails engine if bin/rails exists containing ENGINE_PATH" do
File.stubs(:exists?).returns(true)
File.stubs(:read).with('bin/rails').returns('ENGINE_PATH')
assert Rails::AppRailsLoader.in_rails_application_or_engine?
test "is in a rails engine if #{exe} exists containing ENGINE_PATH" do
File.stubs(:exists?).with(exe).returns(true)
File.stubs(:read).with(exe).returns('ENGINE_PATH')
assert Rails::AppRailsLoader.find_executable
end
end
end

1 comment on commit c5a9c02

@olivierlacan
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stumbled on this quite a bit, good job.

Please sign in to comment.