diff options
Diffstat (limited to 'railties/lib/rails')
68 files changed, 938 insertions, 1249 deletions
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 82fffe86bb..c163081bfc 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,7 +1,5 @@ require 'active_support/core_ext/hash/reverse_merge' -require 'active_support/file_update_checker' require 'fileutils' -require 'rails/plugin' require 'rails/engine' module Rails @@ -10,7 +8,7 @@ module Rails # # == Initialization # - # Rails::Application is responsible for executing all railties, engines and plugin + # Rails::Application is responsible for executing all railties and engines # initializers. It also executes some bootstrap initializers (check # Rails::Application::Bootstrap) and finishing initializers, after all the others # are executed (check Rails::Application::Finisher). @@ -20,7 +18,7 @@ module Rails # Besides providing the same configuration as Rails::Engine and Rails::Railtie, # the application object has several specific configurations, for example # "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters", - # "logger", "reload_plugins" and so forth. + # "logger" and so forth. # # Check Rails::Application::Configuration to see them all. # @@ -33,6 +31,25 @@ module Rails # # The Application is also responsible for building the middleware stack. # + # == Booting process + # + # The application is also responsible for setting up and executing the booting + # process. From the moment you require "config/application.rb" in your app, + # the booting process goes like this: + # + # 1) require "config/boot.rb" to setup load paths + # 2) require railties and engines + # 3) Define Rails.application as "class MyApp::Application < Rails::Application" + # 4) Run config.before_configuration callbacks + # 5) Load config/environments/ENV.rb + # 6) Run config.before_initialize callbacks + # 7) Run Railtie#initializer defined by railties, engines and application. + # One by one, each engine sets up its load paths, routes and runs its config/initializers/* files. + # 9) Custom Railtie#initializers added by railties, engines and applications are executed + # 10) Build the middleware stack and run to_prepare callbacks + # 11) Run config.before_eager_load and eager_load if cache classes is true + # 12) Run config.after_initialize callbacks + # class Application < Engine autoload :Bootstrap, 'rails/application/bootstrap' autoload :Configuration, 'rails/application/configuration' @@ -52,12 +69,14 @@ module Rails attr_accessor :assets, :sandbox alias_method :sandbox?, :sandbox + attr_reader :reloaders delegate :default_url_options, :default_url_options=, :to => :routes def initialize super @initialized = false + @reloaders = [] end # This method is called just after an application inherits from Rails::Application, @@ -83,57 +102,97 @@ module Rails require environment if environment end + # Reload application routes regardless if they changed or not. def reload_routes! routes_reloader.reload! end - def routes_reloader + def routes_reloader #:nodoc: @routes_reloader ||= RoutesReloader.new end - def initialize!(group=:default) + # Returns an array of file paths appended with a hash of directories-extensions + # suitable for ActiveSupport::FileUpdateChecker API. + def watchable_args + files, dirs = config.watchable_files.dup, config.watchable_dirs.dup + + ActiveSupport::Dependencies.autoload_paths.each do |path| + dirs[path.to_s] = [:rb] + end + + [files, dirs] + end + + # Initialize the application passing the given group. By default, the + # group is :default but sprockets precompilation passes group equals + # to assets if initialize_on_precompile is false to avoid booting the + # whole app. + def initialize!(group=:default) #:nodoc: raise "Application has been already initialized." if @initialized run_initializers(group, self) @initialized = true self end + # Load the application and its railties tasks and invoke the registered hooks. + # Check <tt>Rails::Railtie.rake_tasks</tt> for more info. def load_tasks(app=self) initialize_tasks super self end + # Load the application console and invoke the registered hooks. + # Check <tt>Rails::Railtie.console</tt> for more info. def load_console(app=self) initialize_console super self end - # Rails.application.env_config stores some of the Rails initial environment parameters. - # Currently stores: - # - # * action_dispatch.parameter_filter" => config.filter_parameters, - # * action_dispatch.secret_token" => config.secret_token, - # * action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions - # - # These parameters will be used by middlewares and engines to configure themselves. - # + # Stores some of the Rails initial environment parameters which + # will be used by middlewares and engines to configure themselves. def env_config @env_config ||= super.merge({ "action_dispatch.parameter_filter" => config.filter_parameters, "action_dispatch.secret_token" => config.secret_token, - "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions + "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions, + "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local, + "action_dispatch.logger" => Rails.logger, + "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner }) end - def initializers + # Returns the ordered railties for this application considering railties_order. + def ordered_railties #:nodoc: + @ordered_railties ||= begin + order = config.railties_order.map do |railtie| + if railtie == :main_app + self + elsif railtie.respond_to?(:instance) + railtie.instance + else + railtie + end + end + + all = (railties.all - order) + all.push(self) unless all.include?(self) + order.push(:all) unless order.include?(:all) + + index = order.index(:all) + order[index] = all + order.reverse.flatten + end + end + + def initializers #:nodoc: Bootstrap.initializers_for(self) + super + Finisher.initializers_for(self) end - def config + def config #:nodoc: @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd)) end @@ -141,10 +200,23 @@ module Rails self end + def helpers_paths #:nodoc: + config.helpers_paths + end + + def call(env) + env["ORIGINAL_FULLPATH"] = build_original_fullpath(env) + super(env) + end + protected alias :build_middleware_stack :app + def reload_dependencies? + config.reload_classes_only_on_change != true || reloaders.map(&:updated?).any? + end + def default_middleware_stack ActionDispatch::MiddlewareStack.new.tap do |middleware| if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache @@ -166,16 +238,26 @@ module Rails middleware.use ::Rack::MethodOverride middleware.use ::ActionDispatch::RequestId middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods - middleware.use ::ActionDispatch::ShowExceptions, config.consider_all_requests_local + middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path) + middleware.use ::ActionDispatch::DebugExceptions middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies + if config.action_dispatch.x_sendfile_header.present? middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header end - middleware.use ::ActionDispatch::Reloader unless config.cache_classes + + unless config.cache_classes + app = self + middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? } + end + middleware.use ::ActionDispatch::Callbacks middleware.use ::ActionDispatch::Cookies if config.session_store + if config.force_ssl && !config.session_options.key?(:secure) + config.session_options[:secure] = true + end middleware.use config.session_store, config.session_options middleware.use ::ActionDispatch::Flash end @@ -191,7 +273,7 @@ module Rails end end - def initialize_tasks + def initialize_tasks #:nodoc: self.class.rake_tasks do require "rails/tasks" task :environment do @@ -201,10 +283,22 @@ module Rails end end - def initialize_console + def initialize_console #:nodoc: require "pp" require "rails/console/app" require "rails/console/helpers" end + + def build_original_fullpath(env) + path_info = env["PATH_INFO"] + query_string = env["QUERY_STRING"] + script_name = env["SCRIPT_NAME"] + + if query_string.present? + "#{script_name}#{path_info}?#{query_string}" + else + "#{script_name}#{path_info}" + end + end end end diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index c2cb121e42..efc7dca0d4 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -24,40 +24,41 @@ module Rails initializer :initialize_logger, :group => :all do Rails.logger ||= config.logger || begin path = config.paths["log"].first - logger = ActiveSupport::TaggedLogging.new(ActiveSupport::BufferedLogger.new(path)) - logger.level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase) - logger.auto_flushing = false if Rails.env.production? + unless File.exist? File.dirname path + FileUtils.mkdir_p File.dirname path + end + + f = File.open path, 'a' + f.binmode + f.sync = true # make sure every write flushes + + logger = ActiveSupport::TaggedLogging.new( + ActiveSupport::Logger.new(f) + ) + logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase) logger rescue StandardError - logger = ActiveSupport::TaggedLogging.new(ActiveSupport::BufferedLogger.new(STDERR)) - logger.level = ActiveSupport::BufferedLogger::WARN + logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR)) + logger.level = ActiveSupport::Logger::WARN logger.warn( "Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " + "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." ) logger end - at_exit { Rails.logger.flush if Rails.logger.respond_to?(:flush) } end # Initialize cache early in the stack so railties can make use of it. initializer :initialize_cache, :group => :all do - unless defined?(RAILS_CACHE) - silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(config.cache_store) } + unless Rails.cache + Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store) - if RAILS_CACHE.respond_to?(:middleware) - config.middleware.insert_before("Rack::Runtime", RAILS_CACHE.middleware) + if Rails.cache.respond_to?(:middleware) + config.middleware.insert_before("Rack::Runtime", Rails.cache.middleware) end end end - initializer :set_clear_dependencies_hook, :group => :all do - ActionDispatch::Reloader.to_cleanup do - ActiveSupport::DescendantsTracker.clear - ActiveSupport::Dependencies.clear - end - end - # Sets the dependency loading mechanism. # TODO: Remove files from the $" and always use require. initializer :initialize_dependency_mechanism, :group => :all do diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 8f5b28faf8..825ea985fc 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -1,17 +1,17 @@ -require 'active_support/core_ext/string/encoding' require 'active_support/core_ext/kernel/reporting' +require 'active_support/file_update_checker' require 'rails/engine/configuration' module Rails class Application class Configuration < ::Rails::Engine::Configuration attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets, - :cache_classes, :cache_store, :consider_all_requests_local, - :dependency_loading, :filter_parameters, + :cache_classes, :cache_store, :consider_all_requests_local, :console, + :dependency_loading, :exceptions_app, :file_watcher, :filter_parameters, :force_ssl, :helpers_paths, :logger, :log_tags, :preload_frameworks, - :reload_plugins, :secret_token, :serve_static_assets, - :ssl_options, :static_cache_control, :session_options, - :time_zone, :whiny_nils + :railties_order, :relative_url_root, :secret_token, + :serve_static_assets, :ssl_options, :static_cache_control, :session_options, + :time_zone, :reload_classes_only_on_change attr_writer :log_level attr_reader :encoding @@ -19,22 +19,27 @@ module Rails def initialize(*) super self.encoding = "utf-8" - @allow_concurrency = false - @consider_all_requests_local = false - @filter_parameters = [] - @helpers_paths = [] - @dependency_loading = true - @serve_static_assets = true - @static_cache_control = nil - @force_ssl = false - @ssl_options = {} - @session_store = :cookie_store - @session_options = {} - @time_zone = "UTC" - @log_level = nil - @middleware = app_middleware - @generators = app_generators - @cache_store = [ :file_store, "#{root}/tmp/cache/" ] + @allow_concurrency = false + @consider_all_requests_local = false + @filter_parameters = [] + @helpers_paths = [] + @dependency_loading = true + @serve_static_assets = true + @static_cache_control = nil + @force_ssl = false + @ssl_options = {} + @session_store = :cookie_store + @session_options = {} + @time_zone = "UTC" + @log_level = nil + @middleware = app_middleware + @generators = app_generators + @cache_store = [ :file_store, "#{root}/tmp/cache/" ] + @railties_order = [:all] + @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"] + @reload_classes_only_on_change = true + @file_watcher = ActiveSupport::FileUpdateChecker + @exceptions_app = nil @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false @@ -51,6 +56,7 @@ module Rails @assets.js_compressor = nil @assets.css_compressor = nil @assets.initialize_on_precompile = true + @assets.logger = nil end def compiled_asset_path @@ -59,17 +65,9 @@ module Rails def encoding=(value) @encoding = value - if "ruby".encoding_aware? - silence_warnings do - Encoding.default_external = value - Encoding.default_internal = value - end - else - $KCODE = value - if $KCODE == "NONE" - raise "The value you specified for config.encoding is " \ - "invalid. The possible values are UTF8, SJIS, or EUC" - end + silence_warnings do + Encoding.default_external = value + Encoding.default_internal = value end end @@ -93,10 +91,10 @@ module Rails # after boot, and disables reloading code on every request, as these are # fundamentally incompatible with thread safety. def threadsafe! - self.preload_frameworks = true - self.cache_classes = true - self.dependency_loading = false - self.allow_concurrency = true + @preload_frameworks = true + @cache_classes = true + @dependency_loading = false + @allow_concurrency = true self end @@ -113,11 +111,10 @@ module Rails end def colorize_logging - @colorize_logging + ActiveSupport::LogSubscriber.colorize_logging end def colorize_logging=(val) - @colorize_logging = val ActiveSupport::LogSubscriber.colorize_logging = val self.generators.colorize_logging = val end @@ -139,6 +136,11 @@ module Rails @session_options = args.shift || {} end end + + def whiny_nils=(*) + ActiveSupport::Deprecation.warn "config.whiny_nils option " \ + "is deprecated and no longer works", caller + end end end end diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index 028c8814c4..7da495211d 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -19,12 +19,6 @@ module Rails end end - initializer :add_to_prepare_blocks do - config.to_prepare_blocks.each do |block| - ActionDispatch::Reloader.to_prepare(&block) - end - end - initializer :add_builtin_route do |app| if Rails.env.development? app.routes.append do @@ -37,32 +31,60 @@ module Rails build_middleware_stack end - initializer :run_prepare_callbacks do - ActionDispatch::Reloader.prepare! - end - initializer :define_main_app_helper do |app| app.routes.define_mounted_helper(:main_app) end + initializer :add_to_prepare_blocks do + config.to_prepare_blocks.each do |block| + ActionDispatch::Reloader.to_prepare(&block) + end + end + + # This needs to happen before eager load so it happens + # in exactly the same point regardless of config.cache_classes + initializer :run_prepare_callbacks do + ActionDispatch::Reloader.prepare! + end + initializer :eager_load! do - if config.cache_classes && !$rails_rake_task + if config.cache_classes && !(defined?($rails_rake_task) && $rails_rake_task) ActiveSupport.run_load_hooks(:before_eager_load, self) eager_load! end end + # All initialization is done, including eager loading in production initializer :finisher_hook do ActiveSupport.run_load_hooks(:after_initialize, self) end - # Force routes to be loaded just at the end and add it to to_prepare callbacks - # This needs to be after the finisher hook to ensure routes added in the hook - # are still loaded. - initializer :set_routes_reloader do |app| - reloader = lambda { app.routes_reloader.execute_if_updated } - reloader.call - ActionDispatch::Reloader.to_prepare(&reloader) + # Set app reload just after the finisher hook to ensure + # routes added in the hook are still loaded. + initializer :set_routes_reloader_hook do + reloader = routes_reloader + reloader.execute_if_updated + self.reloaders << reloader + ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated } + end + + # Set app reload just after the finisher hook to ensure + # paths added in the hook are still loaded. + initializer :set_clear_dependencies_hook, :group => :all do + callback = lambda do + ActiveSupport::DescendantsTracker.clear + ActiveSupport::Dependencies.clear + end + + if config.reload_classes_only_on_change + reloader = config.file_watcher.new(*watchable_args, &callback) + self.reloaders << reloader + # We need to set a to_prepare callback regardless of the reloader result, i.e. + # models should be reloaded if any of the reloaders (i18n, routes) were updated. + ActionDispatch::Reloader.to_prepare(:prepend => true){ reloader.execute } + else + ActionDispatch::Reloader.to_cleanup(&callback) + end end # Disable dependency loading during request cycle diff --git a/railties/lib/rails/application/railties.rb b/railties/lib/rails/application/railties.rb index 8f3a3e8bbb..f20a9689de 100644 --- a/railties/lib/rails/application/railties.rb +++ b/railties/lib/rails/application/railties.rb @@ -4,7 +4,7 @@ module Rails class Application < Engine class Railties < Rails::Engine::Railties def all(&block) - @all ||= railties + engines + plugins + @all ||= railties + engines @all.each(&block) if block @all end diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/route_inspector.rb index 8252f21aa7..2ca0c68243 100644 --- a/railties/lib/rails/application/route_inspector.rb +++ b/railties/lib/rails/application/route_inspector.rb @@ -1,36 +1,113 @@ +require 'delegate' + module Rails class Application + class RouteWrapper < SimpleDelegator + def endpoint + rack_app ? rack_app.inspect : "#{controller}##{action}" + end + + def constraints + requirements.except(:controller, :action) + end + + def rack_app(app = self.app) + @rack_app ||= begin + class_name = app.class.name.to_s + if class_name == "ActionDispatch::Routing::Mapper::Constraints" + rack_app(app.app) + elsif class_name !~ /^ActionDispatch::Routing/ + app + end + end + end + + def verb + super.source.gsub(/[$^]/, '') + end + + def path + super.spec.to_s + end + + def name + super.to_s + end + + def reqs + @reqs ||= begin + reqs = endpoint + reqs += " #{constraints.inspect}" unless constraints.empty? + reqs + end + end + + def controller + requirements[:controller] || ':controller' + end + + def action + requirements[:action] || ':action' + end + + def internal? + path =~ %r{/rails/info/properties|^#{Rails.application.config.assets.prefix}} + end + + def engine? + rack_app && rack_app.respond_to?(:routes) + end + end + ## # This class is just used for displaying route information when someone # executes `rake routes`. People should not use this class. class RouteInspector # :nodoc: + def initialize + @engines = ActiveSupport::OrderedHash.new + end + def format all_routes, filter = nil if filter all_routes = all_routes.select{ |route| route.defaults[:controller] == filter } end - routes = all_routes.collect do |route| - route_reqs = route.requirements - - rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/ + routes = collect_routes(all_routes) - controller = route_reqs[:controller] || ':controller' - action = route_reqs[:action] || ':action' + formatted_routes(routes) + + formatted_routes_for_engines + end - endpoint = rack_app ? rack_app.inspect : "#{controller}##{action}" - constraints = route_reqs.except(:controller, :action) + def collect_routes(routes) + routes = routes.collect do |route| + RouteWrapper.new(route) + end.reject do |route| + route.internal? + end.collect do |route| + collect_engine_routes(route) - reqs = endpoint - reqs += " #{constraints.inspect}" unless constraints.empty? + {:name => route.name, :verb => route.verb, :path => route.path, :reqs => route.reqs } + end + end - verb = route.verb.source.gsub(/[$^]/, '') + def collect_engine_routes(route) + name = route.endpoint + return unless route.engine? + return if @engines[name] - {:name => route.name.to_s, :verb => verb, :path => route.path.spec.to_s, :reqs => reqs} + routes = route.rack_app.routes + if routes.is_a?(ActionDispatch::Routing::RouteSet) + @engines[name] = collect_routes(routes.routes) end + end - # Skip the route if it's internal info route - routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} } + def formatted_routes_for_engines + @engines.map do |name, routes| + ["\nRoutes for #{name}:"] + formatted_routes(routes) + end.flatten + end + def formatted_routes(routes) name_width = routes.map{ |r| r[:name].length }.max verb_width = routes.map{ |r| r[:verb].length }.max path_width = routes.map{ |r| r[:path].length }.max diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb index 1d1f5e1b06..6f9a200aa9 100644 --- a/railties/lib/rails/application/routes_reloader.rb +++ b/railties/lib/rails/application/routes_reloader.rb @@ -1,10 +1,13 @@ +require "active_support/core_ext/module/delegation" + module Rails class Application - class RoutesReloader < ::ActiveSupport::FileUpdateChecker - attr_reader :route_sets + class RoutesReloader + attr_reader :route_sets, :paths + delegate :execute_if_updated, :execute, :updated?, :to => :updater def initialize - super([]) { reload! } + @paths = [] @route_sets = [] end @@ -16,7 +19,15 @@ module Rails revert end - protected + private + + def updater + @updater ||= begin + updater = ActiveSupport::FileUpdateChecker.new(paths) { reload! } + updater.execute + updater + end + end def clear! route_sets.each do |routes| @@ -31,7 +42,7 @@ module Rails def finalize! route_sets.each do |routes| - ActiveSupport.on_load(:action_controller) { routes.finalize! } + routes.finalize! end end diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb index 36fd9aea19..cc26db849d 100644 --- a/railties/lib/rails/backtrace_cleaner.rb +++ b/railties/lib/rails/backtrace_cleaner.rb @@ -26,17 +26,4 @@ module Rails add_filter { |line| line.sub(gems_regexp, '\2 (\3) \4') } end end - - # For installing the BacktraceCleaner in the test/unit - module BacktraceFilterForTestUnit #:nodoc: - def self.included(klass) - klass.send :alias_method_chain, :filter_backtrace, :cleaning - end - - def filter_backtrace_with_cleaning(backtrace, prefix=nil) - backtrace = filter_backtrace_without_cleaning(backtrace, prefix) - backtrace = backtrace.first.split("\n") if backtrace.size == 1 - Rails.backtrace_cleaner.clean(backtrace) - end - end end diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index e6822b75b7..03538b2422 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -37,13 +37,26 @@ class CodeStatistics #:nodoc: next unless file_name =~ pattern - f = File.open(directory + "/" + file_name) - - while line = f.gets - stats["lines"] += 1 - stats["classes"] += 1 if line =~ /class [A-Z]/ - stats["methods"] += 1 if line =~ /def [a-z]/ - stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/ + comment_started = false + + File.open(directory + "/" + file_name) do |f| + while line = f.gets + stats["lines"] += 1 + if(comment_started) + if line =~ /^=end/ + comment_started = false + end + next + else + if line =~ /^=begin/ + comment_started = true + next + end + end + stats["classes"] += 1 if line =~ /^\s*class\s+[_A-Z]/ + stats["methods"] += 1 if line =~ /^\s*def\s+[_a-z]/ + stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/ + end end end diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb index ada150ceec..71fe604e69 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -91,7 +91,7 @@ In addition to those, there are: destroy Undo code generated with "generate" (short-cut alias: "d") benchmarker See how fast a piece of code runs profiler Get profile information from a piece of code - plugin Install a plugin + plugin new Generates skeleton for developing a Rails plugin runner Run a piece of code in the application environment (short-cut alias: "r") All commands can be run with -h (or --help) for more information. diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb index 6c52d0f70f..b745b45e17 100644 --- a/railties/lib/rails/commands/benchmarker.rb +++ b/railties/lib/rails/commands/benchmarker.rb @@ -21,7 +21,7 @@ def options options end -class BenchmarkerTest < ActionDispatch::PerformanceTest +class BenchmarkerTest < ActionDispatch::PerformanceTest #:nodoc: self.profile_options = options ARGV.each do |expression| diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index 32e361d421..86376ac7e6 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -4,45 +4,68 @@ require 'irb/completion' module Rails class Console - def self.start(app) - new(app).start - end + attr_reader :options, :app, :console, :arguments - def initialize(app) - @app = app + def self.start(*args) + new(*args).start end - def start - options = {} - - OptionParser.new do |opt| - opt.banner = "Usage: console [environment] [options]" - opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v } - opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v } - opt.on('--irb', "DEPRECATED: Invoke `/your/choice/of/ruby script/rails console` instead") { |v| abort '--irb option is no longer supported. Invoke `/your/choice/of/ruby script/rails console` instead' } - opt.parse!(ARGV) - end + def initialize(app, arguments = ARGV) + @app = app + @arguments = arguments + app.load_console + @console = app.config.console || IRB + end - @app.sandbox = options[:sandbox] - @app.load_console + def options + @options ||= begin + options = {} - if options[:debugger] - begin - require 'ruby-debug' - puts "=> Debugger enabled" - rescue Exception - puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'" - exit + OptionParser.new do |opt| + opt.banner = "Usage: console [environment] [options]" + opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v } + opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v } + opt.parse!(arguments) end + + options end + end + + def sandbox? + options[:sandbox] + end + + def debugger? + options[:debugger] + end + + def start + app.sandbox = sandbox? - if options[:sandbox] + require_debugger if debugger? + + if sandbox? puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})" puts "Any modifications you make will be rolled back on exit" else puts "Loading #{Rails.env} environment (Rails #{Rails.version})" end - IRB.start + + if defined?(console::ExtendCommandBundle) + console::ExtendCommandBundle.send :include, Rails::ConsoleMethods + end + console.start + end + + def require_debugger + begin + require 'ruby-debug' + puts "=> Debugger enabled" + rescue Exception + puts "You need to install ruby-debug19 to run the console in debugging mode. With gems, use 'gem install ruby-debug19'" + exit + end end end end diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb index b0ba76217a..6fc127efae 100644 --- a/railties/lib/rails/commands/dbconsole.rb +++ b/railties/lib/rails/commands/dbconsole.rb @@ -1,10 +1,4 @@ require 'erb' - -begin - require 'psych' -rescue LoadError -end - require 'yaml' require 'optparse' require 'rbconfig' @@ -33,7 +27,7 @@ module Rails options['mode'] = mode end - opt.on("-h", "--header") do |h| + opt.on("--header") do |h| options['header'] = h end @@ -41,7 +35,7 @@ module Rails abort opt.to_s unless (0..1).include?(ARGV.size) end - unless config = YAML::load(ERB.new(IO.read("#{@app.root}/config/database.yml")).result)[Rails.env] + unless config = @app.config.database_configuration[Rails.env] abort "No database is configured for the environment '#{Rails.env}'" end diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb deleted file mode 100644 index 4df849447d..0000000000 --- a/railties/lib/rails/commands/plugin.rb +++ /dev/null @@ -1,542 +0,0 @@ -# Rails Plugin Manager. -# -# Installing plugins: -# -# $ rails plugin install continuous_builder asset_timestamping -# -# Specifying revisions: -# -# * Subversion revision is a single integer. -# -# * Git revision format: -# - full - 'refs/tags/1.8.0' or 'refs/heads/experimental' -# - short: 'experimental' (equivalent to 'refs/heads/experimental') -# 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0') -# -# -# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com) -# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php) - -$verbose = false - -require 'open-uri' -require 'fileutils' -require 'tempfile' - -include FileUtils - -class RailsEnvironment - attr_reader :root - - def initialize(dir) - @root = dir - end - - def self.find(dir=nil) - dir ||= pwd - while dir.length > 1 - return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb')) - dir = File.dirname(dir) - end - end - - def self.default - @default ||= find - end - - def self.default=(rails_env) - @default = rails_env - end - - def install(name_uri_or_plugin) - if name_uri_or_plugin.is_a? String - if name_uri_or_plugin =~ /:\/\// - plugin = Plugin.new(name_uri_or_plugin) - else - plugin = Plugins[name_uri_or_plugin] - end - else - plugin = name_uri_or_plugin - end - if plugin - plugin.install - else - puts "Plugin not found: #{name_uri_or_plugin}" - end - end - - def use_svn? - require 'active_support/core_ext/kernel' - silence_stderr {`svn --version` rescue nil} - !$?.nil? && $?.success? - end - - def use_externals? - use_svn? && File.directory?("#{root}/vendor/plugins/.svn") - end - - def use_checkout? - # this is a bit of a guess. we assume that if the rails environment - # is under subversion then they probably want the plugin checked out - # instead of exported. This can be overridden on the command line - File.directory?("#{root}/.svn") - end - - def best_install_method - return :http unless use_svn? - case - when use_externals? then :externals - when use_checkout? then :checkout - else :export - end - end - - def externals - return [] unless use_externals? - ext = `svn propget svn:externals "#{root}/vendor/plugins"` - lines = ext.respond_to?(:lines) ? ext.lines : ext - lines.reject{ |line| line.strip == '' }.map do |line| - line.strip.split(/\s+/, 2) - end - end - - def externals=(items) - unless items.is_a? String - items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n") - end - Tempfile.open("svn-set-prop") do |file| - file.write(items) - file.flush - system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"") - end - end -end - -class Plugin - attr_reader :name, :uri - - def initialize(uri, name = nil) - @uri = uri - guess_name(uri) - end - - def self.find(name) - new(name) - end - - def to_s - "#{@name.ljust(30)}#{@uri}" - end - - def svn_url? - @uri =~ /svn(?:\+ssh)?:\/\/*/ - end - - def git_url? - @uri =~ /^git:\/\// || @uri =~ /\.git$/ - end - - def installed? - File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \ - or rails_env.externals.detect{ |name, repo| self.uri == repo } - end - - def install(method=nil, options = {}) - method ||= rails_env.best_install_method? - if :http == method - method = :export if svn_url? - method = :git if git_url? - end - - uninstall if installed? and options[:force] - - unless installed? - send("install_using_#{method}", options) - run_install_hook - else - puts "already installed: #{name} (#{uri}). pass --force to reinstall" - end - end - - def uninstall - path = "#{rails_env.root}/vendor/plugins/#{name}" - if File.directory?(path) - puts "Removing 'vendor/plugins/#{name}'" if $verbose - run_uninstall_hook - rm_r path - else - puts "Plugin doesn't exist: #{path}" - end - - if rails_env.use_externals? - # clean up svn:externals - externals = rails_env.externals - externals.reject!{|n, u| name == n or name == u} - rails_env.externals = externals - end - end - - def info - tmp = "#{rails_env.root}/_tmp_about.yml" - if svn_url? - cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\"" - puts cmd if $verbose - system(cmd) - end - open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream| - stream.read - end rescue "No about.yml found in #{uri}" - ensure - FileUtils.rm_rf tmp if svn_url? - end - - private - - def run_install_hook - install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb" - load install_hook_file if File.exist? install_hook_file - end - - def run_uninstall_hook - uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb" - load uninstall_hook_file if File.exist? uninstall_hook_file - end - - def install_using_export(options = {}) - svn_command :export, options - end - - def install_using_checkout(options = {}) - svn_command :checkout, options - end - - def install_using_externals(options = {}) - externals = rails_env.externals - externals.push([@name, uri]) - rails_env.externals = externals - install_using_checkout(options) - end - - def install_using_http(options = {}) - root = rails_env.root - mkdir_p "#{root}/vendor/plugins/#{@name}" - Dir.chdir "#{root}/vendor/plugins/#{@name}" do - puts "fetching from '#{uri}'" if $verbose - fetcher = RecursiveHTTPFetcher.new(uri, -1) - fetcher.quiet = true if options[:quiet] - fetcher.fetch - end - end - - def install_using_git(options = {}) - root = rails_env.root - mkdir_p(install_path = "#{root}/vendor/plugins/#{name}") - Dir.chdir install_path do - init_cmd = "git init" - init_cmd += " -q" if options[:quiet] and not $verbose - puts init_cmd if $verbose - system(init_cmd) - base_cmd = "git pull --depth 1 #{uri}" - base_cmd += " -q" if options[:quiet] and not $verbose - base_cmd += " #{options[:revision]}" if options[:revision] - puts base_cmd if $verbose - if system(base_cmd) - puts "removing: .git .gitignore" if $verbose - rm_rf %w(.git .gitignore) - else - rm_rf install_path - end - end - end - - def svn_command(cmd, options = {}) - root = rails_env.root - mkdir_p "#{root}/vendor/plugins" - base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\"" - base_cmd += ' -q' if options[:quiet] and not $verbose - base_cmd += " -r #{options[:revision]}" if options[:revision] - puts base_cmd if $verbose - system(base_cmd) - end - - def guess_name(url) - @name = File.basename(url) - if @name == 'trunk' || @name.empty? - @name = File.basename(File.dirname(url)) - end - @name.gsub!(/\.git$/, '') if @name =~ /\.git$/ - end - - def rails_env - @rails_env || RailsEnvironment.default - end -end - -# load default environment and parse arguments -require 'optparse' -module Commands - class Plugin - attr_reader :environment, :script_name - def initialize - @environment = RailsEnvironment.default - @rails_root = RailsEnvironment.default.root - @script_name = File.basename($0) - end - - def environment=(value) - @environment = value - RailsEnvironment.default = value - end - - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: plugin [OPTIONS] command" - o.define_head "Rails plugin manager." - - o.separator "" - o.separator "GENERAL OPTIONS" - - o.on("-r", "--root=DIR", String, - "Set an explicit rails app directory.", - "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) } - - o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose } - o.on("-h", "--help", "Show this help message.") { puts o; exit } - - o.separator "" - o.separator "COMMANDS" - - o.separator " install Install plugin(s) from known repositories or URLs." - o.separator " remove Uninstall plugins." - - o.separator "" - o.separator "EXAMPLES" - o.separator " Install a plugin from a subversion URL:" - o.separator " #{@script_name} plugin install http://example.com/my_svn_plugin\n" - o.separator " Install a plugin from a git URL:" - o.separator " #{@script_name} plugin install git://github.com/SomeGuy/my_awesome_plugin.git\n" - o.separator " Install a plugin and add a svn:externals entry to vendor/plugins" - o.separator " #{@script_name} plugin install -x my_svn_plugin\n" - end - end - - def parse!(args=ARGV) - general, sub = split_args(args) - options.parse!(general) - - command = general.shift - if command =~ /^(install|remove)$/ - command = Commands.const_get(command.capitalize).new(self) - command.parse!(sub) - else - puts "Unknown command: #{command}" unless command.blank? - puts options - exit 1 - end - end - - def split_args(args) - left = [] - left << args.shift while args[0] and args[0] =~ /^-/ - left << args.shift if args[0] - [left, args] - end - - def self.parse!(args=ARGV) - Plugin.new.parse!(args) - end - end - - class Install - def initialize(base_command) - @base_command = base_command - @method = :http - @options = { :quiet => false, :revision => nil, :force => false } - end - - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]" - o.define_head "Install one or more plugins." - o.separator "" - o.separator "Options:" - o.on( "-x", "--externals", - "Use svn:externals to grab the plugin.", - "Enables plugin updates and plugin versioning.") { |v| @method = :externals } - o.on( "-o", "--checkout", - "Use svn checkout to grab the plugin.", - "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout } - o.on( "-e", "--export", - "Use svn export to grab the plugin.", - "Exports the plugin, allowing you to check it into your local repository. Does not enable updates or add an svn:externals entry.") { |v| @method = :export } - o.on( "-q", "--quiet", - "Suppresses the output from installation.", - "Ignored if -v is passed (rails plugin -v install ...)") { |v| @options[:quiet] = true } - o.on( "-r REVISION", "--revision REVISION", - "Checks out the given revision from subversion or git.", - "Ignored if subversion/git is not used.") { |v| @options[:revision] = v } - o.on( "-f", "--force", - "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true } - o.separator "" - o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to " - o.separator "a plugin repository." - end - end - - def determine_install_method - best = @base_command.environment.best_install_method - @method = :http if best == :http and @method == :export - case - when (best == :http and @method != :http) - msg = "Cannot install using subversion because `svn' cannot be found in your PATH" - when (best == :export and (@method != :export and @method != :http)) - msg = "Cannot install using #{@method} because this project is not under subversion." - when (best != :externals and @method == :externals) - msg = "Cannot install using externals because vendor/plugins is not under subversion." - end - if msg - puts msg - exit 1 - end - @method - end - - def parse!(args) - options.parse!(args) - if args.blank? - puts options - exit 1 - end - environment = @base_command.environment - install_method = determine_install_method - puts "Plugins will be installed using #{install_method}" if $verbose - args.each do |name| - ::Plugin.find(name).install(install_method, @options) - end - rescue StandardError => e - puts "Plugin not found: #{args.inspect}" - puts e.inspect if $verbose - exit 1 - end - end - - class Remove - def initialize(base_command) - @base_command = base_command - end - - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: #{@base_command.script_name} remove name [name]..." - o.define_head "Remove plugins." - end - end - - def parse!(args) - options.parse!(args) - if args.blank? - puts options - exit 1 - end - root = @base_command.environment.root - args.each do |name| - ::Plugin.new(name).uninstall - end - end - end - - class Info - def initialize(base_command) - @base_command = base_command - end - - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: #{@base_command.script_name} info name [name]..." - o.define_head "Shows plugin info at {url}/about.yml." - end - end - - def parse!(args) - options.parse!(args) - args.each do |name| - puts ::Plugin.find(name).info - puts - end - end - end -end - -class RecursiveHTTPFetcher - attr_accessor :quiet - def initialize(urls_to_fetch, level = 1, cwd = ".") - @level = level - @cwd = cwd - @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a - @quiet = false - end - - def ls - @urls_to_fetch.collect do |url| - if url =~ /^svn(\+ssh)?:\/\/.*/ - `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil - else - open(url) do |stream| - links("", stream.read) - end rescue nil - end - end.flatten - end - - def push_d(dir) - @cwd = File.join(@cwd, dir) - FileUtils.mkdir_p(@cwd) - end - - def pop_d - @cwd = File.dirname(@cwd) - end - - def links(base_url, contents) - links = [] - contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link| - link = link.sub(/href="/i, "") - next if link =~ /svnindex.xsl$/ - next if link =~ /^(\w*:|)\/\// || link =~ /^\./ - links << File.join(base_url, link) - end - links - end - - def download(link) - puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet - open(link) do |stream| - File.open(File.join(@cwd, File.basename(link)), "wb") do |file| - file.write(stream.read) - end - end - end - - def fetch(links = @urls_to_fetch) - links.each do |l| - (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l) - end - end - - def fetch_dir(url) - @level += 1 - push_d(File.basename(url)) if @level > 0 - open(url) do |stream| - contents = stream.read - fetch(links(url, contents)) - end - pop_d if @level > 0 - @level -= 1 - end -end - -Commands::Plugin.parse! diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb index ea6347c918..3f6966b4f0 100644 --- a/railties/lib/rails/commands/profiler.rb +++ b/railties/lib/rails/commands/profiler.rb @@ -19,7 +19,7 @@ def options options end -class ProfilerTest < ActionDispatch::PerformanceTest +class ProfilerTest < ActionDispatch::PerformanceTest #:nodoc: self.profile_options = options ARGV.each do |expression| diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 20484a10c8..0b757cbe28 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -67,6 +67,13 @@ module Rails FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make)) end + unless options[:daemonize] + wrapped_app # touch the app so the logger is set up + + console = ActiveSupport::Logger.new($stdout) + Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) + end + super ensure # The '-h' option calls exit before @options is set. @@ -76,7 +83,6 @@ module Rails def middleware middlewares = [] - middlewares << [Rails::Rack::LogTailer, log_path] unless options[:daemonize] middlewares << [Rails::Rack::Debugger] if options[:debugger] middlewares << [::Rack::ContentLength] Hash.new(middlewares) @@ -89,6 +95,7 @@ module Rails def default_options super.merge({ :Port => 3000, + :DoNotReverseLookup => true, :environment => (ENV['RAILS_ENV'] || "development").dup, :daemonize => false, :debugger => false, diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb index 95c74baae2..ee8bb55f38 100644 --- a/railties/lib/rails/console/app.rb +++ b/railties/lib/rails/console/app.rb @@ -3,30 +3,33 @@ require 'active_support/test_case' require 'action_controller' # work around the at_exit hook in test/unit, which kills IRB -Test::Unit.run = true if Test::Unit.respond_to?(:run=) -# reference the global "app" instance, created on demand. To recreate the -# instance, pass a non-false value as the parameter. -def app(create=false) - @app_integration_instance = nil if create - @app_integration_instance ||= new_session do |sess| - sess.host! "www.example.com" - end -end +module Rails + module ConsoleMethods + # reference the global "app" instance, created on demand. To recreate the + # instance, pass a non-false value as the parameter. + def app(create=false) + @app_integration_instance = nil if create + @app_integration_instance ||= new_session do |sess| + sess.host! "www.example.com" + end + end -# create a new session. If a block is given, the new session will be yielded -# to the block before being returned. -def new_session - app = Rails.application - session = ActionDispatch::Integration::Session.new(app) - yield session if block_given? - session -end + # create a new session. If a block is given, the new session will be yielded + # to the block before being returned. + def new_session + app = Rails.application + session = ActionDispatch::Integration::Session.new(app) + yield session if block_given? + session + end -# reloads the environment -def reload!(print=true) - puts "Reloading..." if print - ActionDispatch::Reloader.cleanup! - ActionDispatch::Reloader.prepare! - true + # reloads the environment + def reload!(print=true) + puts "Reloading..." if print + ActionDispatch::Reloader.cleanup! + ActionDispatch::Reloader.prepare! + true + end + end end diff --git a/railties/lib/rails/console/helpers.rb b/railties/lib/rails/console/helpers.rb index 212fc6125a..230d3d9d04 100644 --- a/railties/lib/rails/console/helpers.rb +++ b/railties/lib/rails/console/helpers.rb @@ -1,7 +1,11 @@ -def helper - @helper ||= ApplicationController.helpers -end +module Rails + module ConsoleMethods + def helper + @helper ||= ApplicationController.helpers + end -def controller - @controller ||= ApplicationController.new + def controller + @controller ||= ApplicationController.new + end + end end diff --git a/railties/lib/rails/deprecation.rb b/railties/lib/rails/deprecation.rb new file mode 100644 index 0000000000..c5811b2629 --- /dev/null +++ b/railties/lib/rails/deprecation.rb @@ -0,0 +1,18 @@ +require 'active_support/deprecation/proxy_wrappers' + +module Rails + class DeprecatedConstant < ActiveSupport::Deprecation::DeprecatedConstantProxy + def self.deprecate(old, current) + constant = new(old, current) + eval "::#{old} = constant" + end + + private + + def target + ::Kernel.eval @new_const.to_s + end + end + + DeprecatedConstant.deprecate('RAILS_CACHE', '::Rails.cache') +end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 1c9627734e..77a68eb7f1 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -228,7 +228,7 @@ module Rails # resources :articles # end # - # The routes above will automatically point to <tt>MyEngine::ApplicationContoller</tt>. Furthermore, you don't + # The routes above will automatically point to <tt>MyEngine::ArticlesController</tt>. Furthermore, you don't # need to use longer url helpers like <tt>my_engine_articles_path</tt>. Instead, you should simply use # <tt>articles_path</tt> as you would do with your application. # @@ -296,16 +296,16 @@ module Rails # If you want to share just a few specific helpers you can add them to application's # helpers in ApplicationController: # - # class ApplicationController < ActionController::Base - # helper MyEngine::SharedEngineHelper - # end + # class ApplicationController < ActionController::Base + # helper MyEngine::SharedEngineHelper + # end # # If you want to include all of the engine's helpers, you can use #helpers method on an engine's # instance: # - # class ApplicationController < ActionController::Base - # helper MyEngine::Engine.helpers - # end + # class ApplicationController < ActionController::Base + # helper MyEngine::Engine.helpers + # end # # It will include all of the helpers from engine's directory. Take into account that this does # not include helpers defined in controllers with helper_method or other similar solutions, @@ -330,6 +330,17 @@ module Rails # # MyEngine::Engine.load_seed # + # == Loading priority + # + # In order to change engine's priority you can use config.railties_order in main application. + # It will affect the priority of loading views, helpers, assets and all the other files + # related to engine or application. + # + # Example: + # + # # load Blog::Engine with highest priority, followed by application and other railties + # config.railties_order = [Blog::Engine, :main_app, :all] + # class Engine < Railtie autoload :Configuration, "rails/engine/configuration" autoload :Railties, "rails/engine/railties" @@ -371,20 +382,28 @@ module Rails self.routes.default_scope = { :module => ActiveSupport::Inflector.underscore(mod.name) } self.isolated = true - unless mod.respond_to?(:_railtie) - name = engine_name - _railtie = self + unless mod.respond_to?(:railtie_namespace) + name, railtie = engine_name, self + mod.singleton_class.instance_eval do - define_method(:_railtie) do - _railtie - end + define_method(:railtie_namespace) { railtie } unless mod.respond_to?(:table_name_prefix) - define_method(:table_name_prefix) do - "#{name}_" - end + define_method(:table_name_prefix) { "#{name}_" } + end + + unless mod.respond_to?(:use_relative_model_naming?) + class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__ + end + + unless mod.respond_to?(:railtie_helpers_paths) + define_method(:railtie_helpers_paths) { railtie.helpers_paths } + end + + unless mod.respond_to?(:railtie_routes_url_helpers) + define_method(:railtie_routes_url_helpers) { railtie.routes_url_helpers } end - end + end end end @@ -429,13 +448,6 @@ module Rails def helpers @helpers ||= begin helpers = Module.new - - helpers_paths = if config.respond_to?(:helpers_paths) - config.helpers_paths - else - paths["app/helpers"].existent - end - all = ActionController::Base.all_helpers_from_path(helpers_paths) ActionController::Base.modules_for_helpers(all).each do |mod| helpers.send(:include, mod) @@ -444,6 +456,14 @@ module Rails end end + def helpers_paths + paths["app/helpers"].existent + end + + def routes_url_helpers + routes.url_helpers + end + def app @app ||= begin config.middleware = config.middleware.merge_into(default_middleware_stack) @@ -471,10 +491,19 @@ module Rails @routes end + def ordered_railties + railties.all + [self] + end + def initializers initializers = [] - railties.all { |r| initializers += r.initializers } - initializers += super + ordered_railties.each do |r| + if r == self + initializers += super + else + initializers += r.initializers + end + end initializers end @@ -488,7 +517,7 @@ module Rails # Blog::Engine.load_seed def load_seed seed_file = paths["db/seeds"].existent.first - load(seed_file) if seed_file && File.exist?(seed_file) + load(seed_file) if seed_file end # Add configured load paths to ruby load paths and remove duplicates. diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index f424492bb4..d7405cb519 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -5,7 +5,6 @@ module Rails class Configuration < ::Rails::Railtie::Configuration attr_reader :root attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths - attr_accessor :plugins def initialize(root=nil) super() @@ -30,7 +29,7 @@ module Rails # # config.generators.colorize_logging = false # - def generators #:nodoc + def generators #:nodoc: @generators ||= Rails::Configuration::Generators.new yield(@generators) if block_given? @generators @@ -59,7 +58,6 @@ module Rails paths.add "db/seeds", :with => "db/seeds.rb" paths.add "vendor", :load_path => true paths.add "vendor/assets", :glob => "*" - paths.add "vendor/plugins" paths end end diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb index d5ecd2e48d..033d9c4180 100644 --- a/railties/lib/rails/engine/railties.rb +++ b/railties/lib/rails/engine/railties.rb @@ -7,18 +7,11 @@ module Rails end def all(&block) - @all ||= plugins + @all ||= [] @all.each(&block) if block @all end - def plugins - @plugins ||= begin - plugin_names = (@config.plugins || [:all]).map { |p| p.to_sym } - Plugin.all(plugin_names, @config.paths["vendor/plugins"].existent) - end - end - def self.railties @railties ||= ::Rails::Railtie.subclasses.map(&:instance) end diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 27f8d13ce8..cd277c5097 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -38,11 +38,6 @@ module Rails :test_unit => { :fixture_replacement => '-r', - }, - - :plugin => { - :generator => '-g', - :tasks => '-r' } } @@ -62,11 +57,6 @@ module Rails :stylesheet_engine => :css, :test_framework => false, :template_engine => :erb - }, - - :plugin => { - :generator => false, - :tasks => false } } @@ -195,7 +185,6 @@ module Rails "#{test}:scaffold", "#{test}:view", "#{test}:performance", - "#{test}:plugin", "#{template}:controller", "#{template}:scaffold", "#{template}:mailer", diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index b26839644e..32793b1a70 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -5,40 +5,6 @@ module Rails module Generators module Actions - # Install a plugin. You must provide either a Subversion url or Git url. - # - # For a Git-hosted plugin, you can specify a branch and - # whether it should be added as a submodule instead of cloned. - # - # For a Subversion-hosted plugin you can specify a revision. - # - # ==== Examples - # - # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' - # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :branch => 'stable' - # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true - # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' - # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk', :revision => 1234 - # - def plugin(name, options) - log :plugin, name - - if options[:git] && options[:submodule] - options[:git] = "-b #{options[:branch]} #{options[:git]}" if options[:branch] - in_root do - run "git submodule add #{options[:git]} vendor/plugins/#{name}", :verbose => false - end - elsif options[:git] || options[:svn] - options[:git] = "-b #{options[:branch]} #{options[:git]}" if options[:branch] - options[:svn] = "-r #{options[:revision]} #{options[:svn]}" if options[:revision] - in_root do - run_ruby_script "script/rails plugin install #{options[:svn] || options[:git]}", :verbose => false - end - else - log "! no git or svn provided for #{name}. Skipping..." - end - end - # Adds an entry into Gemfile for the supplied gem. If env # is specified, add the gem to the given environment. # @@ -68,8 +34,9 @@ module Rails end in_root do - str = "gem #{parts.join(", ")}\n" + str = "gem #{parts.join(", ")}" str = " " + str if @in_group + str = "\n" + str append_file "Gemfile", str, :verbose => false end end @@ -87,13 +54,13 @@ module Rails log :gemfile, "group #{name}" in_root do - append_file "Gemfile", "\ngroup #{name} do\n", :force => true + append_file "Gemfile", "\ngroup #{name} do", :force => true @in_group = true instance_eval(&block) @in_group = false - append_file "Gemfile", "end\n", :force => true + append_file "Gemfile", "\nend\n", :force => true end end @@ -124,7 +91,7 @@ module Rails if options[:env].nil? inject_into_file 'config/application.rb', "\n #{data}", :after => sentinel, :verbose => false else - Array.wrap(options[:env]).each do |env| + Array(options[:env]).each do |env| inject_into_file "config/environments/#{env}.rb", "\n #{data}", :after => env_file_sentinel, :verbose => false end end @@ -276,10 +243,10 @@ module Rails # def route(routing_code) log :route, routing_code - sentinel = /\.routes\.draw do(?:\s*\|map\|)?\s*$/ + sentinel = /\.routes\.draw do\s*$/ in_root do - inject_into_file 'config/routes.rb', "\n #{routing_code}\n", { :after => sentinel, :verbose => false } + inject_into_file 'config/routes.rb', "\n #{routing_code}", { :after => sentinel, :verbose => false } end end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 10fdfdd8a9..7c449657b5 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -60,9 +60,6 @@ module Rails class_option :help, :type => :boolean, :aliases => "-h", :group => :rails, :desc => "Show this help message and quit" - - class_option :old_style_hash, :type => :boolean, :default => false, - :desc => "Force using old style hash (:foo => 'bar') on Ruby >= 1.9" end def initialize(*args) @@ -138,19 +135,21 @@ module Rails if options.dev? <<-GEMFILE.strip_heredoc gem 'rails', :path => '#{Rails::Generators::RAILS_DEV_PATH}' - gem 'journey', :git => 'git://github.com/rails/journey.git' + gem 'journey', :git => 'https://github.com/rails/journey.git' + gem 'arel', :git => 'https://github.com/rails/arel.git' GEMFILE elsif options.edge? <<-GEMFILE.strip_heredoc - gem 'rails', :git => 'git://github.com/rails/rails.git' - gem 'journey', :git => 'git://github.com/rails/journey.git' + gem 'rails', :git => 'https://github.com/rails/rails.git' + gem 'journey', :git => 'https://github.com/rails/journey.git' + gem 'arel', :git => 'https://github.com/rails/arel.git' GEMFILE else <<-GEMFILE.strip_heredoc gem 'rails', '#{Rails::VERSION::STRING}' # Bundle edge Rails instead: - # gem 'rails', :git => 'git://github.com/rails/rails.git' + # gem 'rails', :git => 'https://github.com/rails/rails.git' GEMFILE end end @@ -182,30 +181,52 @@ module Rails end end - def ruby_debugger_gemfile_entry - if RUBY_VERSION < "1.9" - "gem 'ruby-debug'" + def assets_gemfile_entry + return if options[:skip_sprockets] + + gemfile = if options.dev? || options.edge? + <<-GEMFILE + # Gems used only for assets and not required + # in production environments by default. + group :assets do + gem 'sass-rails', :git => 'https://github.com/rails/sass-rails.git' + gem 'coffee-rails', :git => 'https://github.com/rails/coffee-rails.git' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + #{javascript_runtime_gemfile_entry} + gem 'uglifier', '>= 1.0.3' + end + GEMFILE else - "gem 'ruby-debug19', :require => 'ruby-debug'" + <<-GEMFILE + # Gems used only for assets and not required + # in production environments by default. + group :assets do + gem 'sass-rails', '~> 4.0.0.beta' + gem 'coffee-rails', '~> 4.0.0.beta' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + #{javascript_runtime_gemfile_entry} + gem 'uglifier', '>= 1.0.3' + end + GEMFILE end - end - def assets_gemfile_entry - <<-GEMFILE.strip_heredoc - # Gems used only for assets and not required - # in production environments by default. - group :assets do - gem 'sass-rails', :git => 'git://github.com/rails/sass-rails.git' - gem 'coffee-rails', :git => 'git://github.com/rails/coffee-rails.git' - gem 'uglifier', '>= 1.0.3' - end - GEMFILE + gemfile.strip_heredoc.gsub(/^[ \t]*$/, '') end def javascript_gemfile_entry "gem '#{options[:javascript]}-rails'" unless options[:skip_javascript] end + def javascript_runtime_gemfile_entry + if defined?(JRUBY_VERSION) + "gem 'therubyrhino'\n" + else + "# gem 'therubyracer'\n" + end + end + def bundle_command(command) say_status :run, "bundle #{command}" @@ -223,7 +244,7 @@ module Rails end def run_bundle - bundle_command('install') unless options[:skip_gemfile] || options[:skip_bundle] + bundle_command('install') unless options[:skip_gemfile] || options[:skip_bundle] || options[:pretend] end def empty_directory_with_gitkeep(destination, config = {}) @@ -235,14 +256,9 @@ module Rails create_file("#{destination}/.gitkeep") unless options[:skip_git] end - # Returns Ruby 1.9 style key-value pair if current code is running on - # Ruby 1.9.x. Returns the old-style (with hash rocket) otherwise. + # Returns Ruby 1.9 style key-value pair. def key_value(key, value) - if options[:old_style_hash] || RUBY_VERSION < '1.9' - ":#{key} => #{value}" - else - "#{key}: #{value}" - end + "#{key}: #{value}" end end end diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb index 911f80cf3a..60e94486bb 100644 --- a/railties/lib/rails/generators/base.rb +++ b/railties/lib/rails/generators/base.rb @@ -34,7 +34,7 @@ module Rails usage = source_root && File.expand_path("../USAGE", source_root) @desc ||= if usage && File.exist?(usage) - File.read(usage) + ERB.new(File.read(usage)).result(binding) else "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator." end @@ -48,6 +48,12 @@ module Rails @namespace ||= super.sub(/_generator$/, '').sub(/:generators:/, ':') end + # Convenience method to hide this generator from the available ones when + # running rails generator command. + def self.hide! + Rails::Generators.hide_namespace self.namespace + end + # Invoke a generator based on the value supplied by the user to the # given option named "name". A class option is created when this method # is invoked and you can set a hash to customize it. @@ -91,7 +97,7 @@ module Rails # # The lookup in this case for test_unit as input is: # - # "test_framework:awesome", "test_framework" + # "test_unit:awesome", "test_unit" # # Which is not the desired the lookup. You can change it by providing the # :as option: @@ -102,7 +108,7 @@ module Rails # # And now it will lookup at: # - # "test_framework:controller", "test_framework" + # "test_unit:controller", "test_unit" # # Similarly, if you want it to also lookup in the rails namespace, you just # need to provide the :base value: @@ -113,7 +119,7 @@ module Rails # # And the lookup is exactly the same as previously: # - # "rails:test_framework", "test_framework:controller", "test_framework" + # "rails:test_unit", "test_unit:controller", "test_unit" # # ==== Switches # @@ -128,13 +134,13 @@ module Rails # # ==== Boolean hooks # - # In some cases, you want to provide a boolean hook. For example, webrat + # In some cases, you may want to provide a boolean hook. For example, webrat # developers might want to have webrat available on controller generator. # This can be achieved as: # # Rails::Generators::ControllerGenerator.hook_for :webrat, :type => :boolean # - # Then, if you want, webrat to be invoked, just supply: + # Then, if you want webrat to be invoked, just supply: # # rails generate controller Account --webrat # @@ -146,7 +152,7 @@ module Rails # # You can also supply a block to hook_for to customize how the hook is # going to be invoked. The block receives two arguments, an instance - # of the current class and the klass to be invoked. + # of the current class and the class to be invoked. # # For example, in the resource generator, the controller should be invoked # with a pluralized class name. But by default it is invoked with the same @@ -254,17 +260,13 @@ module Rails nesting = class_name.split('::') last_name = nesting.pop - # Hack to limit const_defined? to non-inherited on 1.9 - extra = [] - extra << false unless Object.method(:const_defined?).arity == 1 - # Extract the last Module in the nesting last = nesting.inject(Object) do |last_module, nest| - break unless last_module.const_defined?(nest, *extra) + break unless last_module.const_defined?(nest, false) last_module.const_get(nest) end - if last && last.const_defined?(last_name.camelize, *extra) + if last && last.const_defined?(last_name.camelize, false) raise Error, "The name '#{class_name}' is either already used in your application " << "or reserved by Ruby on Rails. Please choose an alternative and run " << "this generator again." diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb index 7b1a2a1841..85296ca37b 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb @@ -10,16 +10,14 @@ <th></th> </tr> -<%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> - <tr> + <%%= content_tag_for(:tr, @<%= plural_table_name %>) do |<%= singular_table_name %>| %> <% attributes.each do |attribute| -%> <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td> <% end -%> <td><%%= link_to 'Show', <%= singular_table_name %> %></td> <td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td> <td><%%= link_to 'Destroy', <%= singular_table_name %>, <%= key_value :confirm, "'Are you sure?'" %>, <%= key_value :method, ":delete" %> %></td> - </tr> -<%% end %> + <%% end %> </table> <br /> diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb index 816d82cac3..7dfc1aa599 100644 --- a/railties/lib/rails/generators/generated_attribute.rb +++ b/railties/lib/rails/generators/generated_attribute.rb @@ -1,14 +1,51 @@ require 'active_support/time' require 'active_support/core_ext/object/inclusion' +require 'active_support/core_ext/object/blank' module Rails module Generators class GeneratedAttribute + INDEX_OPTIONS = %w(index uniq) + UNIQ_INDEX_OPTIONS = %w(uniq) + attr_accessor :name, :type + attr_reader :attr_options + + class << self + def parse(column_definition) + name, type, has_index = column_definition.split(':') + + # if user provided "name:index" instead of "name:string:index" + # type should be set blank so GeneratedAttribute's constructor + # could set it to :string + has_index, type = type, nil if INDEX_OPTIONS.include?(type) + + type, attr_options = *parse_type_and_options(type) + new(name, type, has_index, attr_options) + end + + private + + # parse possible attribute options like :limit for string/text/binary/integer or :precision/:scale for decimals + # when declaring options curly brackets should be used + def parse_type_and_options(type) + case type + when /(string|text|binary|integer)\{(\d+)\}/ + return $1, :limit => $2.to_i + when /decimal\{(\d+)[,.-](\d+)\}/ + return :decimal, :precision => $1.to_i, :scale => $2.to_i + else + return type, {} + end + end + end - def initialize(name, type) - type = :string if type.blank? - @name, @type = name, type.to_sym + def initialize(name, type=nil, index_type=false, attr_options={}) + @name = name + @type = (type.presence || :string).to_sym + @has_index = INDEX_OPTIONS.include?(index_type) + @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type) + @attr_options = attr_options end def field_type @@ -45,8 +82,28 @@ module Rails name.to_s.humanize end + def index_name + reference? ? "#{name}_id" : name + end + def reference? - self.type.in?([:references, :belongs_to]) + self.type.in?(:references, :belongs_to) + end + + def has_index? + @has_index + end + + def has_uniq_index? + @has_uniq_index + end + + def inject_options + "".tap { |s| @attr_options.each { |k,v| s << ", #{k}: #{v.inspect}" } } + end + + def inject_index_options + has_uniq_index? ? ", unique: true" : "" end end end diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index c6c0392f43..9cef55e0a6 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -9,9 +9,6 @@ module Rails class_option :skip_namespace, :type => :boolean, :default => false, :desc => "Skip namespace (affects only isolated applications)" - class_option :old_style_hash, :type => :boolean, :default => false, - :desc => "Force using old style hash (:foo => 'bar') on Ruby >= 1.9" - def initialize(args, *options) #:nodoc: @inside_template = nil # Unfreeze name in case it's given as a frozen string @@ -153,9 +150,8 @@ module Rails # Convert attributes array into GeneratedAttribute objects. def parse_attributes! #:nodoc: - self.attributes = (attributes || []).map do |key_value| - name, type = key_value.split(':') - Rails::Generators::GeneratedAttribute.new(name, type) + self.attributes = (attributes || []).map do |attr| + Rails::Generators::GeneratedAttribute.parse(attr) end end @@ -185,14 +181,9 @@ module Rails end end - # Returns Ruby 1.9 style key-value pair if current code is running on - # Ruby 1.9.x. Returns the old-style (with hash rocket) otherwise. + # Returns Ruby 1.9 style key-value pair. def key_value(key, value) - if options[:old_style_hash] || RUBY_VERSION < '1.9' - ":#{key} => #{value}" - else - "#{key}: #{value}" - end + "#{key}: #{value}" end end end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 3e32f758a4..f0745df667 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -38,7 +38,7 @@ module Rails end def readme - copy_file "README" + copy_file "README", "README.rdoc" end def gemfile @@ -124,7 +124,6 @@ module Rails def vendor vendor_javascripts vendor_stylesheets - vendor_plugins end def vendor_javascripts @@ -134,10 +133,6 @@ module Rails def vendor_stylesheets empty_directory_with_gitkeep "vendor/assets/stylesheets" end - - def vendor_plugins - empty_directory_with_gitkeep "vendor/plugins" - end end module Generators diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index d3b8f4d595..5fdfe8f04e 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -1,11 +1,10 @@ -source 'http://rubygems.org' +source 'https://rubygems.org' <%= rails_gemfile_entry -%> <%= database_gemfile_entry -%> <%= "gem 'jruby-openssl'\n" if defined?(JRUBY_VERSION) -%> -<%= "gem 'json'\n" if RUBY_VERSION < "1.9.2" -%> <%= assets_gemfile_entry %> <%= javascript_gemfile_entry %> @@ -13,11 +12,14 @@ source 'http://rubygems.org' # To use ActiveModel has_secure_password # gem 'bcrypt-ruby', '~> 3.0.0' +# To use Jbuilder templates for JSON +# gem 'jbuilder' + # Use unicorn as the web server # gem 'unicorn' # Deploy with Capistrano -# gem 'capistrano' +# gem 'capistrano', :group => :development # To use debugger -# <%= ruby_debugger_gemfile_entry %> +# gem 'ruby-debug19', :require => 'ruby-debug' diff --git a/railties/lib/rails/generators/rails/app/templates/README b/railties/lib/rails/generators/rails/app/templates/README index 7c36f2356e..d2014bd35f 100644 --- a/railties/lib/rails/generators/rails/app/templates/README +++ b/railties/lib/rails/generators/rails/app/templates/README @@ -86,8 +86,8 @@ programming in general. Debugger support is available through the debugger command when you start your Mongrel or WEBrick server with --debugger. This means that you can break out of execution at any point in the code, investigate and change the model, and then, -resume execution! You need to install ruby-debug to run the server in debugging -mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example: +resume execution! You need to install ruby-debug19 to run the server in debugging +mode. With gems, use <tt>sudo gem install ruby-debug19</tt>. Example: class WeblogController < ActionController::Base def index @@ -191,7 +191,6 @@ The default directory structure of a generated Ruby on Rails application: `-- vendor |-- assets `-- stylesheets - `-- plugins app Holds all the code that's specific to this particular application. @@ -256,6 +255,5 @@ test directory. vendor - External libraries that the application depends on. Also includes the plugins - subdirectory. If the app has frozen rails, those gems also go here, under - vendor/rails/. This directory is in the load path. + External libraries that the application depends on. If the app has frozen rails, + those gems also go here, under vendor/rails/. This directory is in the load path. diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index c63d1b6ac5..bba96a7431 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -2,7 +2,7 @@ <html> <head> <title><%= camelized %></title> - <%%= stylesheet_link_tag "application" %> + <%%= stylesheet_link_tag "application", :media => "all" %> <%%= javascript_include_tag "application" %> <%%= csrf_meta_tags %> </head> diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 13fbe9e526..acf47a03e5 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -13,9 +13,9 @@ require "active_resource/railtie" <% end -%> if defined?(Bundler) - # If you precompile assets before deploying to production, use this line + # If you precompile assets before deploying to production, use this line. Bundler.require(*Rails.groups(:assets => %w(development test))) - # If you want your assets lazily compiled in production, use this line + # If you want your assets lazily compiled in production, use this line. # Bundler.require(:default, :assets, Rails.env) end @@ -28,10 +28,6 @@ module <%= app_const_base %> # Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras) - # Only load the plugins named here, in the order given (default is alphabetical). - # :all can be used as a placeholder for all plugins not explicitly named. - # config.plugins = [ :exception_notification, :ssl_requirement, :all ] - # Activate observers that should always be running. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer @@ -49,11 +45,27 @@ module <%= app_const_base %> # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] + # Use SQL instead of Active Record's schema dumper when creating the database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types. + # config.active_record.schema_format = :sql + + # Enforce whitelist mode for mass assignment. + # This will create an empty whitelist of attributes available for mass-assignment for all models + # in your app. As such, your models will need to explicitly whitelist or blacklist accessible + # parameters by using an attr_accessible or attr_protected declaration. + # config.active_record.whitelist_attributes = true + + # Specifies wether or not has_many or has_one association option :dependent => :restrict raises + # an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be + # raised. If set to false, then an error will be added on the model instead. + <%= comment_if :skip_active_record %>config.active_record.dependent_restrict_raises = false + <% unless options.skip_sprockets? -%> - # Enable the asset pipeline + # Enable the asset pipeline. config.assets.enabled = true - # Version of your assets, change this if you want to expire all your assets + # Version of your assets, change this if you want to expire all your assets. config.assets.version = '1.0' <% end -%> end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 47078e3af9..eb4dfa7c89 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -1,35 +1,38 @@ <%= app_const %>.configure do - # Settings specified here will take precedence over those in config/application.rb + # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false - # Log error messages when you accidentally call methods on nil. - config.whiny_nils = true - - # Show full error reports and disable caching + # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false - # Don't care if the mailer can't send + # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false - # Print deprecation notices to the Rails logger + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Only use best-standards-support built into browsers + # Only use best-standards-support built into browsers. config.action_dispatch.best_standards_support = :builtin <%- unless options.skip_active_record? -%> - # Raise exception on mass assignment protection for ActiveRecord models + # Raise exception on mass assignment protection for Active Record models. config.active_record.mass_assignment_sanitizer = :strict + + # Log the query plan for queries taking more than this (works + # with SQLite, MySQL, and PostgreSQL). + config.active_record.auto_explain_threshold_in_seconds = 0.5 <%- end -%> - # Do not compress assets + <%- unless options.skip_sprockets? -%> + # Do not compress assets. config.assets.compress = false - # Expands the lines which load the assets + # Expands the lines which load the assets. config.assets.debug = true + <%- end -%> end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 50f2df3d35..e9a86d175e 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -1,63 +1,73 @@ <%= app_const %>.configure do - # Settings specified here will take precedence over those in config/application.rb + # Settings specified here will take precedence over those in config/application.rb. - # Code is not reloaded between requests + # Code is not reloaded between requests. config.cache_classes = true - # Full error reports are disabled and caching is turned on + # Full error reports are disabled and caching is turned on. config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Disable Rails's static asset server (Apache or nginx will already do this) + # Disable Rails's static asset server (Apache or nginx will already do this). config.serve_static_assets = false - # Compress JavaScripts and CSS + <%- unless options.skip_sprockets? -%> + # Compress JavaScripts and CSS. config.assets.compress = true - # Don't fallback to assets pipeline if a precompiled asset is missed + # Don't fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # Generate digests for assets URLs + # Generate digests for assets URLs. config.assets.digest = true - # Defaults to Rails.root.join("public/assets") + # Defaults to Rails.root.join("public/assets"). # config.assets.manifest = YOUR_PATH + <%- end -%> - # Specifies the header that your server uses for sending files + # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # See everything in the log (default is :info) + # See everything in the log (default is :info). # config.log_level = :debug - # Prepend all log lines with the following tags + # Prepend all log lines with the following tags. # config.log_tags = [ :subdomain, :uuid ] - # Use a different logger for distributed setups + # Use a different logger for distributed setups. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) - # Use a different cache store in production + # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Enable serving of images, stylesheets, and JavaScripts from an asset server + # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.action_controller.asset_host = "http://assets.example.com" - # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) + <%- unless options.skip_sprockets? -%> + # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added). # config.assets.precompile += %w( search.js ) + <%- end -%> - # Disable delivery errors, bad email addresses will be ignored + # Disable delivery errors, bad email addresses will be ignored. # config.action_mailer.raise_delivery_errors = false - # Enable threaded mode + # Enable threaded mode. # config.threadsafe! # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation can not be found) + # the I18n.default_locale when a translation can not be found). config.i18n.fallbacks = true - # Send deprecation notices to registered listeners + # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify + + <%- unless options.skip_active_record? -%> + # Log the query plan for queries taking more than this (works + # with SQLite, MySQL, and PostgreSQL). + # config.active_record.auto_explain_threshold_in_seconds = 0.5 + <%- end -%> end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index 80198cc21e..b725dd19f6 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -1,5 +1,5 @@ <%= app_const %>.configure do - # Settings specified here will take precedence over those in config/application.rb + # Settings specified here will take precedence over those in config/application.rb. # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that @@ -7,38 +7,30 @@ # and recreated between test runs. Don't rely on the data there! config.cache_classes = true - # Configure static asset server for tests with Cache-Control for performance + # Configure static asset server for tests with Cache-Control for performance. config.serve_static_assets = true config.static_cache_control = "public, max-age=3600" - # Log error messages when you accidentally call methods on nil - config.whiny_nils = true - - # Show full error reports and disable caching + # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false - # Raise exceptions instead of rendering exception templates + # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false - # Disable request forgery protection in test environment - config.action_controller.allow_forgery_protection = false + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - # Use SQL instead of Active Record's schema dumper when creating the test database. - # This is necessary if your schema can't be completely dumped by the schema dumper, - # like if you have constraints or database-specific column types - # config.active_record.schema_format = :sql - <%- unless options.skip_active_record? -%> - # Raise exception on mass assignment protection for ActiveRecord models + # Raise exception on mass assignment protection for Active Record models. config.active_record.mass_assignment_sanitizer = :strict <%- end -%> - # Print deprecation notices to the stderr + # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr end diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore index eb3489a986..8910bf5a06 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ b/railties/lib/rails/generators/rails/app/templates/gitignore @@ -9,6 +9,7 @@ # Ignore the default SQLite database. /db/*.sqlite3 +/db/*.sqlite3-journal # Ignore all logfiles and tempfiles. /log/*.log diff --git a/railties/lib/rails/generators/rails/app/templates/public/index.html b/railties/lib/rails/generators/rails/app/templates/public/index.html index 9d9811a5bf..a1d50995c5 100644 --- a/railties/lib/rails/generators/rails/app/templates/public/index.html +++ b/railties/lib/rails/generators/rails/app/templates/public/index.html @@ -59,7 +59,7 @@ #header { - background-image: url("/assets/rails.png"); + background-image: url("assets/rails.png"); background-repeat: no-repeat; background-position: top left; height: 64px; diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb index 8f5f78556f..52243f4a2f 100644 --- a/railties/lib/rails/generators/rails/controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb @@ -3,7 +3,7 @@ class <%= class_name %>Controller < ApplicationController <% actions.each do |action| -%> def <%= action %> end - +<%= "\n" unless action == actions.last -%> <% end -%> end <% end -%> diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb index 39fa5b63b1..f87dce1502 100644 --- a/railties/lib/rails/generators/rails/migration/migration_generator.rb +++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb @@ -1,7 +1,7 @@ module Rails module Generators class MigrationGenerator < NamedBase #metagenerator - argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]" hook_for :orm, :required => true end end diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb index 629d5eed3f..9bb29b784e 100644 --- a/railties/lib/rails/generators/rails/model/model_generator.rb +++ b/railties/lib/rails/generators/rails/model/model_generator.rb @@ -1,7 +1,7 @@ module Rails module Generators class ModelGenerator < NamedBase #metagenerator - argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]" hook_for :orm, :required => true end end diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 4baa2110e7..f4263d1b98 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -39,7 +39,7 @@ module Rails end def gitignore - copy_file "gitignore", ".gitignore" + template "gitignore", ".gitignore" end def lib @@ -133,6 +133,16 @@ task :default => :test end chmod "script", 0755, :verbose => false end + + def gemfile_entry + return unless inside_application? + + gemfile_in_app_path = File.join(rails_app_path, "Gemfile") + if File.exist? gemfile_in_app_path + entry = "gem '#{name}', :path => '#{relative_path}'" + append_file gemfile_in_app_path, entry + end + end end module Generators @@ -153,6 +163,10 @@ task :default => :test class_option :skip_gemspec, :type => :boolean, :default => false, :desc => "Skip gemspec file" + class_option :skip_gemfile_entry, :type => :boolean, :default => false, + :desc => "If creating plugin in application's directory " + + "skip adding entry to Gemfile" + def initialize(*args) raise Error, "Options should be given after the plugin name. For details run: rails plugin --help" if args[0].blank? @@ -208,6 +222,10 @@ task :default => :test create_dummy_app end + def update_gemfile + build(:gemfile_entry) unless options[:skip_gemfile_entry] + end + def finish_template build(:leftovers) end @@ -246,8 +264,20 @@ task :default => :test "rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]" end + def original_name + @original_name ||= File.basename(destination_root) + end + def name - @name ||= File.basename(destination_root) + @name ||= begin + # same as ActiveSupport::Inflector#underscore except not replacing '-' + underscored = original_name.dup + underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') + underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + underscored.downcase! + + underscored + end end def camelized @@ -255,12 +285,14 @@ task :default => :test end def valid_const? - if camelized =~ /^\d/ - raise Error, "Invalid plugin name #{name}. Please give a name which does not start with numbers." + if original_name =~ /[^0-9a-zA-Z_]+/ + raise Error, "Invalid plugin name #{original_name}. Please give a name which use only alphabetic or numeric or \"_\" characters." + elsif camelized =~ /^\d/ + raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers." elsif RESERVED_NAMES.include?(name) - raise Error, "Invalid plugin name #{name}. Please give a name which does not match one of the reserved rails words." + raise Error, "Invalid plugin name #{original_name}. Please give a name which does not match one of the reserved rails words." elsif Object.const_defined?(camelized) - raise Error, "Invalid plugin name #{name}, constant #{camelized} is already in use. Please choose another plugin name." + raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name." end end @@ -301,6 +333,19 @@ end def mute(&block) shell.mute(&block) end + + def rails_app_path + APP_PATH.sub("/config/application", "") if defined?(APP_PATH) + end + + def inside_application? + rails_app_path && app_path =~ /^#{rails_app_path}/ + end + + def relative_path + return unless inside_application? + app_path.sub(/^#{rails_app_path}\//, '') + end end end end diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile index f4efd3af74..d316b00c43 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile @@ -20,4 +20,4 @@ gem "jquery-rails" <% end -%> # To use debugger -# <%= ruby_debugger_gemfile_entry %> +# gem 'ruby-debug19', :require => 'ruby-debug' diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt index 01550dec2f..bd983fb90f 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt @@ -2,7 +2,7 @@ <html> <head> <title><%= camelized %></title> - <%%= stylesheet_link_tag "<%= name %>/application" %> + <%%= stylesheet_link_tag "<%= name %>/application", :media => "all" %> <%%= javascript_include_tag "<%= name %>/application" %> <%%= csrf_meta_tags %> </head> diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore index 1463de6dfb..458b2c662e 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore @@ -1,6 +1,8 @@ .bundle/ log/*.log pkg/ -test/dummy/db/*.sqlite3 -test/dummy/log/*.log -test/dummy/tmp/
\ No newline at end of file +<%= dummy_path %>/db/*.sqlite3 +<%= dummy_path %>/db/*.sqlite3-journal +<%= dummy_path %>/log/*.log +<%= dummy_path %>/tmp/ +<%= dummy_path %>/.sass-cache diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE index be1d113ed8..4a3eb2c7c7 100644 --- a/railties/lib/rails/generators/rails/scaffold/USAGE +++ b/railties/lib/rails/generators/rails/scaffold/USAGE @@ -7,23 +7,29 @@ Description: under_scored, as the first argument, and an optional list of attribute pairs. - Attribute pairs are field:type arguments specifying the - model's attributes. Timestamps are added by default, so you don't have to - specify them by hand as 'created_at:datetime updated_at:datetime'. + Attributes are field arguments specifying the model's attributes. You can + optionally pass the type and an index to each field. For instance: + "title body:text tracking_id:integer:uniq" will generate a title field of + string type, a body with text type and a tracking_id as an integer with an + unique index. "index" could also be given instead of "uniq" if one desires + a non unique index. + + Timestamps are added by default, so you don't have to specify them by hand + as 'created_at:datetime updated_at:datetime'. You don't have to think up every attribute up front, but it helps to sketch out a few so you can start working with the resource immediately. - For example, 'scaffold post title:string body:text published:boolean' - gives you a model with those three attributes, a controller that handles + For example, 'scaffold post title body:text published:boolean' gives + you a model with those three attributes, a controller that handles the create/show/update/destroy, forms to create and edit your posts, and - an index that lists them all, as well as a resources :posts - declaration in config/routes.rb. + an index that lists them all, as well as a resources :posts declaration + in config/routes.rb. If you want to remove all the generated files, run 'rails destroy scaffold ModelName'. Examples: `rails generate scaffold post` - `rails generate scaffold post title:string body:text published:boolean` - `rails generate scaffold purchase order_id:integer amount:decimal` + `rails generate scaffold post title body:text published:boolean` + `rails generate scaffold purchase amount:decimal tracking_id:integer:uniq` diff --git a/railties/lib/rails/generators/rails/task/USAGE b/railties/lib/rails/generators/rails/task/USAGE new file mode 100644 index 0000000000..dbe9bbaf08 --- /dev/null +++ b/railties/lib/rails/generators/rails/task/USAGE @@ -0,0 +1,9 @@ +Description: + Stubs out a new Rake task. Pass the namespace name, and a list of tasks as arguments. + + This generates a task file in lib/tasks. + +Example: + `rails generate task feeds fetch erase add` + + Task: lib/tasks/feeds.rake
\ No newline at end of file diff --git a/railties/lib/rails/generators/rails/task/task_generator.rb b/railties/lib/rails/generators/rails/task/task_generator.rb new file mode 100644 index 0000000000..8a62d9e8eb --- /dev/null +++ b/railties/lib/rails/generators/rails/task/task_generator.rb @@ -0,0 +1,12 @@ +module Rails + module Generators + class TaskGenerator < NamedBase + argument :actions, :type => :array, :default => [], :banner => "action action" + + def create_task_files + template 'task.rb', File.join('lib/tasks', "#{file_name}.rake") + end + + end + end +end diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb new file mode 100644 index 0000000000..b7407bd6dc --- /dev/null +++ b/railties/lib/rails/generators/rails/task/templates/task.rb @@ -0,0 +1,8 @@ +namespace :<%= file_name %> do +<% actions.each do |action| -%> + desc "TODO" + task :<%= action %> => :environment do + end + +<% end -%> +end diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index b34bc4a524..3c5b39fa16 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -64,7 +64,7 @@ module Rails end begin - "#{options[:orm].to_s.classify}::Generators::ActiveModel".constantize + "#{options[:orm].to_s.camelize}::Generators::ActiveModel".constantize rescue NameError Rails::Generators::ActiveModel end @@ -73,7 +73,7 @@ module Rails # Initialize ORM::Generators::ActiveModel to access instance methods. def orm_instance(name=singular_table_name) - @orm_instance ||= @orm_class.new(name) + @orm_instance ||= orm_class.new(name) end end end diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb index 7319fb79f6..d81c4c3e1d 100644 --- a/railties/lib/rails/generators/test_case.rb +++ b/railties/lib/rails/generators/test_case.rb @@ -218,8 +218,8 @@ module Rails # # create_generated_attribute(:string, 'name') # - def create_generated_attribute(attribute_type, name = 'test') - Rails::Generators::GeneratedAttribute.new(name, attribute_type.to_s) + def create_generated_attribute(attribute_type, name = 'test', index = nil) + Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(':')) end protected diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb index 2ca36a1e44..e82e321914 100644 --- a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb @@ -1,3 +1,3 @@ require 'rubygems' -require 'test/unit' +require 'minitest/autorun' require 'active_support' diff --git a/railties/lib/rails/performance_test_help.rb b/railties/lib/rails/performance_test_help.rb index 4ac38981d0..b1285efde2 100644 --- a/railties/lib/rails/performance_test_help.rb +++ b/railties/lib/rails/performance_test_help.rb @@ -1,3 +1,3 @@ ActionController::Base.perform_caching = true ActiveSupport::Dependencies.mechanism = :require -Rails.logger.level = ActiveSupport::BufferedLogger::INFO +Rails.logger.level = ActiveSupport::Logger::INFO diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb deleted file mode 100644 index 3e27688bb9..0000000000 --- a/railties/lib/rails/plugin.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'rails/engine' -require 'active_support/core_ext/array/conversions' - -module Rails - # Rails::Plugin is nothing more than a Rails::Engine, but since it's loaded too late - # in the boot process, it does not have the same configuration powers as a bare - # Rails::Engine. - # - # Opposite to Rails::Railtie and Rails::Engine, you are not supposed to inherit from - # Rails::Plugin. Rails::Plugin is automatically configured to be an engine by simply - # placing inside vendor/plugins. Since this is done automatically, you actually cannot - # declare a Rails::Engine inside your Plugin, otherwise it would cause the same files - # to be loaded twice. This means that if you want to ship an Engine as gem it cannot - # be used as plugin and vice-versa. - # - # Besides this conceptual difference, the only difference between Rails::Engine and - # Rails::Plugin is that plugins automatically load the file "init.rb" at the plugin - # root during the boot process. - # - class Plugin < Engine - def self.global_plugins - @global_plugins ||= [] - end - - def self.inherited(base) - raise "You cannot inherit from Rails::Plugin" - end - - def self.all(list, paths) - plugins = [] - paths.each do |path| - Dir["#{path}/*"].each do |plugin_path| - plugin = new(plugin_path) - next unless list.include?(plugin.name) || list.include?(:all) - if global_plugins.include?(plugin.name) - warn "WARNING: plugin #{plugin.name} from #{path} was not loaded. Plugin with the same name has been already loaded." - next - end - global_plugins << plugin.name - plugins << plugin - end - end - - plugins.sort_by do |p| - [list.index(p.name) || list.index(:all), p.name.to_s] - end - end - - attr_reader :name, :path - - def railtie_name - name.to_s - end - - def initialize(root) - @name = File.basename(root).to_sym - config.root = root - end - - def config - @config ||= Engine::Configuration.new - end - - initializer :handle_lib_autoload, :before => :set_load_path do |app| - autoload = if app.config.reload_plugins - config.autoload_paths - else - config.autoload_once_paths - end - - autoload.concat paths["lib"].existent - end - - initializer :load_init_rb, :before => :load_config_initializers do |app| - init_rb = File.expand_path("init.rb", root) - if File.file?(init_rb) - # This double assignment is to prevent an "unused variable" warning on Ruby 1.9.3. - config = config = app.config - # TODO: think about evaling initrb in context of Engine (currently it's - # always evaled in context of Rails::Application) - eval(File.read(init_rb), binding, init_rb) - end - end - - initializer :sanity_check_railties_collision do - if Engine.subclasses.map { |k| k.root.to_s }.include?(root.to_s) - raise "\"#{name}\" is a Railtie/Engine and cannot be installed as a plugin" - end - end - end -end diff --git a/railties/lib/rails/rack/debugger.rb b/railties/lib/rails/rack/debugger.rb index 831188eeee..5a78da1731 100644 --- a/railties/lib/rails/rack/debugger.rb +++ b/railties/lib/rails/rack/debugger.rb @@ -12,7 +12,7 @@ module Rails ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings) puts "=> Debugger enabled" rescue LoadError - puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'" + puts "You need to install ruby-debug19 to run the server in debugging mode. With gems, use 'gem install ruby-debug19'" exit end diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb index 830d840894..18f22e8089 100644 --- a/railties/lib/rails/rack/log_tailer.rb +++ b/railties/lib/rails/rack/log_tailer.rb @@ -4,10 +4,13 @@ module Rails def initialize(app, log = nil) @app = app - path = Pathname.new(log || "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath - @cursor = ::File.size(path) + path = Pathname.new(log || "#{::File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath - @file = ::File.open(path, 'r') + @cursor = @file = nil + if ::File.exists?(path) + @cursor = ::File.size(path) + @file = ::File.open(path, 'r') + end end def call(env) @@ -17,6 +20,7 @@ module Rails end def tail! + return unless @cursor @file.seek @cursor unless @file.eof? diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index e8fb1f3d98..7fed7c8631 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -103,11 +103,11 @@ module Rails # end # end # - # == Application, Plugin and Engine + # == Application and Engine # # A Rails::Engine is nothing more than a Railtie with some initializers already set. - # And since Rails::Application and Rails::Plugin are engines, the same configuration - # described here can be used in all three. + # And since Rails::Application is an engine, the same configuration described here + # can be used in both. # # Be sure to look at the documentation of those specific classes for more information. # @@ -117,7 +117,7 @@ module Rails include Initializable - ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Plugin Rails::Engine Rails::Application) + ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application) class << self private :new @@ -181,7 +181,7 @@ module Rails def load_tasks(app=self) extend Rake::DSL if defined? Rake::DSL - self.class.rake_tasks.each { |block| block.call(app) } + self.class.rake_tasks.each { |block| self.instance_exec(app, &block) } # load also tasks from all superclasses klass = self.class.superclass @@ -196,7 +196,7 @@ module Rails end def railtie_namespace - @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:_railtie) } + @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) } end end end diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb index f888684117..cf9e4ad500 100644 --- a/railties/lib/rails/railtie/configuration.rb +++ b/railties/lib/rails/railtie/configuration.rb @@ -7,6 +7,18 @@ module Rails @@options ||= {} end + # Add files that should be watched for change. + def watchable_files + @@watchable_files ||= [] + end + + # Add directories that should be watched for change. + # The key of the hashes should be directories and the values should + # be an array of extensions to match in each directory. + def watchable_dirs + @@watchable_dirs ||= {} + end + # This allows you to modify the application's middlewares from Engines. # # All operations you run on the app_middleware will be replayed on the diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb index 4d57c5973c..4536fedaa3 100644 --- a/railties/lib/rails/ruby_version_check.rb +++ b/railties/lib/rails/ruby_version_check.rb @@ -1,8 +1,8 @@ -if RUBY_VERSION < '1.8.7' +if RUBY_VERSION < '1.9.3' desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" abort <<-end_message - Rails 3 requires Ruby 1.8.7 or 1.9.2. + Rails 4 requires Ruby 1.9.3+. You're running #{desc} @@ -10,14 +10,4 @@ if RUBY_VERSION < '1.8.7' Please upgrade to continue. end_message -elsif RUBY_VERSION > '1.9' and RUBY_VERSION < '1.9.2' - $stderr.puts <<-end_message - - Rails 3 doesn't officially support Ruby 1.9.1 since recent stable - releases have segfaulted the test suite. Please upgrade to Ruby 1.9.2. - - You're running - #{RUBY_DESCRIPTION} - - end_message end diff --git a/railties/lib/rails/rubyprof_ext.rb b/railties/lib/rails/rubyprof_ext.rb index f6e90357ce..017eba3a76 100644 --- a/railties/lib/rails/rubyprof_ext.rb +++ b/railties/lib/rails/rubyprof_ext.rb @@ -12,7 +12,7 @@ module Prof #:nodoc: io.puts " time seconds seconds calls ms/call ms/call name" sum = 0.0 - for r in results + results.each do |r| sum += r.self_time name = if r.method_class.nil? diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 1eed763aa3..9bfc2b16ab 100644 --- a/railties/lib/rails/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb @@ -22,7 +22,7 @@ class SourceAnnotationExtractor # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above. # Otherwise the string contains just line and text. def to_s(options={}) - s = "[%3d] " % line + s = "[#{line.to_s.rjust(options[:indent])}]" s << "[#{tag}] " if options[:tag] s << text end @@ -30,7 +30,7 @@ class SourceAnnotationExtractor # Prints all annotations with tag +tag+ under the root directories +app+, +config+, +lib+, # +script+, and +test+ (recursively). Only filenames with extension - # +.builder+, +.rb+, +.rxml+, +.rhtml+, or +.erb+ are taken into account. The +options+ + # +.builder+, +.rb+, and +.erb+ are taken into account. The +options+ # hash is passed to each annotation's +to_s+. # # This class method is the single entry point for the rake tasks. @@ -46,16 +46,14 @@ class SourceAnnotationExtractor end # Returns a hash that maps filenames under +dirs+ (recursively) to arrays - # with their annotations. Only files with annotations are included, and only - # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+ - # are taken into account. + # with their annotations. def find(dirs=%w(app config lib script test)) dirs.inject({}) { |h, dir| h.update(find_in(dir)) } end # Returns a hash that maps filenames under +dir+ (recursively) to arrays # with their annotations. Only files with annotations are included, and only - # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+ + # those with extension +.builder+, +.rb+, +.erb+, +.haml+, +.slim+ and +.coffee+ # are taken into account. def find_in(dir) results = {} @@ -65,10 +63,14 @@ class SourceAnnotationExtractor if File.directory?(item) results.update(find_in(item)) - elsif item =~ /\.(builder|(r(?:b|xml|js)))$/ + elsif item =~ /\.(builder|rb|coffee)$/ results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/)) - elsif item =~ /\.(rhtml|erb)$/ + elsif item =~ /\.erb$/ results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/)) + elsif item =~ /\.haml$/ + results.update(extract_annotations_from(item, /-\s*#\s*(#{tag}):?\s*(.*)$/)) + elsif item =~ /\.slim$/ + results.update(extract_annotations_from(item, /\/\s*\s*(#{tag}):?\s*(.*)$/)) end end @@ -91,6 +93,7 @@ class SourceAnnotationExtractor # Prints the mapping from filenames to annotations in +results+ ordered by filename. # The +options+ hash is passed to each annotation's +to_s+. def display(results, options={}) + options[:indent] = results.map { |f, a| a.map(&:line) }.flatten.max.to_s.size results.keys.sort.each do |file| puts "#{file}:" results[file].each do |note| diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake index ca8875ad9b..e09379c8c2 100644 --- a/railties/lib/rails/tasks/documentation.rake +++ b/railties/lib/rails/tasks/documentation.rake @@ -55,7 +55,7 @@ namespace :doc do rdoc.rdoc_files.include('app/**/*.rb') rdoc.rdoc_files.include('lib/**/*.rb') } - Rake::Task['doc:app'].comment = "Generate docs for the app -- also available doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")" + Rake::Task['doc:app'].comment = "Generate docs for the app -- also available doc:rails, doc:guides (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")" # desc 'Generate documentation for the Rails framework.' RDocTaskWithoutDescriptions.new("rails") { |rdoc| @@ -63,93 +63,55 @@ namespace :doc do rdoc.template = "#{ENV['template']}.rb" if ENV['template'] rdoc.title = "Rails Framework Documentation" rdoc.options << '--line-numbers' - rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('README.rdoc') gem_path('actionmailer') do |actionmailer| - %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_mailer/base.rb).each do |file| + %w(README.rdoc CHANGELOG.md MIT-LICENSE lib/action_mailer/base.rb).each do |file| rdoc.rdoc_files.include("#{actionmailer}/#{file}") end end gem_path('actionpack') do |actionpack| - %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG.md MIT-LICENSE lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file| rdoc.rdoc_files.include("#{actionpack}/#{file}") end end gem_path('activemodel') do |activemodel| - %w(README.rdoc CHANGELOG MIT-LICENSE lib/active_model/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG.md MIT-LICENSE lib/active_model/**/*.rb).each do |file| rdoc.rdoc_files.include("#{activemodel}/#{file}") end end gem_path('activerecord') do |activerecord| - %w(README.rdoc CHANGELOG lib/active_record/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG.md lib/active_record/**/*.rb).each do |file| rdoc.rdoc_files.include("#{activerecord}/#{file}") end end gem_path('activeresource') do |activeresource| - %w(README.rdoc CHANGELOG lib/active_resource.rb lib/active_resource/*).each do |file| + %w(README.rdoc CHANGELOG.md lib/active_resource.rb lib/active_resource/*).each do |file| rdoc.rdoc_files.include("#{activeresource}/#{file}") end end gem_path('activesupport') do |activesupport| - %w(README.rdoc CHANGELOG lib/active_support/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG.md lib/active_support/**/*.rb).each do |file| rdoc.rdoc_files.include("#{activesupport}/#{file}") end end gem_path('railties') do |railties| - %w(README.rdoc CHANGELOG lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file| + %w(README.rdoc CHANGELOG.md lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file| rdoc.rdoc_files.include("#{railties}/#{file}") end end } - plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) } - - # desc "Generate documentation for all installed plugins" - task :plugins => plugins.collect { |plugin| "doc:plugins:#{plugin}" } - - # desc "Remove plugin documentation" - task :clobber_plugins do - rm_rf 'doc/plugins' rescue nil - end - # desc "Generate Rails Guides" task :guides do # FIXME: Reaching outside lib directory is a bad idea require File.expand_path('../../../../guides/rails_guides', __FILE__) RailsGuides::Generator.new(Rails.root.join("doc/guides")).generate end - - namespace :plugins do - # Define doc tasks for each plugin - plugins.each do |plugin| - # desc "Generate documentation for the #{plugin} plugin" - task(plugin => :environment) do - plugin_base = "vendor/plugins/#{plugin}" - options = [] - files = Rake::FileList.new - options << "-o doc/plugins/#{plugin}" - options << "--title '#{plugin.titlecase} Plugin Documentation'" - options << '--line-numbers' - options << '--charset' << 'utf-8' - options << '-T html' - - files.include("#{plugin_base}/lib/**/*.rb") - if File.exist?("#{plugin_base}/README") - files.include("#{plugin_base}/README") - options << "--main '#{plugin_base}/README'" - end - files.include("#{plugin_base}/CHANGELOG") if File.exist?("#{plugin_base}/CHANGELOG") - - options << files.to_s - - sh %(rdoc #{options * ' '}) - end - end - end end diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake index 40f8c1034a..f684e71267 100644 --- a/railties/lib/rails/tasks/statistics.rake +++ b/railties/lib/rails/tasks/statistics.rake @@ -2,6 +2,7 @@ STATS_DIRECTORIES = [ %w(Controllers app/controllers), %w(Helpers app/helpers), %w(Models app/models), + %w(Mailers app/mailers), %w(Libraries lib/), %w(APIs app/apis), %w(Integration\ tests test/integration), diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index 8d0d8cacac..11e4353c87 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -2,31 +2,19 @@ # so fixtures aren't loaded into that environment abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production? -require 'test/unit' +require 'minitest/autorun' require 'active_support/test_case' require 'action_controller/test_case' require 'action_dispatch/testing/integration' -if defined?(Test::Unit::Util::BacktraceFilter) && ENV['BACKTRACE'].nil? - require 'rails/backtrace_cleaner' - Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit } -end - -if defined?(MiniTest) - # Enable turn if it is available - begin - require 'turn' - - if MiniTest::Unit.respond_to?(:use_natural_language_case_names=) - MiniTest::Unit.use_natural_language_case_names = true - end - rescue LoadError - end +# Enable turn if it is available +begin + require 'turn' + MiniTest::Unit.use_natural_language_case_names = true +rescue LoadError end if defined?(ActiveRecord::Base) - require 'active_record/test_case' - class ActiveSupport::TestCase include ActiveRecord::TestFixtures self.fixture_path = "#{Rails.root}/test/fixtures/" diff --git a/railties/lib/rails/test_unit/sub_test_task.rb b/railties/lib/rails/test_unit/sub_test_task.rb new file mode 100644 index 0000000000..87b6f9b5a4 --- /dev/null +++ b/railties/lib/rails/test_unit/sub_test_task.rb @@ -0,0 +1,8 @@ +module Rails + # Silence the default description to cut down on `rake -T` noise. + class SubTestTask < Rake::TestTask + def desc(string) + # Ignore the description. + end + end +end diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 3d87529ad4..55bebb549b 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -1,35 +1,6 @@ require 'rbconfig' require 'rake/testtask' - -# Monkey-patch to silence the description from Rake::TestTask to cut down on rake -T noise -class TestTaskWithoutDescription < Rake::TestTask - # Create the tasks defined by this task lib. - def define - lib_path = @libs.join(File::PATH_SEPARATOR) - task @name do - run_code = '' - RakeFileUtils.verbose(@verbose) do - run_code = - case @loader - when :direct - "-e 'ARGV.each{|f| load f}'" - when :testrb - "-S testrb #{fix}" - when :rake - rake_loader - end - @ruby_opts.unshift( "-I\"#{lib_path}\"" ) - @ruby_opts.unshift( "-w" ) if @warning - ruby @ruby_opts.join(" ") + - " \"#{run_code}\" " + - file_list.collect { |fn| "\"#{fn}\"" }.join(' ') + - " #{option_list}" - end - end - self - end -end - +require 'rails/test_unit/sub_test_task' TEST_CHANGES_SINCE = Time.now - 600 @@ -74,22 +45,9 @@ end task :default => :test -desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile, test:plugins)' +desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile)' task :test do - tests_to_run = ENV['TEST'] ? ["test:single"] : %w(test:units test:functionals test:integration) - errors = tests_to_run.collect do |task| - begin - Rake::Task[task].invoke - nil - rescue => e - { :task => task, :exception => e } - end - end.compact - - if errors.any? - puts errors.map { |e| "Errors running #{e[:task]}! #{e[:exception].inspect}" }.join("\n") - abort - end + Rake::Task[ENV['TEST'] ? 'test:single' : 'test:run'].invoke end namespace :test do @@ -97,6 +55,22 @@ namespace :test do # Placeholder task for other Railtie and plugins to enhance. See Active Record for an example. end + task :run do + errors = %w(test:units test:functionals test:integration).collect do |task| + begin + Rake::Task[task].invoke + nil + rescue => e + { :task => task, :exception => e } + end + end.compact + + if errors.any? + puts errors.map { |e| "Errors running #{e[:task]}! #{e[:exception].inspect}" }.join("\n") + abort + end + end + Rake::TestTask.new(:recent => "test:prepare") do |t| since = TEST_CHANGES_SINCE touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } + @@ -123,8 +97,7 @@ namespace :test do unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" } functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" } - - unit_tests.uniq + functional_tests.uniq + (unit_tests + functional_tests).uniq.select { |file| File.exist?(file) } end t.libs << 'test' @@ -135,39 +108,29 @@ namespace :test do t.libs << "test" end - TestTaskWithoutDescription.new(:units => "test:prepare") do |t| + Rails::SubTestTask.new(:units => "test:prepare") do |t| t.libs << "test" t.pattern = 'test/unit/**/*_test.rb' end - TestTaskWithoutDescription.new(:functionals => "test:prepare") do |t| + Rails::SubTestTask.new(:functionals => "test:prepare") do |t| t.libs << "test" t.pattern = 'test/functional/**/*_test.rb' end - TestTaskWithoutDescription.new(:integration => "test:prepare") do |t| + Rails::SubTestTask.new(:integration => "test:prepare") do |t| t.libs << "test" t.pattern = 'test/integration/**/*_test.rb' end - TestTaskWithoutDescription.new(:benchmark => 'test:prepare') do |t| + Rails::SubTestTask.new(:benchmark => 'test:prepare') do |t| t.libs << 'test' t.pattern = 'test/performance/**/*_test.rb' t.options = '-- --benchmark' end - TestTaskWithoutDescription.new(:profile => 'test:prepare') do |t| + Rails::SubTestTask.new(:profile => 'test:prepare') do |t| t.libs << 'test' t.pattern = 'test/performance/**/*_test.rb' end - - TestTaskWithoutDescription.new(:plugins => :environment) do |t| - t.libs << "test" - - if ENV['PLUGIN'] - t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb" - else - t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb' - end - end end diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index 254227ecf7..ec878f9dcf 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -1,7 +1,7 @@ module Rails module VERSION #:nodoc: - MAJOR = 3 - MINOR = 2 + MAJOR = 4 + MINOR = 0 TINY = 0 PRE = "beta" |