diff options
-rw-r--r-- | railties/lib/generator/actions.rb | 257 | ||||
-rw-r--r-- | railties/lib/generator/base.rb | 36 | ||||
-rw-r--r-- | railties/lib/generator/generators/app.thor | 237 |
3 files changed, 410 insertions, 120 deletions
diff --git a/railties/lib/generator/actions.rb b/railties/lib/generator/actions.rb new file mode 100644 index 0000000000..41410eba94 --- /dev/null +++ b/railties/lib/generator/actions.rb @@ -0,0 +1,257 @@ +module Rails + module Generators + module Actions + + # Loads an external file and execute it in the instance binding. + # + # ==== Parameters + # path<String>:: The path to the file to execute. Can be a web address or + # a relative path from the source root. + # log_status<Boolean>:: if false, does not log the status. True by default. + # If a symbol is given, uses it as the output color. + # + # ==== Examples + # + # apply "http://gist.github.com/103208" + # + # apply "recipes/jquery.rb" + # + def apply(path, log_status=true) + path = File.expand_path(path, source_root) unless path =~ /^http\:\/\// + say_status_if_log :applying, path, log_status + instance_eval(open(path).read) + say_status_if_log :applied, path, log_status + end + + # Install a plugin. You must provide either a Subversion url or Git url. + # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned. + # + # ==== Examples + # + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true + # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + # + def plugin(name, options) + log 'plugin', name + + if options[:git] && options[:submodule] + in_root do + Git.run("submodule add #{options[:git]} vendor/plugins/#{name}") + end + elsif options[:git] || options[:svn] + in_root do + run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false) + end + else + log "! no git or svn provided for #{name}. skipping..." + end + end + + # Adds an entry into config/environment.rb for the supplied gem : + def gem(name, options = {}) + log 'gem', name + env = options.delete(:env) + + gems_code = "config.gem '#{name}'" + + if options.any? + opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ") + gems_code << ", #{opts}" + end + + environment gems_code, :env => env + end + + # Adds a line inside the Initializer block for config/environment.rb. Used by #gem + # If options :env is specified, the line is appended to the corresponding + # file in config/environments/#{env}.rb + def environment(data = nil, options = {}, &block) + sentinel = 'Rails::Initializer.run do |config|' + + data = block.call if !data && block_given? + + in_root do + if options[:env].nil? + gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n " << data + end + else + Array.wrap(options[:env]).each do|env| + append_file "config/environments/#{env}.rb", "\n#{data}" + end + end + end + end + + # Run a command in git. + # + # ==== Examples + # + # git :init + # git :add => "this.file that.rb" + # git :add => "onefile.rb", :rm => "badfile.cxx" + # + def git(command = {}) + in_root do + if command.is_a?(Symbol) + log 'running', "git #{command}" + Git.run(command.to_s) + else + command.each do |command, options| + log 'running', "git #{command} #{options}" + Git.run("#{command} #{options}") + end + end + end + end + + # Create a new file in the vendor/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # vendor("sekrit.rb") do + # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" + # "salt = '#{sekrit_salt}'" + # end + # + # vendor("foreign.rb", "# Foreign code is fun") + # + def vendor(filename, data = nil, &block) + log 'vendoring', filename + file("vendor/#{filename}", data, false, &block) + end + + # Create a new file in the lib/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # lib("crypto.rb") do + # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" + # end + # + # lib("foreign.rb", "# Foreign code is fun") + # + def lib(filename, data = nil, &block) + log 'lib', filename + file("lib/#{filename}", data, false, &block) + end + + # Create a new Rakefile with the provided code (either in a block or a string). + # + # ==== Examples + # + # rakefile("bootstrap.rake") do + # project = ask("What is the UNIX name of your project?") + # + # <<-TASK + # namespace :#{project} do + # task :bootstrap do + # puts "i like boots!" + # end + # end + # TASK + # end + # + # rakefile("seed.rake", "puts 'im plantin ur seedz'") + # + def rakefile(filename, data = nil, &block) + log 'rakefile', filename + file("lib/tasks/#{filename}", data, false, &block) + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # ==== Examples + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do + # data << "#{const} = :entp" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + # + def initializer(filename, data = nil, &block) + log 'initializer', filename + file("config/initializers/#{filename}", data, false, &block) + end + + # Generate something using a generator from Rails or a plugin. + # The second parameter is the argument string that is passed to + # the generator or an Array that is joined. + # + # ==== Example + # + # generate(:authenticated, "user session") + # + def generate(what, *args) + log 'generating', what + argument = args.map {|arg| arg.to_s }.flatten.join(" ") + + in_root { run_ruby_script("script/generate #{what} #{argument}", false) } + end + + # Runs the supplied rake task + # + # ==== Example + # + # rake("db:migrate") + # rake("db:migrate", :env => "production") + # rake("gems:install", :sudo => true) + # + def rake(command, options = {}) + log 'rake', command + env = options[:env] || 'development' + sudo = options[:sudo] ? 'sudo ' : '' + in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) } + end + + # Just run the capify command in root + # + # ==== Example + # + # capify! + # + def capify! + log 'capifying' + in_root { run('capify .', false) } + end + + # Add Rails to /vendor/rails + # + # ==== Example + # + # freeze! + # + def freeze!(args = {}) + log 'vendor', 'rails edge' + in_root { run('rake rails:freeze:edge', false) } + end + + # Make an entry in Rails routing file conifg/routes.rb + # + # === Example + # + # route "map.root :controller => :welcome" + # + def route(routing_code) + log 'route', routing_code + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + in_root do + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{routing_code}\n" + end + end + end + + end + end +end diff --git a/railties/lib/generator/base.rb b/railties/lib/generator/base.rb new file mode 100644 index 0000000000..ea1e5a0402 --- /dev/null +++ b/railties/lib/generator/base.rb @@ -0,0 +1,36 @@ +require File.dirname(__FILE__) + '/actions' + +# TODO Use vendored Thor +require 'rubygems' +gem 'josevalim-thor' +require 'thor' + +module Rails + module Generators + class Base < Thor::Group + include Rails::Generators::Actions + include Thor::Actions + + # Make aliases for backwards compatibily. Usa no_tasks to avoid aliases + # from becoming tasks. + # + no_tasks { + alias :file :create_file + alias :log :say_status + } + + # Automatically sets the source root based on the class name. + # + def self.source_root + @source_root ||= begin + klass_name = self.name + klass_name.gsub!(/^Rails::Generators::/, '') + klass_name.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') + klass_name.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + File.join(File.dirname(__FILE__), 'templates', klass_name.downcase) + end + end + + end + end +end diff --git a/railties/lib/generator/generators/app.thor b/railties/lib/generator/generators/app.thor index 8dc94641b3..4aa2022759 100644 --- a/railties/lib/generator/generators/app.thor +++ b/railties/lib/generator/generators/app.thor @@ -1,168 +1,165 @@ +require File.dirname(__FILE__) + '/../base' require 'rbconfig' require 'digest/md5' require 'active_support/secure_random' require 'rails/version' -class App < Thor::Group - include Thor::Actions +module Rails::Generators + class App < Base + DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) + DEFAULT_DATABASE = 'sqlite3' - def self.source_root - @source_root ||= File.join(File.dirname(__FILE__), '..', 'templates', 'app') - end - - DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) - DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) - DEFAULT_DATABASE = 'sqlite3' - - argument :app_path, :type => :string + argument :app_path, :type => :string - class_option :ruby, :type => :string, :aliases => "-d", :default => DEFAULT_SHEBANG, - :desc => "Path to the Ruby binary of your choice" + class_option :ruby, :type => :string, :aliases => "-d", :default => DEFAULT_SHEBANG, + :desc => "Path to the Ruby binary of your choice" - class_option :database, :type => :string, :aliases => "-d", :default => DEFAULT_DATABASE, - :desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})" + class_option :database, :type => :string, :aliases => "-d", :default => DEFAULT_DATABASE, + :desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})" - class_option :with_dispatchers, :type => :boolean, :aliases => "-D", :default => false, - :desc => "Add CGI/FastCGI/mod_ruby dispatchers code" + class_option :with_dispatchers, :type => :boolean, :aliases => "-D", :default => false, + :desc => "Add CGI/FastCGI/mod_ruby dispatchers code" - class_option :freeze, :type => :boolean, :aliases => "-f", :default => false, - :desc => "Freeze Rails in vendor/rails from the gems" + class_option :freeze, :type => :boolean, :aliases => "-f", :default => false, + :desc => "Freeze Rails in vendor/rails from the gems" - class_option :template, :type => :string, :aliases => "-m", - :desc => "Use an application template that lives at path (can be a filesystem path or URL)." + class_option :template, :type => :string, :aliases => "-m", + :desc => "Use an application template that lives at path (can be a filesystem path or URL)." - def create_root - self.root = File.expand_path(app_path, root) - empty_directory '.' + def create_root + self.root = File.expand_path(app_path, root) + empty_directory '.' - app_name # Sets the app name - FileUtils.cd(root) - end + app_name # Sets the app name + FileUtils.cd(root) + end - def create_root_files - copy_file "Rakefile" - copy_file "README" - end + def create_root_files + copy_file "Rakefile" + copy_file "README" + end - def create_app_files - directory "app" - end + def create_app_files + directory "app" + end - def create_config_files - empty_directory "config" + def create_config_files + empty_directory "config" - inside "config" do - copy_file "boot.rb" - copy_file "routes.rb" + inside "config" do + copy_file "boot.rb" + copy_file "routes.rb" - template "databases/#{options[:database]}.yml", "database.yml" - template "environment.rb" + template "databases/#{options[:database]}.yml", "database.yml" + template "environment.rb" - directory "environments" - directory "initializers" - directory "locales" + directory "environments" + directory "initializers" + directory "locales" + end end - end - def create_db_files - directory "db" - end + def create_db_files + directory "db" + end - def create_doc_files - directory "doc" - end + def create_doc_files + directory "doc" + end - def create_lib_files - empty_directory "lib" - empty_directory "lib/tasks" - end + def create_lib_files + empty_directory "lib" + empty_directory "lib/tasks" + end - def create_log_files - empty_directory "log" + def create_log_files + empty_directory "log" - inside "log" do - %w( server production development test ).each do |file| - create_file "#{file}.log" - chmod "#{file}.log", 0666, false + inside "log" do + %w( server production development test ).each do |file| + create_file "#{file}.log" + chmod "#{file}.log", 0666, false + end end end - end - def create_public_files - directory "public" - end + def create_public_files + directory "public" + end - def create_dispatch_files - return unless options.with_dispatchers? + def create_dispatch_files + return unless options.with_dispatchers? - copy_file "dispatchers/config.ru", "config.ru" + copy_file "dispatchers/config.ru", "config.ru" - template "dispatchers/dispatch.rb", "public/dispatch.rb" - chmod "public/dispatch.rb", 0755, false + template "dispatchers/dispatch.rb", "public/dispatch.rb" + chmod "public/dispatch.rb", 0755, false - template "dispatchers/dispatch.rb", "public/dispatch.cgi" - chmod "public/dispatch.cgi", 0755, false + template "dispatchers/dispatch.rb", "public/dispatch.cgi" + chmod "public/dispatch.cgi", 0755, false - template "dispatchers/dispatch.fcgi", "public/dispatch.fcgi" - chmod "public/dispatch.fcgi", 0755, false - end + template "dispatchers/dispatch.fcgi", "public/dispatch.fcgi" + chmod "public/dispatch.fcgi", 0755, false + end - def create_javascript_files - directory "javascripts/prototype", "public/javascripts" - end + def create_javascript_files + directory "javascripts/prototype", "public/javascripts" + end - def create_script_files - directory "script" - chmod "script", 0755, false - end + def create_script_files + directory "script" + chmod "script", 0755, false + end - def create_test_files - directory "test" - end + def create_test_files + directory "test" + end - def create_tmp_files - empty_directory "tmp" + def create_tmp_files + empty_directory "tmp" - inside "tmp" do - %w(sessions sockets cache pids).each do |dir| - empty_directory dir + inside "tmp" do + %w(sessions sockets cache pids).each do |dir| + empty_directory dir + end end end - end - def create_vendor_files - empty_directory "vendor/plugins" - end + def create_vendor_files + empty_directory "vendor/plugins" + end - protected + protected - def app_name - @app_name ||= File.basename(root) - end + def app_name + @app_name ||= File.basename(root) + end - def app_secret - ActiveSupport::SecureRandom.hex(64) - end + def app_secret + ActiveSupport::SecureRandom.hex(64) + end - def freeze - options[:freeze] - end + def freeze + options[:freeze] + end - def shebang - options[:ruby] || "#!/usr/bin/env ruby" - end + def shebang + options[:ruby] || "#!/usr/bin/env ruby" + end - def mysql_socket - @mysql_socket ||= [ - "/tmp/mysql.sock", # default - "/var/run/mysqld/mysqld.sock", # debian/gentoo - "/var/tmp/mysql.sock", # freebsd - "/var/lib/mysql/mysql.sock", # fedora - "/opt/local/lib/mysql/mysql.sock", # fedora - "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql - "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 - "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 - "/opt/lampp/var/mysql/mysql.sock" # xampp for linux - ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ - end + def mysql_socket + @mysql_socket ||= [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ + end + end end |