├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── commands.gemspec ├── lib ├── commands.rb └── rails │ └── commands │ ├── commander.rb │ ├── console_delegation.rb │ ├── environment.rb │ ├── generator.rb │ ├── raker.rb │ ├── test_environment.rb │ └── tester.rb └── test └── commands_test.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | gem "rails" 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | jbuilder (0.9.1) 5 | activesupport (>= 3.0.0) 6 | 7 | GEM 8 | remote: http://rubygems.org/ 9 | specs: 10 | actionpack (3.2.8) 11 | activemodel (= 3.2.8) 12 | activesupport (= 3.2.8) 13 | builder (~> 3.0.0) 14 | erubis (~> 2.7.0) 15 | journey (~> 1.0.4) 16 | rack (~> 1.4.0) 17 | rack-cache (~> 1.2) 18 | rack-test (~> 0.6.1) 19 | sprockets (~> 2.1.3) 20 | activemodel (3.2.8) 21 | activesupport (= 3.2.8) 22 | builder (~> 3.0.0) 23 | activesupport (3.2.8) 24 | i18n (~> 0.6) 25 | multi_json (~> 1.0) 26 | builder (3.0.0) 27 | erubis (2.7.0) 28 | hike (1.2.1) 29 | i18n (0.6.1) 30 | journey (1.0.4) 31 | multi_json (1.3.6) 32 | rack (1.4.1) 33 | rack-cache (1.2) 34 | rack (>= 0.4) 35 | rack-test (0.6.1) 36 | rack (>= 1.0) 37 | rake (0.9.2.2) 38 | sprockets (2.1.3) 39 | hike (~> 1.2) 40 | rack (~> 1.0) 41 | tilt (~> 1.1, != 1.3.0) 42 | tilt (1.3.3) 43 | 44 | PLATFORMS 45 | ruby 46 | 47 | DEPENDENCIES 48 | actionpack 49 | jbuilder! 50 | rake 51 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 David Heinemeier Hansson, 37signals 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Commands 2 | ======== 3 | 4 | Run Rake and Rails commands during a development console session and run tests during a test console session. This side-steps the need to load the entire environment over and over again when you run these commands from the shell. This constant reloading of the environment is what causes slow boot time on big applications. Think of this like a baby Zeus or the Turbolinks of commands. 5 | 6 | 7 | Installation 8 | ------------ 9 | 10 | Add this line to your application's Gemfile: 11 | 12 | gem 'commands' 13 | 14 | And then execute: 15 | 16 | $ bundle 17 | 18 | Usage 19 | ----- 20 | 21 | When your console boots, it'll automatically have a `commander` object instantiated. The following methods are delegated to this object: rake, generate, destroy, update. It's used like this: 22 | 23 | > generate "scaffold post title:string" 24 | > rake "db:migrate" 25 | 26 | To use a persistent console for testing, be sure to first start it in the test environment, like so `./script/rails console test`. Then you can run tests like this: 27 | 28 | > test "models" 29 | > test "controllers" 30 | > test "models/person" 31 | 32 | You can see the options available for all the commands by running them with no parameters. 33 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 'rake/testtask' 3 | 4 | Bundler.require 5 | 6 | Rake::TestTask.new do |test| 7 | test.test_files = FileList["test/*_test.rb"] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /commands.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'commands' 3 | s.version = '0.2.1' 4 | s.author = 'David Heinemeier Hansson' 5 | s.email = 'david@37signals.com' 6 | s.summary = 'Run Rake/Rails commands through the console' 7 | s.homepage = 'https://github.com/rails/commands' 8 | 9 | s.add_dependency 'rails', '>= 3.2.0' 10 | 11 | s.files = Dir["#{File.dirname(__FILE__)}/**/*"] 12 | end 13 | -------------------------------------------------------------------------------- /lib/commands.rb: -------------------------------------------------------------------------------- 1 | require 'rails/console/app' 2 | require 'rails/commands/console_delegation' 3 | 4 | Rails::ConsoleMethods.send :include, Rails::Commands::ConsoleDelegation 5 | -------------------------------------------------------------------------------- /lib/rails/commands/commander.rb: -------------------------------------------------------------------------------- 1 | require 'rails/commands/raker' 2 | require 'rails/commands/tester' 3 | require 'rails/commands/generator' 4 | 5 | module Rails 6 | module Commands 7 | class Commander 8 | delegate :rake, to: :raker 9 | delegate :test, to: :tester 10 | delegate :generate, :destroy, :update, to: :generator 11 | 12 | attr_reader :raker, :tester, :generator 13 | 14 | def initialize 15 | @raker = Raker.new 16 | @tester = Tester.new 17 | @generator = Generator.new 18 | end 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /lib/rails/commands/console_delegation.rb: -------------------------------------------------------------------------------- 1 | require 'rails/commands/commander' 2 | 3 | module Rails 4 | module Commands 5 | module ConsoleDelegation 6 | def commander 7 | @commander ||= Commander.new 8 | end 9 | 10 | def test(*args) 11 | if Rails.env.test? 12 | commander.test(*args) 13 | else 14 | puts "You can only run tests in a console started in the test environment. " + 15 | "Use `./script/rails console test` to start such a console" 16 | end 17 | end 18 | 19 | delegate :rake, :generate, :destroy, :update, to: :commander 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/rails/commands/environment.rb: -------------------------------------------------------------------------------- 1 | module Rails 2 | module Commands 3 | module Environment 4 | extend self 5 | 6 | def fork 7 | Kernel.fork do 8 | yield 9 | Kernel.exit 10 | end 11 | 12 | Process.waitall 13 | end 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /lib/rails/commands/generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | 3 | module Rails 4 | module Commands 5 | class Generator 6 | def initialize 7 | load_rails_generators 8 | end 9 | 10 | def generate(argv = nil) 11 | generator :generate, argv 12 | end 13 | 14 | def update(argv = nil) 15 | generator :update, argv 16 | end 17 | 18 | def destroy(argv = nil) 19 | generator :destroy, argv 20 | end 21 | 22 | 23 | private 24 | def load_rails_generators 25 | Rails.application.load_generators 26 | end 27 | 28 | def generator(name, argv = nil) 29 | if argv.nil? 30 | # FIXME: I don't know why we can't just catch SystemExit here, then we wouldn't need this if block 31 | Rails::Generators.help name 32 | else 33 | ARGV.replace argv.nil? ? [nil] : argv.split(" ") 34 | load "rails/commands/#{name}.rb" 35 | ARGV.replace [nil] 36 | end 37 | 38 | "Completed" 39 | end 40 | end 41 | end 42 | end -------------------------------------------------------------------------------- /lib/rails/commands/raker.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | 3 | module Rails 4 | module Commands 5 | class Raker 6 | include Rake::DSL 7 | 8 | def initialize 9 | load_rake_tasks 10 | end 11 | 12 | def rake(task = nil) 13 | task.nil? ? print_rake_tasks : invoke_rake_task(task) 14 | "Completed" 15 | rescue SystemExit, RuntimeError => e 16 | "Failed: #{e.message}" 17 | end 18 | 19 | 20 | private 21 | def load_rake_tasks 22 | Rake::TaskManager.record_task_metadata = true # needed to capture comments from define_task 23 | load Rails.root.join('Rakefile') 24 | end 25 | 26 | def print_rake_tasks 27 | Rake.application.options.show_tasks = :tasks 28 | Rake.application.options.show_task_pattern = Regexp.new('') 29 | Rake.application.display_tasks_and_comments 30 | end 31 | 32 | def invoke_rake_task(task) 33 | task, *options = task.split(" ") 34 | 35 | ARGV.replace options 36 | 37 | # FIXME: Before we can use this, we need a way to reset the options again 38 | # Rake.application.handle_options 39 | 40 | expose_argv_arguments_via_env { Rake::Task[task].invoke } 41 | Rake.application.tasks.each(&:reenable) # Rake by default only allows tasks to be run once per session 42 | ensure 43 | ARGV.replace([]) 44 | end 45 | 46 | def expose_argv_arguments_via_env 47 | argv_arguments.each { |key, value| ENV[key] = value } 48 | yield 49 | ensure 50 | argv_arguments.keys.each { |key| ENV.delete(key) } 51 | end 52 | 53 | def argv_arguments 54 | ARGV.each_with_object({}) do |arg, arguments| 55 | if arg =~ /^(\w+)=(.*)$/ 56 | arguments[$1] = $2 57 | end 58 | end 59 | end 60 | end 61 | end 62 | end -------------------------------------------------------------------------------- /lib/rails/commands/test_environment.rb: -------------------------------------------------------------------------------- 1 | require 'rails/commands/environment' 2 | 3 | module Rails 4 | module Commands 5 | module TestEnvironment 6 | extend self 7 | 8 | def fork 9 | Environment.fork do 10 | setup_for_test 11 | yield 12 | end 13 | 14 | reset_active_record 15 | end 16 | 17 | 18 | private 19 | def setup_for_test 20 | reload_classes 21 | add_test_dir_to_load_path 22 | end 23 | 24 | def reload_classes 25 | # Overwrite the default config.cache_classes = true, 26 | # so we can change classes in the test session. 27 | ActiveSupport::Dependencies.mechanism = :load 28 | 29 | ActionDispatch::Reloader.cleanup! 30 | ActionDispatch::Reloader.prepare! 31 | end 32 | 33 | def reset_active_record 34 | if defined? ActiveRecord 35 | ActiveRecord::Base.clear_active_connections! 36 | ActiveRecord::Base.establish_connection 37 | end 38 | end 39 | 40 | def add_test_dir_to_load_path 41 | test_path = Rails.root.join("test") 42 | $:.unshift(test_path) unless $:.first == test_path 43 | end 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /lib/rails/commands/tester.rb: -------------------------------------------------------------------------------- 1 | require 'rails/commands/test_environment' 2 | 3 | module Rails 4 | module Commands 5 | class Tester 6 | def test(what = nil) 7 | case what 8 | when NilClass 9 | print_test_usage 10 | when "all" 11 | run "test/**/**/*_test.rb" 12 | when /^[^\/]+$/ # models 13 | run "test/#{what}/**/*_test.rb" 14 | when /[\/]+/ # models/person 15 | run "test/#{what}_test.rb" 16 | end 17 | 18 | "Completed" 19 | end 20 | 21 | 22 | private 23 | def run(*test_patterns) 24 | TestEnvironment.fork do 25 | test_patterns.each do |test_pattern| 26 | Dir[test_pattern].each do |path| 27 | require File.expand_path(path) 28 | end 29 | end 30 | 31 | trigger_runner 32 | end 33 | end 34 | 35 | def trigger_runner 36 | if defined?(Test::Unit::TestCase) && ActiveSupport::TestCase.ancestors.include?(Test::Unit::TestCase) 37 | MiniTest::Unit.runner.run 38 | else 39 | # MiniTest::Spec setups in Rails 4.0+ has autorun defined 40 | end 41 | end 42 | 43 | def print_test_usage 44 | puts <<-EOT 45 | Usage: 46 | test "WHAT" 47 | 48 | Description: 49 | Runs either a full set of test suites or single suite. 50 | 51 | If you supply WHAT with either models, controllers, helpers, integration, or performance, 52 | those whole sets will be run. 53 | 54 | If you supply WHAT with models/person, just test/models/person_test.rb will be run. 55 | EOT 56 | end 57 | end 58 | end 59 | end -------------------------------------------------------------------------------- /test/commands_test.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'active_support/test_case' 3 | 4 | require 'commands' 5 | 6 | class CommandsTest < ActiveSupport::TestCase 7 | end 8 | --------------------------------------------------------------------------------