diff options
Diffstat (limited to 'railties/lib/rails/generators')
161 files changed, 5904 insertions, 0 deletions
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb new file mode 100644 index 0000000000..4709914947 --- /dev/null +++ b/railties/lib/rails/generators/actions.rb @@ -0,0 +1,280 @@ +require 'open-uri' +require 'rbconfig' + +module Rails + module Generators + module Actions + def initialize(*) # :nodoc: + super + @in_group = nil + @after_bundle_callbacks = [] + end + + # Adds an entry into +Gemfile+ for the supplied gem. + # + # gem "rspec", group: :test + # gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "http://gems.github.com/" + # gem "rails", "3.0", git: "git://github.com/rails/rails" + def gem(*args) + options = args.extract_options! + name, version = args + + # Set the message to be shown in logs. Uses the git repo if one is given, + # otherwise use name (version). + parts, message = [ quote(name) ], name + if version ||= options.delete(:version) + parts << quote(version) + message << " (#{version})" + end + message = options[:git] if options[:git] + + log :gemfile, message + + options.each do |option, value| + parts << "#{option}: #{quote(value)}" + end + + in_root do + str = "gem #{parts.join(", ")}" + str = " " + str if @in_group + str = "\n" + str + append_file "Gemfile", str, verbose: false + end + end + + # Wraps gem entries inside a group. + # + # gem_group :development, :test do + # gem "rspec-rails" + # end + def gem_group(*names, &block) + name = names.map(&:inspect).join(", ") + log :gemfile, "group #{name}" + + in_root do + append_file "Gemfile", "\ngroup #{name} do", force: true + + @in_group = true + instance_eval(&block) + @in_group = false + + append_file "Gemfile", "\nend\n", force: true + end + end + + # Add the given source to +Gemfile+ + # + # add_source "http://gems.github.com/" + def add_source(source, options={}) + log :source, source + + in_root do + prepend_file "Gemfile", "source #{quote(source)}\n", verbose: false + end + end + + # Adds a line inside the Application class for <tt>config/application.rb</tt>. + # + # If options <tt>:env</tt> is specified, the line is appended to the corresponding + # file in <tt>config/environments</tt>. + # + # environment do + # "config.autoload_paths += %W(#{config.root}/extras)" + # end + # + # environment(nil, env: "development") do + # "config.autoload_paths += %W(#{config.root}/extras)" + # end + def environment(data=nil, options={}, &block) + sentinel = /class [a-z_:]+ < Rails::Application/i + env_file_sentinel = /Rails\.application\.configure do/ + data = block.call if !data && block_given? + + in_root do + if options[:env].nil? + inject_into_file 'config/application.rb', "\n #{data}", after: sentinel, verbose: false + else + Array(options[:env]).each do |env| + inject_into_file "config/environments/#{env}.rb", "\n #{data}", after: env_file_sentinel, verbose: false + end + end + end + end + alias :application :environment + + # Run a command in git. + # + # git :init + # git add: "this.file that.rb" + # git add: "onefile.rb", rm: "badfile.cxx" + def git(commands={}) + if commands.is_a?(Symbol) + run "git #{commands}" + else + commands.each do |cmd, options| + run "git #{cmd} #{options}" + end + end + end + + # Create a new file in the <tt>vendor/</tt> directory. Code can be specified + # in a block or a data string can be given. + # + # 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 :vendor, filename + create_file("vendor/#{filename}", data, verbose: 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. + # + # 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 + create_file("lib/#{filename}", data, verbose: false, &block) + end + + # Create a new +Rakefile+ with the provided code (either in a block or a string). + # + # 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 "Planting seeds"') + def rakefile(filename, data=nil, &block) + log :rakefile, filename + create_file("lib/tasks/#{filename}", data, verbose: false, &block) + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do |const| + # data << "#{const} = :entp\n" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + def initializer(filename, data=nil, &block) + log :initializer, filename + create_file("config/initializers/#{filename}", data, verbose: 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. + # + # generate(:authenticated, "user session") + def generate(what, *args) + log :generate, what + argument = args.flat_map {|arg| arg.to_s }.join(" ") + + in_root { run_ruby_script("bin/rails generate #{what} #{argument}", verbose: false) } + end + + # Runs the supplied rake task + # + # rake("db:migrate") + # rake("db:migrate", env: "production") + # rake("gems:install", sudo: true) + def rake(command, options={}) + log :rake, command + env = options[:env] || ENV["RAILS_ENV"] || 'development' + sudo = options[:sudo] && RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : '' + in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", verbose: false) } + end + + # Just run the capify command in root + # + # capify! + def capify! + log :capify, "" + in_root { run("#{extify(:capify)} .", verbose: false) } + end + + # Make an entry in Rails routing file <tt>config/routes.rb</tt> + # + # route "root 'welcome#index'" + def route(routing_code) + log :route, routing_code + sentinel = /\.routes\.draw do\s*$/ + + in_root do + inject_into_file 'config/routes.rb', "\n #{routing_code}", { after: sentinel, verbose: false } + end + end + + # Reads the given file at the source root and prints it in the console. + # + # readme "README" + def readme(path) + log File.read(find_in_source_paths(path)) + end + + # Registers a callback to be executed after bundle and spring binstubs + # have run. + # + # after_bundle do + # git add: '.' + # end + def after_bundle(&block) + @after_bundle_callbacks << block + end + + protected + + # Define log for backwards compatibility. If just one argument is sent, + # invoke say, otherwise invoke say_status. Differently from say and + # similarly to say_status, this method respects the quiet? option given. + def log(*args) + if args.size == 1 + say args.first.to_s unless options.quiet? + else + args << (self.behavior == :invoke ? :green : :red) + say_status(*args) + end + end + + # Add an extension to the given name based on the platform. + def extify(name) + if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + "#{name}.bat" + else + name + end + end + + # Surround string with single quotes if there is no quotes. + # Otherwise fall back to double quotes + def quote(str) + if str.include?("'") + str.inspect + else + "'#{str}'" + end + end + end + end +end diff --git a/railties/lib/rails/generators/actions/create_migration.rb b/railties/lib/rails/generators/actions/create_migration.rb new file mode 100644 index 0000000000..682092fdf2 --- /dev/null +++ b/railties/lib/rails/generators/actions/create_migration.rb @@ -0,0 +1,69 @@ +require 'thor/actions' + +module Rails + module Generators + module Actions + class CreateMigration < Thor::Actions::CreateFile + + def migration_dir + File.dirname(@destination) + end + + def migration_file_name + @base.migration_file_name + end + + def identical? + exists? && File.binread(existing_migration) == render + end + + def revoke! + say_destination = exists? ? relative_existing_migration : relative_destination + say_status :remove, :red, say_destination + return unless exists? + ::FileUtils.rm_rf(existing_migration) unless pretend? + existing_migration + end + + def relative_existing_migration + base.relative_to_original_destination_root(existing_migration) + end + + def existing_migration + @existing_migration ||= begin + @base.class.migration_exists?(migration_dir, migration_file_name) || + File.exist?(@destination) && @destination + end + end + alias :exists? :existing_migration + + protected + + def on_conflict_behavior(&block) + options = base.options.merge(config) + if identical? + say_status :identical, :blue, relative_existing_migration + elsif options[:force] + say_status :remove, :green, relative_existing_migration + say_status :create, :green + unless pretend? + ::FileUtils.rm_rf(existing_migration) + block.call + end + elsif options[:skip] + say_status :skip, :yellow + else + say_status :conflict, :red + raise Error, "Another migration is already named #{migration_file_name}: " + + "#{existing_migration}. Use --force to replace this migration " + + "or --skip to ignore conflicted file." + end + end + + def say_status(status, color, message = relative_destination) + base.shell.say_status(status, message, color) if config[:verbose] + end + end + end + end +end diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb new file mode 100644 index 0000000000..6183944bb0 --- /dev/null +++ b/railties/lib/rails/generators/active_model.rb @@ -0,0 +1,78 @@ +module Rails + module Generators + # ActiveModel is a class to be implemented by each ORM to allow Rails to + # generate customized controller code. + # + # The API has the same methods as ActiveRecord, but each method returns a + # string that matches the ORM API. + # + # For example: + # + # ActiveRecord::Generators::ActiveModel.find(Foo, "params[:id]") + # # => "Foo.find(params[:id])" + # + # DataMapper::Generators::ActiveModel.find(Foo, "params[:id]") + # # => "Foo.get(params[:id])" + # + # On initialization, the ActiveModel accepts the instance name that will + # receive the calls: + # + # builder = ActiveRecord::Generators::ActiveModel.new "@foo" + # builder.save # => "@foo.save" + # + # The only exception in ActiveModel for ActiveRecord is the use of self.build + # instead of self.new. + # + class ActiveModel + attr_reader :name + + def initialize(name) + @name = name + end + + # GET index + def self.all(klass) + "#{klass}.all" + end + + # GET show + # GET edit + # PATCH/PUT update + # DELETE destroy + def self.find(klass, params=nil) + "#{klass}.find(#{params})" + end + + # GET new + # POST create + def self.build(klass, params=nil) + if params + "#{klass}.new(#{params})" + else + "#{klass}.new" + end + end + + # POST create + def save + "#{name}.save" + end + + # PATCH/PUT update + def update(params=nil) + "#{name}.update(#{params})" + end + + # POST create + # PATCH/PUT update + def errors + "#{name}.errors" + end + + # DELETE destroy + def destroy + "#{name}.destroy" + end + end + end +end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb new file mode 100644 index 0000000000..caaaae09e6 --- /dev/null +++ b/railties/lib/rails/generators/app_base.rb @@ -0,0 +1,368 @@ +require 'digest/md5' +require 'active_support/core_ext/string/strip' +require 'rails/version' unless defined?(Rails::VERSION) +require 'open-uri' +require 'uri' +require 'rails/generators' +require 'active_support/core_ext/array/extract_options' + +module Rails + module Generators + class AppBase < Base # :nodoc: + DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver ) + JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc ) + DATABASES.concat(JDBC_DATABASES) + + attr_accessor :rails_template + add_shebang_option! + + argument :app_path, type: :string + + def self.strict_args_position + false + end + + def self.add_shared_options_for(name) + class_option :template, type: :string, aliases: '-m', + desc: "Path to some #{name} template (can be a filesystem path or URL)" + + class_option :skip_gemfile, type: :boolean, default: false, + desc: "Don't create a Gemfile" + + class_option :skip_bundle, type: :boolean, aliases: '-B', default: false, + desc: "Don't run bundle install" + + class_option :skip_git, type: :boolean, aliases: '-G', default: false, + desc: 'Skip .gitignore file' + + class_option :skip_keeps, type: :boolean, default: false, + desc: 'Skip source control .keep files' + + class_option :skip_active_record, type: :boolean, aliases: '-O', default: false, + desc: 'Skip Active Record files' + + class_option :skip_gems, type: :array, default: [], + desc: 'Skip the provided gems files' + + class_option :skip_action_view, type: :boolean, aliases: '-V', default: false, + desc: 'Skip Action View files' + + class_option :skip_sprockets, type: :boolean, aliases: '-S', default: false, + desc: 'Skip Sprockets files' + + class_option :skip_spring, type: :boolean, default: false, + desc: "Don't install Spring application preloader" + + class_option :database, type: :string, aliases: '-d', default: 'sqlite3', + desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})" + + class_option :javascript, type: :string, aliases: '-j', default: 'jquery', + desc: 'Preconfigure for selected JavaScript library' + + class_option :skip_javascript, type: :boolean, aliases: '-J', default: false, + desc: 'Skip JavaScript files' + + class_option :dev, type: :boolean, default: false, + desc: "Setup the #{name} with Gemfile pointing to your Rails checkout" + + class_option :edge, type: :boolean, default: false, + desc: "Setup the #{name} with Gemfile pointing to Rails repository" + + class_option :skip_test_unit, type: :boolean, aliases: '-T', default: false, + desc: 'Skip Test::Unit files' + + class_option :rc, type: :string, default: false, + desc: "Path to file containing extra configuration options for rails command" + + class_option :no_rc, type: :boolean, default: false, + desc: 'Skip loading of extra configuration options from .railsrc file' + + class_option :help, type: :boolean, aliases: '-h', group: :rails, + desc: 'Show this help message and quit' + end + + def initialize(*args) + @gem_filter = lambda { |gem| !options[:skip_gems].include?(gem.name) } + @extra_entries = [] + super + convert_database_option_for_jruby + end + + protected + + def gemfile_entry(name, *args) + options = args.extract_options! + version = args.first + github = options[:github] + path = options[:path] + + if github + @extra_entries << GemfileEntry.github(name, github) + elsif path + @extra_entries << GemfileEntry.path(name, path) + else + @extra_entries << GemfileEntry.version(name, version) + end + self + end + + def gemfile_entries + [rails_gemfile_entry, + database_gemfile_entry, + assets_gemfile_entry, + javascript_gemfile_entry, + jbuilder_gemfile_entry, + sdoc_gemfile_entry, + psych_gemfile_entry, + @extra_entries].flatten.find_all(&@gem_filter) + end + + def add_gem_entry_filter + @gem_filter = lambda { |next_filter, entry| + yield(entry) && next_filter.call(entry) + }.curry[@gem_filter] + end + + def builder + @builder ||= begin + builder_class = get_builder_class + builder_class.send(:include, ActionMethods) + builder_class.new(self) + end + end + + def build(meth, *args) + builder.send(meth, *args) if builder.respond_to?(meth) + end + + def create_root + valid_const? + + empty_directory '.' + FileUtils.cd(destination_root) unless options[:pretend] + end + + def apply_rails_template + apply rails_template if rails_template + rescue Thor::Error, LoadError, Errno::ENOENT => e + raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}" + end + + def set_default_accessors! + self.destination_root = File.expand_path(app_path, destination_root) + self.rails_template = case options[:template] + when /^https?:\/\// + options[:template] + when String + File.expand_path(options[:template], Dir.pwd) + else + options[:template] + end + end + + def database_gemfile_entry + return [] if options[:skip_active_record] + GemfileEntry.version gem_for_database, nil, + "Use #{options[:database]} as the database for Active Record" + end + + def include_all_railties? + !options[:skip_active_record] && !options[:skip_action_view] && !options[:skip_test_unit] && !options[:skip_sprockets] + end + + def comment_if(value) + options[value] ? '# ' : '' + end + + def sqlite3? + !options[:skip_active_record] && options[:database] == 'sqlite3' + end + + class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out) + def initialize(name, version, comment, options = {}, commented_out = false) + super + end + + def self.github(name, github, comment = nil) + new(name, nil, comment, github: github) + end + + def self.version(name, version, comment = nil) + new(name, version, comment) + end + + def self.path(name, path, comment = nil) + new(name, nil, comment, path: path) + end + end + + def rails_gemfile_entry + if options.dev? + [GemfileEntry.path('rails', Rails::Generators::RAILS_DEV_PATH), + GemfileEntry.github('arel', 'rails/arel'), + GemfileEntry.github('rack', 'rack/rack'), + GemfileEntry.github('i18n', 'svenfuchs/i18n')] + elsif options.edge? + [GemfileEntry.github('rails', 'rails/rails'), + GemfileEntry.github('arel', 'rails/arel'), + GemfileEntry.github('rack', 'rack/rack'), + GemfileEntry.github('i18n', 'svenfuchs/i18n')] + else + [GemfileEntry.version('rails', + Rails::VERSION::STRING, + "Bundle edge Rails instead: gem 'rails', github: 'rails/rails'")] + end + end + + def gem_for_database + # %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql ) + case options[:database] + when "oracle" then "ruby-oci8" + when "postgresql" then "pg" + when "frontbase" then "ruby-frontbase" + when "mysql" then "mysql2" + when "sqlserver" then "activerecord-sqlserver-adapter" + when "jdbcmysql" then "activerecord-jdbcmysql-adapter" + when "jdbcsqlite3" then "activerecord-jdbcsqlite3-adapter" + when "jdbcpostgresql" then "activerecord-jdbcpostgresql-adapter" + when "jdbc" then "activerecord-jdbc-adapter" + else options[:database] + end + end + + def convert_database_option_for_jruby + if defined?(JRUBY_VERSION) + case options[:database] + when "oracle" then options[:database].replace "jdbc" + when "postgresql" then options[:database].replace "jdbcpostgresql" + when "mysql" then options[:database].replace "jdbcmysql" + when "sqlite3" then options[:database].replace "jdbcsqlite3" + end + end + end + + def assets_gemfile_entry + return [] if options[:skip_sprockets] + + gems = [] + if options.dev? || options.edge? + gems << GemfileEntry.github('sprockets-rails', 'rails/sprockets-rails', + 'Use edge version of sprockets-rails') + gems << GemfileEntry.github('sass-rails', 'rails/sass-rails', + 'Use SCSS for stylesheets') + else + gems << GemfileEntry.version('sass-rails', + '~> 4.0.3', + 'Use SCSS for stylesheets') + end + + gems << GemfileEntry.version('uglifier', + '>= 1.3.0', + 'Use Uglifier as compressor for JavaScript assets') + + gems + end + + def jbuilder_gemfile_entry + comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder' + GemfileEntry.version('jbuilder', '~> 2.0', comment) + end + + def sdoc_gemfile_entry + comment = 'bundle exec rake doc:rails generates the API under doc/api.' + GemfileEntry.new('sdoc', '~> 0.4.0', comment, group: :doc) + end + + def coffee_gemfile_entry + comment = 'Use CoffeeScript for .js.coffee assets and views' + if options.dev? || options.edge? + GemfileEntry.github 'coffee-rails', 'rails/coffee-rails', comment + else + GemfileEntry.version 'coffee-rails', '~> 4.0.0', comment + end + end + + def javascript_gemfile_entry + if options[:skip_javascript] + [] + else + gems = [coffee_gemfile_entry, javascript_runtime_gemfile_entry] + gems << GemfileEntry.version("#{options[:javascript]}-rails", nil, + "Use #{options[:javascript]} as the JavaScript library") + + gems << GemfileEntry.version("turbolinks", nil, + "Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks") + gems + end + end + + def javascript_runtime_gemfile_entry + comment = 'See https://github.com/sstephenson/execjs#readme for more supported runtimes' + if defined?(JRUBY_VERSION) + GemfileEntry.version 'therubyrhino', nil, comment + else + GemfileEntry.new 'therubyracer', nil, comment, { platforms: :ruby }, true + end + end + + def psych_gemfile_entry + return [] unless defined?(Rubinius) + + comment = 'Use Psych as the YAML engine, instead of Syck, so serialized ' \ + 'data can be read safely from different rubies (see http://git.io/uuLVag)' + GemfileEntry.new('psych', '~> 2.0', comment, platforms: :rbx) + end + + def bundle_command(command) + say_status :run, "bundle #{command}" + + # We are going to shell out rather than invoking Bundler::CLI.new(command) + # because `rails new` loads the Thor gem and on the other hand bundler uses + # its own vendored Thor, which could be a different version. Running both + # things in the same process is a recipe for a night with paracetamol. + # + # We use backticks and #print here instead of vanilla #system because it + # is easier to silence stdout in the existing test suite this way. The + # end-user gets the bundler commands called anyway, so no big deal. + # + # We unset temporary bundler variables to load proper bundler and Gemfile. + # + # Thanks to James Tucker for the Gem tricks involved in this call. + _bundle_command = Gem.bin_path('bundler', 'bundle') + + require 'bundler' + Bundler.with_clean_env do + output = `"#{Gem.ruby}" "#{_bundle_command}" #{command}` + print output unless options[:quiet] + end + end + + def bundle_install? + !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend]) + end + + def spring_install? + !options[:skip_spring] && Process.respond_to?(:fork) + end + + def run_bundle + bundle_command('install') if bundle_install? + end + + def generate_spring_binstubs + if bundle_install? && spring_install? + bundle_command("exec spring binstub --all") + end + end + + def empty_directory_with_keep_file(destination, config = {}) + empty_directory(destination, config) + keep_file(destination) + end + + def keep_file(destination) + create_file("#{destination}/.keep") unless options[:skip_keeps] + end + end + end +end diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb new file mode 100644 index 0000000000..9af6435f23 --- /dev/null +++ b/railties/lib/rails/generators/base.rb @@ -0,0 +1,379 @@ +begin + require 'thor/group' +rescue LoadError + puts "Thor is not available.\nIf you ran this command from a git checkout " \ + "of Rails, please make sure thor is installed,\nand run this command " \ + "as `ruby #{$0} #{(ARGV | ['--dev']).join(" ")}`" + exit +end + +module Rails + module Generators + class Error < Thor::Error # :nodoc: + end + + class Base < Thor::Group + include Thor::Actions + include Rails::Generators::Actions + + add_runtime_options! + strict_args_position! + + # Returns the source root for this generator using default_source_root as default. + def self.source_root(path=nil) + @_source_root = path if path + @_source_root ||= default_source_root + end + + # Tries to get the description from a USAGE file one folder above the source + # root otherwise uses a default description. + def self.desc(description=nil) + return super if description + + @desc ||= if usage_path + ERB.new(File.read(usage_path)).result(binding) + else + "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator." + end + end + + # Convenience method to get the namespace from the class name. It's the + # same as Thor default except that the Generator at the end of the class + # is removed. + def self.namespace(name=nil) + return super if name + @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. + # + # ==== Examples + # + # module Rails::Generators + # class ControllerGenerator < Base + # hook_for :test_framework, aliases: "-t" + # end + # end + # + # The example above will create a test framework option and will invoke + # a generator based on the user supplied value. + # + # For example, if the user invoke the controller generator as: + # + # rails generate controller Account --test-framework=test_unit + # + # The controller generator will then try to invoke the following generators: + # + # "rails:test_unit", "test_unit:controller", "test_unit" + # + # Notice that "rails:generators:test_unit" could be loaded as well, what + # Rails looks for is the first and last parts of the namespace. This is what + # allows any test framework to hook into Rails as long as it provides any + # of the hooks above. + # + # ==== Options + # + # The first and last part used to find the generator to be invoked are + # guessed based on class invokes hook_for, as noticed in the example above. + # This can be customized with two options: :in and :as. + # + # Let's suppose you are creating a generator that needs to invoke the + # controller generator from test unit. Your first attempt is: + # + # class AwesomeGenerator < Rails::Generators::Base + # hook_for :test_framework + # end + # + # The lookup in this case for test_unit as input is: + # + # "test_unit:awesome", "test_unit" + # + # Which is not the desired lookup. You can change it by providing the + # :as option: + # + # class AwesomeGenerator < Rails::Generators::Base + # hook_for :test_framework, as: :controller + # end + # + # And now it will lookup at: + # + # "test_unit:controller", "test_unit" + # + # Similarly, if you want it to also lookup in the rails namespace, you just + # need to provide the :in value: + # + # class AwesomeGenerator < Rails::Generators::Base + # hook_for :test_framework, in: :rails, as: :controller + # end + # + # And the lookup is exactly the same as previously: + # + # "rails:test_unit", "test_unit:controller", "test_unit" + # + # ==== Switches + # + # All hooks come with switches for user interface. If you do not want + # to use any test framework, you can do: + # + # rails generate controller Account --skip-test-framework + # + # Or similarly: + # + # rails generate controller Account --no-test-framework + # + # ==== Boolean hooks + # + # 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: + # + # rails generate controller Account --webrat + # + # The hooks lookup is similar as above: + # + # "rails:generators:webrat", "webrat:generators:controller", "webrat" + # + # ==== Custom invocations + # + # 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 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 + # name as the resource generator, which is singular. To change this, we + # can give a block to customize how the controller can be invoked. + # + # hook_for :resource_controller do |instance, controller| + # instance.invoke controller, [ instance.name.pluralize ] + # end + # + def self.hook_for(*names, &block) + options = names.extract_options! + in_base = options.delete(:in) || base_name + as_hook = options.delete(:as) || generator_name + + names.each do |name| + unless class_options.key?(name) + defaults = if options[:type] == :boolean + { } + elsif [true, false].include?(default_value_for_option(name, options)) + { banner: "" } + else + { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" } + end + + class_option(name, defaults.merge!(options)) + end + + hooks[name] = [ in_base, as_hook ] + invoke_from_option(name, options, &block) + end + end + + # Remove a previously added hook. + # + # remove_hook_for :orm + def self.remove_hook_for(*names) + remove_invocation(*names) + + names.each do |name| + hooks.delete(name) + end + end + + # Make class option aware of Rails::Generators.options and Rails::Generators.aliases. + def self.class_option(name, options={}) #:nodoc: + options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc) + options[:aliases] = default_aliases_for_option(name, options) + options[:default] = default_value_for_option(name, options) + super(name, options) + end + + # Returns the default source root for a given generator. This is used internally + # by rails to set its generators source root. If you want to customize your source + # root, you should use source_root. + def self.default_source_root + return unless base_name && generator_name + return unless default_generator_root + path = File.join(default_generator_root, 'templates') + path if File.exist?(path) + end + + # Returns the base root for a common set of generators. This is used to dynamically + # guess the default source root. + def self.base_root + File.dirname(__FILE__) + end + + # Cache source root and add lib/generators/base/generator/templates to + # source paths. + def self.inherited(base) #:nodoc: + super + + # Invoke source_root so the default_source_root is set. + base.source_root + + if base.name && base.name !~ /Base$/ + Rails::Generators.subclasses << base + + Rails::Generators.templates_path.each do |path| + if base.name.include?('::') + base.source_paths << File.join(path, base.base_name, base.generator_name) + else + base.source_paths << File.join(path, base.generator_name) + end + end + end + end + + protected + + # Check whether the given class names are already taken by user + # application or Ruby on Rails. + def class_collisions(*class_names) #:nodoc: + return unless behavior == :invoke + + class_names.flatten.each do |class_name| + class_name = class_name.to_s + next if class_name.strip.empty? + + # Split the class from its module nesting + nesting = class_name.split('::') + last_name = nesting.pop + last = extract_last_module(nesting) + + 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." + end + end + end + + # Takes in an array of nested modules and extracts the last module + def extract_last_module(nesting) + nesting.inject(Object) do |last_module, nest| + break unless last_module.const_defined?(nest, false) + last_module.const_get(nest) + end + end + + # Use Rails default banner. + def self.banner + "rails generate #{namespace.sub(/^rails:/,'')} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]".gsub(/\s+/, ' ') + end + + # Sets the base_name taking into account the current class namespace. + def self.base_name + @base_name ||= begin + if base = name.to_s.split('::').first + base.underscore + end + end + end + + # Removes the namespaces and get the generator name. For example, + # Rails::Generators::ModelGenerator will return "model" as generator name. + def self.generator_name + @generator_name ||= begin + if generator = name.to_s.split('::').last + generator.sub!(/Generator$/, '') + generator.underscore + end + end + end + + # Returns the default value for the option name given doing a lookup in + # Rails::Generators.options. + def self.default_value_for_option(name, options) + default_for_option(Rails::Generators.options, name, options, options[:default]) + end + + # Return default aliases for the option name given doing a lookup in + # Rails::Generators.aliases. + def self.default_aliases_for_option(name, options) + default_for_option(Rails::Generators.aliases, name, options, options[:aliases]) + end + + # Return default for the option name given doing a lookup in config. + def self.default_for_option(config, name, options, default) + if generator_name and c = config[generator_name.to_sym] and c.key?(name) + c[name] + elsif base_name and c = config[base_name.to_sym] and c.key?(name) + c[name] + elsif config[:rails].key?(name) + config[:rails][name] + else + default + end + end + + # Keep hooks configuration that are used on prepare_for_invocation. + def self.hooks #:nodoc: + @hooks ||= from_superclass(:hooks, {}) + end + + # Prepare class invocation to search on Rails namespace if a previous + # added hook is being used. + def self.prepare_for_invocation(name, value) #:nodoc: + return super unless value.is_a?(String) || value.is_a?(Symbol) + + if value && constants = self.hooks[name] + value = name if TrueClass === value + Rails::Generators.find_by_namespace(value, *constants) + elsif klass = Rails::Generators.find_by_namespace(value) + klass + else + super + end + end + + # Small macro to add ruby as an option to the generator with proper + # default value plus an instance helper method called shebang. + def self.add_shebang_option! + class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command, + desc: "Path to the Ruby binary of your choice", banner: "PATH" + + no_tasks { + define_method :shebang do + @shebang ||= begin + command = if options[:ruby] == Thor::Util.ruby_command + "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}" + else + options[:ruby] + end + "#!#{command}" + end + end + } + end + + def self.usage_path + paths = [ + source_root && File.expand_path("../USAGE", source_root), + default_generator_root && File.join(default_generator_root, "USAGE") + ] + paths.compact.detect { |path| File.exist? path } + end + + def self.default_generator_root + path = File.expand_path(File.join(base_name, generator_name), base_root) + path if File.exist?(path) + end + + end + end +end diff --git a/railties/lib/rails/generators/css/assets/assets_generator.rb b/railties/lib/rails/generators/css/assets/assets_generator.rb new file mode 100644 index 0000000000..e4a305f4b3 --- /dev/null +++ b/railties/lib/rails/generators/css/assets/assets_generator.rb @@ -0,0 +1,13 @@ +require "rails/generators/named_base" + +module Css # :nodoc: + module Generators # :nodoc: + class AssetsGenerator < Rails::Generators::NamedBase # :nodoc: + source_root File.expand_path("../templates", __FILE__) + + def copy_stylesheet + copy_file "stylesheet.css", File.join('app/assets/stylesheets', class_path, "#{file_name}.css") + end + end + end +end diff --git a/railties/lib/rails/generators/css/assets/templates/stylesheet.css b/railties/lib/rails/generators/css/assets/templates/stylesheet.css new file mode 100644 index 0000000000..afad32db02 --- /dev/null +++ b/railties/lib/rails/generators/css/assets/templates/stylesheet.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..cf534030f9 --- /dev/null +++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb @@ -0,0 +1,16 @@ +require "rails/generators/named_base" + +module Css # :nodoc: + module Generators # :nodoc: + class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc: + # In order to allow the Sass generators to pick up the default Rails CSS and + # transform it, we leave it in a standard location for the CSS stylesheet + # generators to handle. For the simple, default case, just copy it over. + def copy_stylesheet + dir = Rails::Generators::ScaffoldGenerator.source_root + file = File.join(dir, "scaffold.css") + create_file "app/assets/stylesheets/scaffold.css", File.read(file) + end + end + end +end diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb new file mode 100644 index 0000000000..0755ac335c --- /dev/null +++ b/railties/lib/rails/generators/erb.rb @@ -0,0 +1,25 @@ +require 'rails/generators/named_base' + +module Erb # :nodoc: + module Generators # :nodoc: + class Base < Rails::Generators::NamedBase #:nodoc: + protected + + def formats + [format] + end + + def format + :html + end + + def handler + :erb + end + + def filename_with_extensions(name, format = self.format) + [name, format, handler].compact.join(".") + end + end + end +end diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb new file mode 100644 index 0000000000..94c1b835d1 --- /dev/null +++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb @@ -0,0 +1,22 @@ +require 'rails/generators/erb' + +module Erb # :nodoc: + module Generators # :nodoc: + class ControllerGenerator < Base # :nodoc: + argument :actions, type: :array, default: [], banner: "action action" + + def copy_view_files + base_path = File.join("app/views", class_path, file_name) + empty_directory base_path + + actions.each do |action| + @action = action + formats.each do |format| + @path = File.join(base_path, filename_with_extensions(action, format)) + template filename_with_extensions(:view, format), @path + end + end + end + end + end +end diff --git a/railties/lib/rails/generators/erb/controller/templates/view.html.erb b/railties/lib/rails/generators/erb/controller/templates/view.html.erb new file mode 100644 index 0000000000..cd54d13d83 --- /dev/null +++ b/railties/lib/rails/generators/erb/controller/templates/view.html.erb @@ -0,0 +1,2 @@ +<h1><%= class_name %>#<%= @action %></h1> +<p>Find me in <%= @path %></p> diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb new file mode 100644 index 0000000000..66b17bd10e --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -0,0 +1,13 @@ +require 'rails/generators/erb/controller/controller_generator' + +module Erb # :nodoc: + module Generators # :nodoc: + class MailerGenerator < ControllerGenerator # :nodoc: + protected + + def formats + [:text, :html] + end + end + end +end diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.html.erb b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb new file mode 100644 index 0000000000..b5045671b3 --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/templates/view.html.erb @@ -0,0 +1,5 @@ +<h1><%= class_name %>#<%= @action %></h1> + +<p> + <%%= @greeting %>, find me in <%= @path %> +</p> diff --git a/railties/lib/rails/generators/erb/mailer/templates/view.text.erb b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb new file mode 100644 index 0000000000..342285df19 --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/templates/view.text.erb @@ -0,0 +1,3 @@ +<%= class_name %>#<%= @action %> + +<%%= @greeting %>, find me in <%= @path %> diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..c94829a0ae --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb @@ -0,0 +1,31 @@ +require 'rails/generators/erb' +require 'rails/generators/resource_helpers' + +module Erb # :nodoc: + module Generators # :nodoc: + class ScaffoldGenerator < Base # :nodoc: + include Rails::Generators::ResourceHelpers + + argument :attributes, type: :array, default: [], banner: "field:type field:type" + + def create_root_folder + empty_directory File.join("app/views", controller_file_path) + end + + def copy_view_files + available_views.each do |view| + formats.each do |format| + filename = filename_with_extensions(view, format) + template filename, File.join("app/views", controller_file_path, filename) + end + end + end + + protected + + def available_views + %w(index edit show new _form) + end + end + end +end diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb new file mode 100644 index 0000000000..bba9141fb8 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb @@ -0,0 +1,32 @@ +<%%= form_for(@<%= singular_table_name %>) do |f| %> + <%% if @<%= singular_table_name %>.errors.any? %> + <div id="error_explanation"> + <h2><%%= pluralize(@<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2> + + <ul> + <%% @<%= singular_table_name %>.errors.full_messages.each do |message| %> + <li><%%= message %></li> + <%% end %> + </ul> + </div> + <%% end %> + +<% attributes.each do |attribute| -%> + <div class="field"> +<% if attribute.password_digest? -%> + <%%= f.label :password %><br> + <%%= f.password_field :password %> + </div> + <div class="field"> + <%%= f.label :password_confirmation %><br> + <%%= f.password_field :password_confirmation %> +<% else -%> + <%%= f.label :<%= attribute.column_name %> %><br> + <%%= f.<%= attribute.field_type %> :<%= attribute.column_name %> %> +<% end -%> + </div> +<% end -%> + <div class="actions"> + <%%= f.submit %> + </div> +<%% end %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb new file mode 100644 index 0000000000..5620fcc850 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb @@ -0,0 +1,6 @@ +<h1>Editing <%= singular_table_name.titleize %></h1> + +<%%= render 'form' %> + +<%%= link_to 'Show', @<%= singular_table_name %> %> | +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb new file mode 100644 index 0000000000..5e194783ff --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb @@ -0,0 +1,31 @@ +<p id="notice"><%%= notice %></p> + +<h1>Listing <%= plural_table_name.titleize %></h1> + +<table> + <thead> + <tr> +<% attributes.reject(&:password_digest?).each do |attribute| -%> + <th><%= attribute.human_name %></th> +<% end -%> + <th colspan="3"></th> + </tr> + </thead> + + <tbody> + <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> + <tr> +<% attributes.reject(&:password_digest?).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 %>, method: :delete, data: { confirm: 'Are you sure?' } %></td> + </tr> + <%% end %> + </tbody> +</table> + +<br> + +<%%= link_to 'New <%= human_name %>', new_<%= singular_table_name %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb new file mode 100644 index 0000000000..db13a5d870 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb @@ -0,0 +1,5 @@ +<h1>New <%= singular_table_name.titleize %></h1> + +<%%= render 'form' %> + +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb new file mode 100644 index 0000000000..5e634153be --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb @@ -0,0 +1,11 @@ +<p id="notice"><%%= notice %></p> + +<% attributes.reject(&:password_digest?).each do |attribute| -%> +<p> + <strong><%= attribute.human_name %>:</strong> + <%%= @<%= singular_table_name %>.<%= attribute.name %> %> +</p> + +<% end -%> +<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> | +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb new file mode 100644 index 0000000000..c5326d70d1 --- /dev/null +++ b/railties/lib/rails/generators/generated_attribute.rb @@ -0,0 +1,150 @@ +require 'active_support/time' + +module Rails + module Generators + class GeneratedAttribute # :nodoc: + INDEX_OPTIONS = %w(index uniq) + UNIQ_INDEX_OPTIONS = %w(uniq) + + attr_accessor :name, :type + attr_reader :attr_options + attr_writer :index_name + + 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) + type = type.to_sym if type + + if type && reference?(type) + references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { unique: true } : true + attr_options[:index] = references_index + end + + new(name, type, has_index, attr_options) + end + + def reference?(type) + [:references, :belongs_to].include? type + end + + private + + # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to + # 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 + when /(references|belongs_to)\{polymorphic\}/ + return $1, polymorphic: true + else + return type, {} + end + end + end + + def initialize(name, type=nil, index_type=false, attr_options={}) + @name = name + @type = type || :string + @has_index = INDEX_OPTIONS.include?(index_type) + @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type) + @attr_options = attr_options + end + + def field_type + @field_type ||= case type + when :integer then :number_field + when :float, :decimal then :text_field + when :time then :time_select + when :datetime, :timestamp then :datetime_select + when :date then :date_select + when :text then :text_area + when :boolean then :check_box + else + :text_field + end + end + + def default + @default ||= case type + when :integer then 1 + when :float then 1.5 + when :decimal then "9.99" + when :datetime, :timestamp, :time then Time.now.to_s(:db) + when :date then Date.today.to_s(:db) + when :string then name == "type" ? "" : "MyString" + when :text then "MyText" + when :boolean then false + when :references, :belongs_to then nil + else + "" + end + end + + def plural_name + name.sub(/_id$/, '').pluralize + end + + def singular_name + name.sub(/_id$/, '').singularize + end + + def human_name + name.humanize + end + + def index_name + @index_name ||= if polymorphic? + %w(id type).map { |t| "#{name}_#{t}" } + else + column_name + end + end + + def column_name + @column_name ||= reference? ? "#{name}_id" : name + end + + def foreign_key? + !!(name =~ /_id$/) + end + + def reference? + self.class.reference?(type) + end + + def polymorphic? + self.attr_options.has_key?(:polymorphic) + end + + def has_index? + @has_index + end + + def has_uniq_index? + @has_uniq_index + end + + def password_digest? + name == 'password' && type == :digest + 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 +end diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb new file mode 100644 index 0000000000..1e925b2cd2 --- /dev/null +++ b/railties/lib/rails/generators/js/assets/assets_generator.rb @@ -0,0 +1,13 @@ +require "rails/generators/named_base" + +module Js # :nodoc: + module Generators # :nodoc: + class AssetsGenerator < Rails::Generators::NamedBase # :nodoc: + source_root File.expand_path("../templates", __FILE__) + + def copy_javascript + copy_file "javascript.js", File.join('app/assets/javascripts', class_path, "#{file_name}.js") + end + end + end +end diff --git a/railties/lib/rails/generators/js/assets/templates/javascript.js b/railties/lib/rails/generators/js/assets/templates/javascript.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/railties/lib/rails/generators/js/assets/templates/javascript.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb new file mode 100644 index 0000000000..cd388e590a --- /dev/null +++ b/railties/lib/rails/generators/migration.rb @@ -0,0 +1,69 @@ +require 'active_support/concern' +require 'rails/generators/actions/create_migration' + +module Rails + module Generators + # Holds common methods for migrations. It assumes that migrations has the + # [0-9]*_name format and can be used by another frameworks (like Sequel) + # just by implementing the next migration version method. + module Migration + extend ActiveSupport::Concern + attr_reader :migration_number, :migration_file_name, :migration_class_name + + module ClassMethods + def migration_lookup_at(dirname) #:nodoc: + Dir.glob("#{dirname}/[0-9]*_*.rb") + end + + def migration_exists?(dirname, file_name) #:nodoc: + migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first + end + + def current_migration_number(dirname) #:nodoc: + migration_lookup_at(dirname).collect do |file| + File.basename(file).split("_").first.to_i + end.max.to_i + end + + def next_migration_number(dirname) #:nodoc: + raise NotImplementedError + end + end + + def create_migration(destination, data, config = {}, &block) + action Rails::Generators::Actions::CreateMigration.new(self, destination, block || data.to_s, config) + end + + def set_migration_assigns!(destination) + destination = File.expand_path(destination, self.destination_root) + + migration_dir = File.dirname(destination) + @migration_number = self.class.next_migration_number(migration_dir) + @migration_file_name = File.basename(destination, '.rb') + @migration_class_name = @migration_file_name.camelize + end + + # Creates a migration template at the given destination. The difference + # to the default template method is that the migration version is appended + # to the destination file name. + # + # The migration version, migration file name, migration class name are + # available as instance variables in the template to be rendered. + # + # migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb" + def migration_template(source, destination, config = {}) + source = File.expand_path(find_in_source_paths(source.to_s)) + + set_migration_assigns!(destination) + context = instance_eval('binding') + + dir, base = File.split(destination) + numbered_destination = File.join(dir, ["%migration_number%", base].join('_')) + + create_migration numbered_destination, nil, config do + ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context) + end + end + end + end +end diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb new file mode 100644 index 0000000000..42c646543e --- /dev/null +++ b/railties/lib/rails/generators/model_helpers.rb @@ -0,0 +1,28 @@ +require 'rails/generators/active_model' + +module Rails + module Generators + module ModelHelpers # :nodoc: + PLURAL_MODEL_NAME_WARN_MESSAGE = "[WARNING] The model name '%s' was recognized as a plural, using the singular '%s' instead. " \ + "Override with --force-plural or setup custom inflection rules for this noun before running the generator." + mattr_accessor :skip_warn + + def self.included(base) #:nodoc: + base.class_option :force_plural, type: :boolean, default: false, desc: 'Forces the use of the given model name' + end + + def initialize(args, *_options) + super + if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural] + singular = name.singularize + unless ModelHelpers.skip_warn + say PLURAL_MODEL_NAME_WARN_MESSAGE % [name, singular] + ModelHelpers.skip_warn = true + end + name.replace singular + assign_names!(name) + end + end + end + end +end diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb new file mode 100644 index 0000000000..b7da44ca2d --- /dev/null +++ b/railties/lib/rails/generators/named_base.rb @@ -0,0 +1,205 @@ +require 'active_support/core_ext/module/introspection' +require 'rails/generators/base' +require 'rails/generators/generated_attribute' + +module Rails + module Generators + class NamedBase < Base + argument :name, type: :string + class_option :skip_namespace, type: :boolean, default: false, + desc: "Skip namespace (affects only isolated applications)" + + def initialize(args, *options) #:nodoc: + @inside_template = nil + # Unfreeze name in case it's given as a frozen string + args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen? + super + assign_names!(self.name) + parse_attributes! if respond_to?(:attributes) + end + + # Defines the template that would be used for the migration file. + # The arguments include the source template file, the migration filename etc. + no_tasks do + def template(source, *args, &block) + inside_template do + super + end + end + end + + protected + attr_reader :file_name + + # FIXME: We are avoiding to use alias because a bug on thor that make + # this method public and add it to the task list. + def singular_name + file_name + end + + # Wrap block with namespace of current application + # if namespace exists and is not skipped + def module_namespacing(&block) + content = capture(&block) + content = wrap_with_namespace(content) if namespaced? + concat(content) + end + + def indent(content, multiplier = 2) + spaces = " " * multiplier + content.each_line.map {|line| line.blank? ? line : "#{spaces}#{line}" }.join + end + + def wrap_with_namespace(content) + content = indent(content).chomp + "module #{namespace.name}\n#{content}\nend\n" + end + + def inside_template + @inside_template = true + yield + ensure + @inside_template = false + end + + def inside_template? + @inside_template + end + + def namespace + Rails::Generators.namespace + end + + def namespaced? + !options[:skip_namespace] && namespace + end + + def file_path + @file_path ||= (class_path + [file_name]).join('/') + end + + def class_path + inside_template? || !namespaced? ? regular_class_path : namespaced_class_path + end + + def regular_class_path + @class_path + end + + def namespaced_file_path + @namespaced_file_path ||= namespaced_class_path.join("/") + end + + def namespaced_class_path + @namespaced_class_path ||= [namespaced_path] + @class_path + end + + def namespaced_path + @namespaced_path ||= namespace.name.split("::").first.underscore + end + + def class_name + (class_path + [file_name]).map!{ |m| m.camelize }.join('::') + end + + def human_name + @human_name ||= singular_name.humanize + end + + def plural_name + @plural_name ||= singular_name.pluralize + end + + def i18n_scope + @i18n_scope ||= file_path.tr('/', '.') + end + + def table_name + @table_name ||= begin + base = pluralize_table_names? ? plural_name : singular_name + (class_path + [base]).join('_') + end + end + + def uncountable? + singular_name == plural_name + end + + def index_helper + uncountable? ? "#{plural_table_name}_index" : plural_table_name + end + + def singular_table_name + @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name) + end + + def plural_table_name + @plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize) + end + + def plural_file_name + @plural_file_name ||= file_name.pluralize + end + + def route_url + @route_url ||= class_path.collect {|dname| "/" + dname }.join + "/" + plural_file_name + end + + # Tries to retrieve the application name or simple return application. + def application_name + if defined?(Rails) && Rails.application + Rails.application.class.name.split('::').first.underscore + else + "application" + end + end + + def assign_names!(name) #:nodoc: + @class_path = name.include?('/') ? name.split('/') : name.split('::') + @class_path.map! { |m| m.underscore } + @file_name = @class_path.pop + end + + # Convert attributes array into GeneratedAttribute objects. + def parse_attributes! #:nodoc: + self.attributes = (attributes || []).map do |attr| + Rails::Generators::GeneratedAttribute.parse(attr) + end + end + + def attributes_names + @attributes_names ||= attributes.each_with_object([]) do |a, names| + names << a.column_name + names << 'password_confirmation' if a.password_digest? + names << "#{a.name}_type" if a.polymorphic? + end + end + + def pluralize_table_names? + !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names + end + + # Add a class collisions name to be checked on class initialization. You + # can supply a hash with a :prefix or :suffix to be tested. + # + # ==== Examples + # + # check_class_collision suffix: "Decorator" + # + # If the generator is invoked with class name Admin, it will check for + # the presence of "AdminDecorator". + # + def self.check_class_collision(options={}) + define_method :check_class_collision do + name = if self.respond_to?(:controller_class_name) # for ScaffoldBase + controller_class_name + else + class_name + end + + class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}" + end + end + end + end +end diff --git a/railties/lib/rails/generators/rails/app/USAGE b/railties/lib/rails/generators/rails/app/USAGE new file mode 100644 index 0000000000..691095f33f --- /dev/null +++ b/railties/lib/rails/generators/rails/app/USAGE @@ -0,0 +1,15 @@ +Description: + The 'rails new' command creates a new Rails application with a default + directory structure and configuration at the path you specify. + + You can specify extra command-line arguments to be used every time + 'rails new' runs in the .railsrc configuration file in your home directory. + + Note that the arguments specified in the .railsrc file don't affect the + defaults values shown above in this help message. + +Example: + rails new ~/Code/Ruby/weblog + + This generates a skeletal Rails installation in ~/Code/Ruby/weblog. + See the README in the newly created application to get going. diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb new file mode 100644 index 0000000000..9110c129d1 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -0,0 +1,407 @@ +require 'rails/generators/app_base' + +module Rails + module ActionMethods # :nodoc: + attr_reader :options + + def initialize(generator) + @generator = generator + @options = generator.options + end + + private + %w(template copy_file directory empty_directory inside + empty_directory_with_keep_file create_file chmod shebang).each do |method| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args, &block) + @generator.send(:#{method}, *args, &block) + end + RUBY + end + + # TODO: Remove once this is fully in place + def method_missing(meth, *args, &block) + @generator.send(meth, *args, &block) + end + end + + # The application builder allows you to override elements of the application + # generator without being forced to reverse the operations of the default + # generator. + # + # This allows you to override entire operations, like the creation of the + # Gemfile, README, or JavaScript files, without needing to know exactly + # what those operations do so you can create another template action. + class AppBuilder + def rakefile + template "Rakefile" + end + + def readme + copy_file "README.rdoc", "README.rdoc" + end + + def gemfile + template "Gemfile" + end + + def configru + template "config.ru" + end + + def gitignore + template "gitignore", ".gitignore" + end + + def app + directory 'app' + + keep_file 'app/assets/images' + keep_file 'app/mailers' + keep_file 'app/models' + + keep_file 'app/controllers/concerns' + keep_file 'app/models/concerns' + end + + def bin + directory "bin" do |content| + "#{shebang}\n" + content + end + chmod "bin", 0755 & ~File.umask, verbose: false + end + + def config + empty_directory "config" + + inside "config" do + template "routes.rb" + template "application.rb" + template "environment.rb" + template "secrets.yml" + + directory "environments" + directory "initializers" + directory "locales" + end + end + + def config_when_updating + cookie_serializer_config_exist = File.exist?('config/initializers/cookies_serializer.rb') + + config + + unless cookie_serializer_config_exist + gsub_file 'config/initializers/cookies_serializer.rb', /json/, 'marshal' + end + end + + def database_yml + template "config/databases/#{options[:database]}.yml", "config/database.yml" + end + + def db + directory "db" + end + + def lib + empty_directory 'lib' + empty_directory_with_keep_file 'lib/tasks' + empty_directory_with_keep_file 'lib/assets' + end + + def log + empty_directory_with_keep_file 'log' + end + + def public_directory + directory "public", "public", recursive: false + end + + def test + empty_directory_with_keep_file 'test/fixtures' + empty_directory_with_keep_file 'test/controllers' + empty_directory_with_keep_file 'test/mailers' + empty_directory_with_keep_file 'test/models' + empty_directory_with_keep_file 'test/helpers' + empty_directory_with_keep_file 'test/integration' + + template 'test/test_helper.rb' + end + + def tmp + empty_directory "tmp/cache" + empty_directory "tmp/cache/assets" + end + + def vendor + vendor_javascripts + vendor_stylesheets + end + + def vendor_javascripts + unless options[:skip_javascript] + empty_directory_with_keep_file 'vendor/assets/javascripts' + end + end + + def vendor_stylesheets + empty_directory_with_keep_file 'vendor/assets/stylesheets' + end + end + + module Generators + # We need to store the RAILS_DEV_PATH in a constant, otherwise the path + # can change in Ruby 1.8.7 when we FileUtils.cd. + RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__)) + RESERVED_NAMES = %w[application destroy plugin runner test] + + class AppGenerator < AppBase # :nodoc: + add_shared_options_for "application" + + # Add bin/rails options + class_option :version, type: :boolean, aliases: "-v", group: :rails, + desc: "Show Rails version number and quit" + + def initialize(*args) + super + + unless app_path + raise Error, "Application name should be provided in arguments. For details run: rails --help" + end + + if !options[:skip_active_record] && !DATABASES.include?(options[:database]) + raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." + end + end + + public_task :set_default_accessors! + public_task :create_root + + def create_root_files + build(:readme) + build(:rakefile) + build(:configru) + build(:gitignore) unless options[:skip_git] + build(:gemfile) unless options[:skip_gemfile] + end + + def create_app_files + build(:app) + end + + def create_bin_files + build(:bin) + end + + def create_config_files + build(:config) + end + + def update_config_files + build(:config_when_updating) + end + remove_task :update_config_files + + def create_boot_file + template "config/boot.rb" + end + + def create_active_record_files + return if options[:skip_active_record] + build(:database_yml) + end + + def create_db_files + build(:db) + end + + def create_lib_files + build(:lib) + end + + def create_log_files + build(:log) + end + + def create_public_files + build(:public_directory) + end + + def create_test_files + build(:test) unless options[:skip_test_unit] + end + + def create_tmp_files + build(:tmp) + end + + def create_vendor_files + build(:vendor) + end + + def delete_js_folder_skipping_javascript + if options[:skip_javascript] + remove_dir 'app/assets/javascripts' + end + end + + def delete_assets_initializer_skipping_sprockets + if options[:skip_sprockets] + remove_file 'config/initializers/assets.rb' + end + end + + def finish_template + build(:leftovers) + end + + public_task :apply_rails_template, :run_bundle + public_task :generate_spring_binstubs + + def run_after_bundle_callbacks + @after_bundle_callbacks.each do |callback| + callback.call + end + end + + protected + + def self.banner + "rails new #{self.arguments.map(&:usage).join(' ')} [options]" + end + + # Define file as an alias to create_file for backwards compatibility. + def file(*args, &block) + create_file(*args, &block) + end + + def app_name + @app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr('\\', '').tr(". ", "_") + end + + def defined_app_name + defined_app_const_base.underscore + end + + def defined_app_const_base + Rails.respond_to?(:application) && defined?(Rails::Application) && + Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "") + end + + alias :defined_app_const_base? :defined_app_const_base + + def app_const_base + @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, '_').squeeze('_').camelize + end + alias :camelized :app_const_base + + def app_const + @app_const ||= "#{app_const_base}::Application" + end + + def valid_const? + if app_const =~ /^\d/ + raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers." + elsif RESERVED_NAMES.include?(app_name) + raise Error, "Invalid application name #{app_name}. Please give a name which does not match one of the reserved rails words." + elsif Object.const_defined?(app_const_base) + raise Error, "Invalid application name #{app_name}, constant #{app_const_base} is already in use. Please choose another application name." + end + end + + def app_secret + SecureRandom.hex(64) + 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 RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + end + + def get_builder_class + defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder + end + end + + # This class handles preparation of the arguments before the AppGenerator is + # called. The class provides version or help information if they were + # requested, and also constructs the railsrc file (used for extra configuration + # options). + # + # This class should be called before the AppGenerator is required and started + # since it configures and mutates ARGV correctly. + class ARGVScrubber # :nodoc + def initialize(argv = ARGV) + @argv = argv + end + + def prepare! + handle_version_request!(@argv.first) + handle_invalid_command!(@argv.first, @argv) do + handle_rails_rc!(@argv.drop(1)) + end + end + + def self.default_rc_file + File.expand_path('~/.railsrc') + end + + private + + def handle_version_request!(argument) + if ['--version', '-v'].include?(argument) + require 'rails/version' + puts "Rails #{Rails::VERSION::STRING}" + exit(0) + end + end + + def handle_invalid_command!(argument, argv) + if argument == "new" + yield + else + ['--help'] + argv.drop(1) + end + end + + def handle_rails_rc!(argv) + if argv.find { |arg| arg == '--no-rc' } + argv.reject { |arg| arg == '--no-rc' } + else + railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) } + end + end + + def railsrc(argv) + if (customrc = argv.index{ |x| x.include?("--rc=") }) + fname = File.expand_path(argv[customrc].gsub(/--rc=/, "")) + yield(argv.take(customrc) + argv.drop(customrc + 1), fname) + else + yield argv, self.class.default_rc_file + end + end + + def read_rc_file(railsrc) + extra_args = File.readlines(railsrc).flat_map(&:split) + puts "Using #{extra_args.join(" ")} from #{railsrc}" + extra_args + end + + def insert_railsrc_into_argv!(argv, railsrc) + return argv unless File.exist?(railsrc) + extra_args = read_rc_file railsrc + argv.take(1) + extra_args + argv.drop(1) + end + end + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile new file mode 100644 index 0000000000..8b51fda359 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -0,0 +1,45 @@ +source 'https://rubygems.org' + +<% gemfile_entries.each do |gem| -%> +<% if gem.comment -%> + +# <%= gem.comment %> +<% end -%> +<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> +<% if gem.options.any? -%> +, <%= gem.options.map { |k,v| + "#{k}: #{v.inspect}" }.join(', ') %> +<% end -%> +<% end -%> + +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use Unicorn as the app server +# gem 'unicorn' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +group :development, :test do +<% unless defined?(JRUBY_VERSION) -%> + # Call 'debugger' anywhere in the code to stop execution and get a debugger console + <%- if RUBY_VERSION < '2.0.0' -%> + gem 'debugger' + <%- else -%> + gem 'byebug' + <%- end -%> + + # Access an IRB console on exceptions page and /console in development + gem 'web-console' +<%- if spring_install? %> + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' +<% end -%> +<% end -%> +end + +<% if RUBY_PLATFORM.match(/bccwin|cygwin|emx|mingw|mswin|wince/) -%> +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw] +<% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/README.rdoc b/railties/lib/rails/generators/rails/app/templates/README.rdoc new file mode 100644 index 0000000000..dd4e97e22e --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/README.rdoc @@ -0,0 +1,28 @@ +== README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... + + +Please feel free to use a different markup language if you do not plan to run +<tt>rake doc:app</tt>. diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile new file mode 100644 index 0000000000..ba6b733dd2 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require File.expand_path('../config/application', __FILE__) + +Rails.application.load_tasks diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt new file mode 100644 index 0000000000..07ea09cdbd --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt @@ -0,0 +1,20 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. +// +// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details +// about supported directives. +// +<% unless options[:skip_javascript] -%> +//= require <%= options[:javascript] %> +//= require <%= options[:javascript] %>_ujs +<% if gemfile_entries.any? { |m| m.name == "turbolinks" } -%> +//= require turbolinks +<% end -%> +<% end -%> +//= require_tree . diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css new file mode 100644 index 0000000000..a443db3401 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any styles + * defined in the other CSS/SCSS files in this directory. It is generally better to create a new + * file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt new file mode 100644 index 0000000000..d83690e1b9 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt @@ -0,0 +1,5 @@ +class ApplicationController < ActionController::Base + # Prevent CSRF attacks by raising an exception. + # For APIs, you may want to use :null_session instead. + protect_from_forgery with: :exception +end diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb new file mode 100644 index 0000000000..de6be7945c --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end 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 new file mode 100644 index 0000000000..75ea52828e --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> + <title><%= camelized %></title> + <%- if options[:skip_javascript] -%> + <%%= stylesheet_link_tag 'application', media: 'all' %> + <%- else -%> + <%- if gemfile_entries.any? { |m| m.name == 'turbolinks' } -%> + <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> + <%%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> + <%- else -%> + <%%= stylesheet_link_tag 'application', media: 'all' %> + <%%= javascript_include_tag 'application' %> + <%- end -%> + <%- end -%> + <%%= csrf_meta_tags %> +</head> +<body> + +<%%= yield %> + +</body> +</html> diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle b/railties/lib/rails/generators/rails/app/templates/bin/bundle new file mode 100644 index 0000000000..1123dcf501 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/bundle @@ -0,0 +1,2 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rails b/railties/lib/rails/generators/rails/app/templates/bin/rails new file mode 100644 index 0000000000..6a128b95e5 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/rails @@ -0,0 +1,3 @@ +APP_PATH = File.expand_path('../../config/application', __FILE__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rake b/railties/lib/rails/generators/rails/app/templates/bin/rake new file mode 100644 index 0000000000..d14fc8395b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/rake @@ -0,0 +1,3 @@ +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup b/railties/lib/rails/generators/rails/app/templates/bin/setup new file mode 100644 index 0000000000..0e22b3fa5c --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/setup @@ -0,0 +1,28 @@ +require 'pathname' + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +Dir.chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file: + + puts "== Installing dependencies ==" + system "gem install bundler --conservative" + system "bundle check || bundle install" + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # system "cp config/database.yml.sample config/database.yml" + # end + + puts "\n== Preparing database ==" + system "bin/rake db:setup" + + puts "\n== Removing old logs and tempfiles ==" + system "rm -f log/*" + system "rm -rf tmp/cache" + + puts "\n== Restarting application server ==" + system "touch tmp/restart.txt" +end diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru new file mode 100644 index 0000000000..5bc2a619e8 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run Rails.application diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb new file mode 100644 index 0000000000..16fe50bab8 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -0,0 +1,34 @@ +require File.expand_path('../boot', __FILE__) + +<% if include_all_railties? -%> +require 'rails/all' +<% else -%> +# Pick the frameworks you want: +require "active_model/railtie" +<%= comment_if :skip_active_record %>require "active_record/railtie" +require "action_controller/railtie" +require "action_mailer/railtie" +<%= comment_if :skip_action_view %>require "action_view/railtie" +<%= comment_if :skip_sprockets %>require "sprockets/railtie" +<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie" +<% end -%> + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module <%= app_const_base %> + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. + # config.time_zone = 'Central Time (US & Canada)' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb new file mode 100644 index 0000000000..6b750f00b1 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml new file mode 100644 index 0000000000..34fc0e3465 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml @@ -0,0 +1,49 @@ +# FrontBase versions 4.x +# +# Get the bindings: +# gem install ruby-frontbase +# +# Configure Using Gemfile +# gem 'ruby-frontbase' +# +default: &default + adapter: frontbase + host: localhost + username: <%= app_name %> + password: '' + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="frontbase://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml new file mode 100644 index 0000000000..187ff01bac --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml @@ -0,0 +1,85 @@ +# IBM Dataservers +# +# Home Page +# https://github.com/dparnell/ibm_db +# +# To install the ibm_db gem: +# +# On Linux: +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include +# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32 +# gem install ibm_db +# +# On Mac OS X 10.5: +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include +# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib32 +# export ARCHFLAGS="-arch i386" +# gem install ibm_db +# +# On Mac OS X 10.6: +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_INCLUDE=/opt/ibm/db2/V9.7/include +# export IBM_DB_LIB=/opt/ibm/db2/V9.7/lib64 +# export ARCHFLAGS="-arch x86_64" +# gem install ibm_db +# +# On Windows: +# Issue the command: gem install ibm_db +# +# Configure Using Gemfile +# gem 'ibm_db' +# +# +default: &default + adapter: ibm_db + username: db2inst1 + password: + #schema: db2inst1 + #host: localhost + #port: 50000 + #account: my_account + #app_user: my_app_user + #application: my_application + #workstation: my_workstation + #security: SSL + #timeout: 10 + #authentication: SERVER + #parameterized: false + +development: + <<: *default + database: <%= app_name[0,4] %>_dev + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name[0,4] %>_tst + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="ibm-db://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml new file mode 100644 index 0000000000..db0a429753 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml @@ -0,0 +1,68 @@ +# If you are using mssql, derby, hsqldb, or h2 with one of the +# ActiveRecord JDBC adapters, install the appropriate driver, e.g.,: +# gem install activerecord-jdbcmssql-adapter +# +# Configure using Gemfile: +# gem 'activerecord-jdbcmssql-adapter' +# +# development: +# adapter: mssql +# username: <%= app_name %> +# password: +# host: localhost +# database: <%= app_name %>_development +# +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +# +# test: +# adapter: mssql +# username: <%= app_name %> +# password: +# host: localhost +# database: <%= app_name %>_test +# +# production: +# adapter: mssql +# username: <%= app_name %> +# password: +# host: localhost +# database: <%= app_name %>_production + +# If you are using oracle, db2, sybase, informix or prefer to use the plain +# JDBC adapter, configure your database setting as the example below (requires +# you to download and manually install the database vendor's JDBC driver .jar +# file). See your driver documentation for the appropriate driver class and +# connection string: + +default: &default + adapter: jdbc + username: <%= app_name %> + password: + driver: + +development: + <<: *default + url: jdbc:db://localhost/<%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + url: jdbc:db://localhost/<%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +production: + url: jdbc:db://localhost/<%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml new file mode 100644 index 0000000000..acb93939e1 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml @@ -0,0 +1,52 @@ +# MySQL. Versions 4.1 and 5.0 are recommended. +# +# Install the MySQL driver: +# gem install activerecord-jdbcmysql-adapter +# +# Configure Using Gemfile +# gem 'activerecord-jdbcmysql-adapter' +# +# And be sure to use new-style password hashing: +# http://dev.mysql.com/doc/refman/5.0/en/old-client.html +# +default: &default + adapter: mysql + username: root + password: + host: localhost + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml new file mode 100644 index 0000000000..9e99264d33 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml @@ -0,0 +1,68 @@ +# PostgreSQL. Versions 8.2 and up are supported. +# +# Configure Using Gemfile +# gem 'activerecord-jdbcpostgresql-adapter' +# +default: &default + adapter: postgresql + encoding: unicode + +development: + <<: *default + database: <%= app_name %>_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user that initialized the database. + #username: <%= app_name %> + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml new file mode 100644 index 0000000000..28c36eb82f --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml @@ -0,0 +1,23 @@ +# SQLite version 3.x +# gem 'activerecord-jdbcsqlite3-adapter' +# +# Configure Using Gemfile +# gem 'activerecord-jdbcsqlite3-adapter' +# +default: &default + adapter: sqlite3 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml new file mode 100644 index 0000000000..4b2e6646c7 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml @@ -0,0 +1,58 @@ +# MySQL. Versions 5.0+ are recommended. +# +# Install the MYSQL driver +# gem install mysql2 +# +# Ensure the MySQL gem is defined in your Gemfile +# gem 'mysql2' +# +# And be sure to use new-style password hashing: +# http://dev.mysql.com/doc/refman/5.0/en/old-client.html +# +default: &default + adapter: mysql2 + encoding: utf8 + pool: 5 + username: root + password: +<% if mysql_socket -%> + socket: <%= mysql_socket %> +<% else -%> + host: localhost +<% end -%> + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml new file mode 100644 index 0000000000..9aedcc15cb --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml @@ -0,0 +1,58 @@ +# Oracle/OCI 8i, 9, 10g +# +# Requires Ruby/OCI8: +# https://github.com/kubo/ruby-oci8 +# +# Specify your database using any valid connection syntax, such as a +# tnsnames.ora service name, or an SQL connect string of the form: +# +# //host:[port][/service name] +# +# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And +# until true bind variables are supported, cursor_sharing is set by default +# to 'similar'. Both can be changed in the configuration below; the defaults +# are equivalent to specifying: +# +# prefetch_rows: 100 +# cursor_sharing: similar +# +default: &default + adapter: oracle + username: <%= app_name %> + password: + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="oracle://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml new file mode 100644 index 0000000000..feb25bbc6b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml @@ -0,0 +1,85 @@ +# PostgreSQL. Versions 8.2 and up are supported. +# +# Install the pg driver: +# gem install pg +# On OS X with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On OS X with MacPorts: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem 'pg' +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see rails configuration guide + # http://guides.rubyonrails.org/configuring.html#database-pooling + pool: 5 + +development: + <<: *default + database: <%= app_name %>_development + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user that initialized the database. + #username: <%= app_name %> + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml new file mode 100644 index 0000000000..1c1a37ca8d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: sqlite3 + pool: 5 + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml new file mode 100644 index 0000000000..30b0df34a8 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml @@ -0,0 +1,68 @@ +# SQL Server (2005 or higher recommended) +# +# Install the adapters and driver +# gem install tiny_tds +# gem install activerecord-sqlserver-adapter +# +# Ensure the activerecord adapter and db driver gems are defined in your Gemfile +# gem 'tiny_tds' +# gem 'activerecord-sqlserver-adapter' +# +# You should make sure freetds is configured correctly first. +# freetds.conf contains host/port/protocol_versions settings. +# http://freetds.schemamania.org/userguide/freetdsconf.htm +# +# A typical Microsoft server +# [mssql] +# host = mssqlserver.yourdomain.com +# port = 1433 +# tds version = 7.1 + +# If you can connect with "tsql -S servername", your basic FreeTDS installation is working. +# 'man tsql' for more info +# Set timeout to a larger number if valid queries against a live db fail +# +default: &default + adapter: sqlserver + encoding: utf8 + reconnect: false + username: <%= app_name %> + password: + timeout: 25 + dataserver: from_freetds.conf + +development: + <<: *default + database: <%= app_name %>_development + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: <%= app_name %>_test + +# As with config/secrets.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password as a unix environment variable when you boot +# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full rundown on how to provide these environment variables in a +# production deployment. +# +# On Heroku and other platform providers, you may have a full connection URL +# available as an environment variable. For example: +# +# DATABASE_URL="sqlserver://myuser:mypass@localhost/somedatabase" +# +# You can use this database configuration with: +# +# production: +# url: <%%= ENV['DATABASE_URL'] %> +# +production: + <<: *default + database: <%= app_name %>_production + username: <%= app_name %> + password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb new file mode 100644 index 0000000000..ee8d90dc65 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require File.expand_path('../application', __FILE__) + +# Initialize the Rails application. +Rails.application.initialize! 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 new file mode 100644 index 0000000000..35e3035a0b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -0,0 +1,45 @@ +Rails.application.configure do + # 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 + + # Do not eager load code on boot. + config.eager_load = false + + # 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. + config.action_mailer.raise_delivery_errors = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + <%- unless options.skip_active_record? -%> + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + <%- end -%> + <%- unless options.skip_sprockets? -%> + # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. + config.assets.debug = true + + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. + config.assets.digest = true + + # Adds additional error checking when serving assets at runtime. + # Checks for improperly declared sprockets dependencies. + # Raises helpful error messages. + config.assets.raise_runtime_errors = true + <%- end -%> + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +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 new file mode 100644 index 0000000000..277fe01e89 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -0,0 +1,83 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Enable Rack::Cache to put a simple HTTP cache in front of your application + # Add `rack-cache` to your Gemfile before enabling this. + # For large-scale production use, consider using a caching reverse proxy like NGINX, varnish or squid. + # config.action_dispatch.rack_cache = true + + # Disable Rails's static asset server (Apache or NGINX will already do this). + config.serve_static_assets = false + + <%- unless options.skip_sprockets? -%> + # Compress JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. + config.assets.digest = true + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + <%- end -%> + + # 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 + + # Set to :info to decrease the log volume. + config.log_level = :debug + + # Prepend all log lines with the following tags. + # config.log_tags = [ :subdomain, :uuid ] + + # Use a different logger for distributed setups. + # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = "http://assets.example.com" + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Disable automatic flushing of the log to improve performance. + # config.autoflush_log = false + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + <%- unless options.skip_active_record? -%> + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + <%- 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 new file mode 100644 index 0000000000..053f5b66d7 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -0,0 +1,39 @@ +Rails.application.configure do + # 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 + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure static asset server for tests with Cache-Control for performance. + config.serve_static_assets = true + config.static_cache_control = 'public, max-age=3600' + + # 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. + config.action_dispatch.show_exceptions = 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 + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true +end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt new file mode 100644 index 0000000000..01ef3e6630 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt @@ -0,0 +1,11 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. +# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000000..59385cdf37 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb new file mode 100644 index 0000000000..7f70458dee --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000000..4a994e1e7b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb new file mode 100644 index 0000000000..ac033bf9dc --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb new file mode 100644 index 0000000000..dc1899682b --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt new file mode 100644 index 0000000000..2bb9b82c61 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %> diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt new file mode 100644 index 0000000000..f2110c2c70 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] if respond_to?(:wrap_parameters) +end +<%- unless options.skip_active_record? -%> + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end +<%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml new file mode 100644 index 0000000000..0653957166 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml @@ -0,0 +1,23 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb new file mode 100644 index 0000000000..3f66539d54 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -0,0 +1,56 @@ +Rails.application.routes.draw do + # The priority is based upon order of creation: first created -> highest priority. + # See how all your routes lay out with "rake routes". + + # You can have the root of your site routed with "root" + # root 'welcome#index' + + # Example of regular route: + # get 'products/:id' => 'catalog#view' + + # Example of named route that can be invoked with purchase_url(id: product.id) + # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase + + # Example resource route (maps HTTP verbs to controller actions automatically): + # resources :products + + # Example resource route with options: + # resources :products do + # member do + # get 'short' + # post 'toggle' + # end + # + # collection do + # get 'sold' + # end + # end + + # Example resource route with sub-resources: + # resources :products do + # resources :comments, :sales + # resource :seller + # end + + # Example resource route with more complex sub-resources: + # resources :products do + # resources :comments + # resources :sales do + # get 'recent', on: :collection + # end + # end + + # Example resource route with concerns: + # concern :toggleable do + # post 'toggle' + # end + # resources :posts, concerns: :toggleable + # resources :photos, concerns: :toggleable + + # Example resource route within a namespace: + # namespace :admin do + # # Directs /admin/products/* to Admin::ProductsController + # # (app/controllers/admin/products_controller.rb) + # resources :products + # end +end diff --git a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml new file mode 100644 index 0000000000..b2669a0f79 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml @@ -0,0 +1,22 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rake secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: <%= app_secret %> + +test: + secret_key_base: <%= app_secret %> + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%%= ENV["SECRET_KEY_BASE"] %> diff --git a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt new file mode 100644 index 0000000000..4edb1e857e --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) +# Mayor.create(name: 'Emanuel', city: cities.first) diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore new file mode 100644 index 0000000000..8775e5e235 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/gitignore @@ -0,0 +1,18 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +<% if sqlite3? -%> +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +<% end -%> +# Ignore all logfiles and tempfiles. +/log/*.log +/tmp diff --git a/railties/lib/rails/generators/rails/app/templates/public/404.html b/railties/lib/rails/generators/rails/app/templates/public/404.html new file mode 100644 index 0000000000..b612547fc2 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/public/404.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html> +<head> + <title>The page you were looking for doesn't exist (404)</title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <style> + body { + background-color: #EFEFEF; + color: #2E2F30; + text-align: center; + font-family: arial, sans-serif; + margin: 0; + } + + div.dialog { + width: 95%; + max-width: 33em; + margin: 4em auto 0; + } + + div.dialog > div { + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #BBB; + border-top: #B00100 solid 4px; + border-top-left-radius: 9px; + border-top-right-radius: 9px; + background-color: white; + padding: 7px 12% 0; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } + + h1 { + font-size: 100%; + color: #730E15; + line-height: 1.5em; + } + + div.dialog > p { + margin: 0 0 1em; + padding: 1em; + background-color: #F7F7F7; + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #999; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top-color: #DADADA; + color: #666; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } + </style> +</head> + +<body> + <!-- This file lives in public/404.html --> + <div class="dialog"> + <div> + <h1>The page you were looking for doesn't exist.</h1> + <p>You may have mistyped the address or the page may have moved.</p> + </div> + <p>If you are the application owner check the logs for more information.</p> + </div> +</body> +</html> diff --git a/railties/lib/rails/generators/rails/app/templates/public/422.html b/railties/lib/rails/generators/rails/app/templates/public/422.html new file mode 100644 index 0000000000..a21f82b3bd --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/public/422.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html> +<head> + <title>The change you wanted was rejected (422)</title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <style> + body { + background-color: #EFEFEF; + color: #2E2F30; + text-align: center; + font-family: arial, sans-serif; + margin: 0; + } + + div.dialog { + width: 95%; + max-width: 33em; + margin: 4em auto 0; + } + + div.dialog > div { + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #BBB; + border-top: #B00100 solid 4px; + border-top-left-radius: 9px; + border-top-right-radius: 9px; + background-color: white; + padding: 7px 12% 0; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } + + h1 { + font-size: 100%; + color: #730E15; + line-height: 1.5em; + } + + div.dialog > p { + margin: 0 0 1em; + padding: 1em; + background-color: #F7F7F7; + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #999; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top-color: #DADADA; + color: #666; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } + </style> +</head> + +<body> + <!-- This file lives in public/422.html --> + <div class="dialog"> + <div> + <h1>The change you wanted was rejected.</h1> + <p>Maybe you tried to change something you didn't have access to.</p> + </div> + <p>If you are the application owner check the logs for more information.</p> + </div> +</body> +</html> diff --git a/railties/lib/rails/generators/rails/app/templates/public/500.html b/railties/lib/rails/generators/rails/app/templates/public/500.html new file mode 100644 index 0000000000..061abc587d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/public/500.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> +<head> + <title>We're sorry, but something went wrong (500)</title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <style> + body { + background-color: #EFEFEF; + color: #2E2F30; + text-align: center; + font-family: arial, sans-serif; + margin: 0; + } + + div.dialog { + width: 95%; + max-width: 33em; + margin: 4em auto 0; + } + + div.dialog > div { + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #BBB; + border-top: #B00100 solid 4px; + border-top-left-radius: 9px; + border-top-right-radius: 9px; + background-color: white; + padding: 7px 12% 0; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } + + h1 { + font-size: 100%; + color: #730E15; + line-height: 1.5em; + } + + div.dialog > p { + margin: 0 0 1em; + padding: 1em; + background-color: #F7F7F7; + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #999; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top-color: #DADADA; + color: #666; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } + </style> +</head> + +<body> + <!-- This file lives in public/500.html --> + <div class="dialog"> + <div> + <h1>We're sorry, but something went wrong.</h1> + </div> + <p>If you are the application owner check the logs for more information.</p> + </div> +</body> +</html> diff --git a/railties/lib/rails/generators/rails/app/templates/public/favicon.ico b/railties/lib/rails/generators/rails/app/templates/public/favicon.ico new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/public/favicon.ico diff --git a/railties/lib/rails/generators/rails/app/templates/public/robots.txt b/railties/lib/rails/generators/rails/app/templates/public/robots.txt new file mode 100644 index 0000000000..3c9c7c01f3 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb new file mode 100644 index 0000000000..87b8fe3516 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb @@ -0,0 +1,12 @@ +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' + +class ActiveSupport::TestCase +<% unless options[:skip_active_record] -%> + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + +<% end -%> + # Add more helper methods to be used by all tests here... +end diff --git a/railties/lib/rails/generators/rails/assets/USAGE b/railties/lib/rails/generators/rails/assets/USAGE new file mode 100644 index 0000000000..d2e5ed4482 --- /dev/null +++ b/railties/lib/rails/generators/rails/assets/USAGE @@ -0,0 +1,20 @@ +Description: + Stubs out new asset placeholders. Pass the asset name, either CamelCased + or under_scored. + + To create an asset within a folder, specify the asset's name as a + path like 'parent/name'. + + This generates a JavaScript stub in app/assets/javascripts and a stylesheet + stub in app/assets/stylesheets. + + If CoffeeScript is available, JavaScripts will be generated with the .coffee extension. + If Sass 3 is available, stylesheets will be generated with the .scss extension. + +Example: + `rails generate assets posts` + + Posts assets. + JavaScript: app/assets/javascripts/posts.js + Stylesheet: app/assets/stylesheets/posts.css + diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb new file mode 100644 index 0000000000..6f4b86e708 --- /dev/null +++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb @@ -0,0 +1,25 @@ +module Rails + module Generators + class AssetsGenerator < NamedBase # :nodoc: + class_option :javascripts, type: :boolean, desc: "Generate JavaScripts" + class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets" + + class_option :javascript_engine, desc: "Engine for JavaScripts" + class_option :stylesheet_engine, desc: "Engine for Stylesheets" + + protected + + def asset_name + file_name + end + + hook_for :javascript_engine do |javascript_engine| + invoke javascript_engine, [name] if options[:javascripts] + end + + hook_for :stylesheet_engine do |stylesheet_engine| + invoke stylesheet_engine, [name] if options[:stylesheets] + end + end + end +end diff --git a/railties/lib/rails/generators/rails/assets/templates/javascript.js b/railties/lib/rails/generators/rails/assets/templates/javascript.js new file mode 100644 index 0000000000..dee720facd --- /dev/null +++ b/railties/lib/rails/generators/rails/assets/templates/javascript.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css new file mode 100644 index 0000000000..7594abf268 --- /dev/null +++ b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/railties/lib/rails/generators/rails/controller/USAGE b/railties/lib/rails/generators/rails/controller/USAGE new file mode 100644 index 0000000000..64239ad599 --- /dev/null +++ b/railties/lib/rails/generators/rails/controller/USAGE @@ -0,0 +1,18 @@ +Description: + Stubs out a new controller and its views. Pass the controller name, either + CamelCased or under_scored, and a list of views as arguments. + + To create a controller within a module, specify the controller name as a + path like 'parent_module/controller_name'. + + This generates a controller class in app/controllers and invokes helper, + template engine, assets, and test framework generators. + +Example: + `rails generate controller CreditCards open debit credit close` + + CreditCards controller with URLs like /credit_cards/debit. + Controller: app/controllers/credit_cards_controller.rb + Test: test/controllers/credit_cards_controller_test.rb + Views: app/views/credit_cards/debit.html.erb [...] + Helper: app/helpers/credit_cards_helper.rb diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb new file mode 100644 index 0000000000..fbecab1823 --- /dev/null +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb @@ -0,0 +1,58 @@ +module Rails + module Generators + class ControllerGenerator < NamedBase # :nodoc: + argument :actions, type: :array, default: [], banner: "action action" + class_option :skip_routes, type: :boolean, desc: "Dont' add routes to config/routes.rb." + + check_class_collision suffix: "Controller" + + def create_controller_files + template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb") + end + + def add_routes + unless options[:skip_routes] + actions.reverse.each do |action| + route generate_routing_code(action) + end + end + end + + hook_for :template_engine, :test_framework, :helper, :assets + + private + + # This method creates nested route entry for namespaced resources. + # For eg. rails g controller foo/bar/baz index + # Will generate - + # namespace :foo do + # namespace :bar do + # get 'baz/index' + # end + # end + def generate_routing_code(action) + depth = regular_class_path.length + # Create 'namespace' ladder + # namespace :foo do + # namespace :bar do + namespace_ladder = regular_class_path.each_with_index.map do |ns, i| + indent("namespace :#{ns} do\n", i * 2) + end.join + + # Create route + # get 'baz/index' + route = indent(%{get '#{file_name}/#{action}'\n}, depth * 2) + + # Create `end` ladder + # end + # end + end_ladder = (1..depth).reverse_each.map do |i| + indent("end\n", i * 2) + end.join + + # Combine the 3 parts to generate complete route entry + namespace_ladder + route + end_ladder + end + end + end +end diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb new file mode 100644 index 0000000000..633e0b3177 --- /dev/null +++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb @@ -0,0 +1,13 @@ +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +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/generator/USAGE b/railties/lib/rails/generators/rails/generator/USAGE new file mode 100644 index 0000000000..799383050c --- /dev/null +++ b/railties/lib/rails/generators/rails/generator/USAGE @@ -0,0 +1,13 @@ +Description: + Stubs out a new generator at lib/generators. Pass the generator name as an argument, + either CamelCased or snake_cased. + +Example: + `rails generate generator Awesome` + + creates a standard awesome generator: + lib/generators/awesome/ + lib/generators/awesome/awesome_generator.rb + lib/generators/awesome/USAGE + lib/generators/awesome/templates/ + test/lib/generators/awesome_generator_test.rb diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb new file mode 100644 index 0000000000..15d88f06ac --- /dev/null +++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb @@ -0,0 +1,27 @@ +module Rails + module Generators + class GeneratorGenerator < NamedBase # :nodoc: + check_class_collision suffix: "Generator" + + class_option :namespace, type: :boolean, default: true, + desc: "Namespace generator under lib/generators/name" + + def create_generator_files + directory '.', generator_dir + end + + hook_for :test_framework + + protected + + def generator_dir + if options[:namespace] + File.join("lib", "generators", regular_class_path, file_name) + else + File.join("lib", "generators", regular_class_path) + end + end + + end + end +end diff --git a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt new file mode 100644 index 0000000000..d0575772bc --- /dev/null +++ b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt @@ -0,0 +1,3 @@ +class <%= class_name %>Generator < Rails::Generators::NamedBase + source_root File.expand_path('../templates', __FILE__) +end diff --git a/railties/lib/rails/generators/rails/generator/templates/USAGE.tt b/railties/lib/rails/generators/rails/generator/templates/USAGE.tt new file mode 100644 index 0000000000..1bb8df840d --- /dev/null +++ b/railties/lib/rails/generators/rails/generator/templates/USAGE.tt @@ -0,0 +1,8 @@ +Description: + Explain the generator + +Example: + rails generate <%= file_name %> Thing + + This will create: + what/will/it/create diff --git a/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory b/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory diff --git a/railties/lib/rails/generators/rails/helper/USAGE b/railties/lib/rails/generators/rails/helper/USAGE new file mode 100644 index 0000000000..8855ef3b01 --- /dev/null +++ b/railties/lib/rails/generators/rails/helper/USAGE @@ -0,0 +1,13 @@ +Description: + Stubs out a new helper. Pass the helper name, either CamelCased + or under_scored. + + To create a helper within a module, specify the helper name as a + path like 'parent_module/helper_name'. + +Example: + `rails generate helper CreditCard` + + Credit card helper. + Helper: app/helpers/credit_card_helper.rb + diff --git a/railties/lib/rails/generators/rails/helper/helper_generator.rb b/railties/lib/rails/generators/rails/helper/helper_generator.rb new file mode 100644 index 0000000000..5ff38e4111 --- /dev/null +++ b/railties/lib/rails/generators/rails/helper/helper_generator.rb @@ -0,0 +1,13 @@ +module Rails + module Generators + class HelperGenerator < NamedBase # :nodoc: + check_class_collision suffix: "Helper" + + def create_helper_files + template 'helper.rb', File.join('app/helpers', class_path, "#{file_name}_helper.rb") + end + + hook_for :test_framework + end + end +end diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb b/railties/lib/rails/generators/rails/helper/templates/helper.rb new file mode 100644 index 0000000000..b4173151b4 --- /dev/null +++ b/railties/lib/rails/generators/rails/helper/templates/helper.rb @@ -0,0 +1,4 @@ +<% module_namespacing do -%> +module <%= class_name %>Helper +end +<% end -%> diff --git a/railties/lib/rails/generators/rails/integration_test/USAGE b/railties/lib/rails/generators/rails/integration_test/USAGE new file mode 100644 index 0000000000..57ee3543e6 --- /dev/null +++ b/railties/lib/rails/generators/rails/integration_test/USAGE @@ -0,0 +1,10 @@ +Description: + Stubs out a new integration test. Pass the name of the test, either + CamelCased or under_scored, as an argument. + + This generator invokes the current integration tool, which defaults to + TestUnit. + +Example: + `rails generate integration_test GeneralStories` creates a GeneralStories + integration test in test/integration/general_stories_test.rb diff --git a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb new file mode 100644 index 0000000000..70770ddcb8 --- /dev/null +++ b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb @@ -0,0 +1,7 @@ +module Rails + module Generators + class IntegrationTestGenerator < NamedBase # :nodoc: + hook_for :integration_tool, as: :integration + end + end +end diff --git a/railties/lib/rails/generators/rails/migration/USAGE b/railties/lib/rails/generators/rails/migration/USAGE new file mode 100644 index 0000000000..baf3d9894f --- /dev/null +++ b/railties/lib/rails/generators/rails/migration/USAGE @@ -0,0 +1,35 @@ +Description: + Stubs out a new database migration. Pass the migration name, either + CamelCased or under_scored, and an optional list of attribute pairs as arguments. + + A migration class is generated in db/migrate prefixed by a timestamp of the current date and time. + + You can name your migration in either of these formats to generate add/remove + column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable + +Example: + `rails generate migration AddSslFlag` + + If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration + db/migrate/20080514090912_add_ssl_flag.rb + + `rails generate migration AddTitleBodyToPost title:string body:text published:boolean` + + This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with this in the Change migration: + + add_column :posts, :title, :string + add_column :posts, :body, :text + add_column :posts, :published, :boolean + +Migration names containing JoinTable will generate join tables for use with +has_and_belongs_to_many associations. + +Example: + `rails g migration CreateMediaJoinTable artists musics:uniq` + + will create the migration + + create_join_table :artists, :musics do |t| + # t.index [:artist_id, :music_id] + t.index [:music_id, :artist_id], unique: true + end diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb new file mode 100644 index 0000000000..965c42db36 --- /dev/null +++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb @@ -0,0 +1,8 @@ +module Rails + module Generators + class MigrationGenerator < NamedBase # :nodoc: + argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]" + hook_for :orm, required: true + end + end +end diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE new file mode 100644 index 0000000000..2a6b8700e3 --- /dev/null +++ b/railties/lib/rails/generators/rails/model/USAGE @@ -0,0 +1,110 @@ +Description: + Stubs out a new model. Pass the model name, either CamelCased or + under_scored, and an optional list of attribute pairs as arguments. + + 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'. + + As a special case, specifying 'password:digest' will generate a + password_digest field of string type, and configure your generated model and + tests for use with ActiveModel has_secure_password (assuming the default ORM + and test framework are being used). + + 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 model immediately. + + This generator invokes your configured ORM and test framework, which + defaults to ActiveRecord and TestUnit. + + Finally, if --parent option is given, it's used as superclass of the + created model. This allows you create Single Table Inheritance models. + + If you pass a namespaced model name (e.g. admin/account or Admin::Account) + then the generator will create a module with a table_name_prefix method + to prefix the model's table name with the module name (e.g. admin_account) + +Available field types: + + Just after the field name you can specify a type like text or boolean. + It will generate the column with the associated SQL type. For instance: + + `rails generate model post title:string body:text` + + will generate a title column with a varchar type and a body column with a text + type. If no type is specified the string type will be used by default. + You can use the following types: + + integer + primary_key + decimal + float + boolean + binary + string + text + date + time + datetime + timestamp + + You can also consider `references` as a kind of type. For instance, if you run: + + `rails generate model photo title:string album:references` + + It will generate an `album_id` column. You should generate these kinds of fields when + you will use a `belongs_to` association, for instance. `references` also supports + polymorphism, you can enable polymorphism like this: + + `rails generate model product supplier:references{polymorphic}` + + For integer, string, text and binary fields, an integer in curly braces will + be set as the limit: + + `rails generate model user pseudo:string{30}` + + For decimal, two integers separated by a comma in curly braces will be used + for precision and scale: + + `rails generate model product 'price:decimal{10,2}'` + + You can add a `:uniq` or `:index` suffix for unique or standard indexes + respectively: + + `rails generate model user pseudo:string:uniq` + `rails generate model user pseudo:string:index` + + You can combine any single curly brace option with the index options: + + `rails generate model user username:string{30}:uniq` + `rails generate model product supplier:references{polymorphic}:index` + + If you require a `password_digest` string column for use with + has_secure_password, you should specify `password:digest`: + + `rails generate model user password:digest` + +Examples: + `rails generate model account` + + For ActiveRecord and TestUnit it creates: + + Model: app/models/account.rb + Test: test/models/account_test.rb + Fixtures: test/fixtures/accounts.yml + Migration: db/migrate/XXX_create_accounts.rb + + `rails generate model post title:string body:text published:boolean` + + Creates a Post model with a string title, text body, and published flag. + + `rails generate model admin/account` + + For ActiveRecord and TestUnit it creates: + + Module: app/models/admin.rb + Model: app/models/admin/account.rb + Test: test/models/admin/account_test.rb + Fixtures: test/fixtures/admin/accounts.yml + Migration: db/migrate/XXX_create_admin_accounts.rb + diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb new file mode 100644 index 0000000000..87bab129bb --- /dev/null +++ b/railties/lib/rails/generators/rails/model/model_generator.rb @@ -0,0 +1,12 @@ +require 'rails/generators/model_helpers' + +module Rails + module Generators + class ModelGenerator < NamedBase # :nodoc: + include Rails::Generators::ModelHelpers + + argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]" + hook_for :orm, required: true + end + end +end diff --git a/railties/lib/rails/generators/rails/plugin/USAGE b/railties/lib/rails/generators/rails/plugin/USAGE new file mode 100644 index 0000000000..9a7bf9f396 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/USAGE @@ -0,0 +1,10 @@ +Description: + The 'rails plugin new' command creates a skeleton for developing any + kind of Rails extension with ability to run tests using dummy Rails + application. + +Example: + rails plugin new ~/Code/Ruby/blog + + This generates a skeletal Rails plugin in ~/Code/Ruby/blog. + See the README in the newly created plugin to get going. diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb new file mode 100644 index 0000000000..584f776c01 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -0,0 +1,393 @@ +require 'active_support/core_ext/hash/slice' +require "rails/generators/rails/app/app_generator" +require 'date' + +module Rails + # The plugin builder allows you to override elements of the plugin + # generator without being forced to reverse the operations of the default + # generator. + # + # This allows you to override entire operations, like the creation of the + # Gemfile, README, or JavaScript files, without needing to know exactly + # what those operations do so you can create another template action. + class PluginBuilder + def rakefile + template "Rakefile" + end + + def app + if mountable? + directory 'app' + empty_directory_with_keep_file "app/assets/images/#{name}" + elsif full? + empty_directory_with_keep_file 'app/models' + empty_directory_with_keep_file 'app/controllers' + empty_directory_with_keep_file 'app/views' + empty_directory_with_keep_file 'app/helpers' + empty_directory_with_keep_file 'app/mailers' + empty_directory_with_keep_file "app/assets/images/#{name}" + end + end + + def readme + template "README.rdoc" + end + + def gemfile + template "Gemfile" + end + + def license + template "MIT-LICENSE" + end + + def gemspec + template "%name%.gemspec" + end + + def gitignore + template "gitignore", ".gitignore" + end + + def lib + template "lib/%name%.rb" + template "lib/tasks/%name%_tasks.rake" + template "lib/%name%/version.rb" + template "lib/%name%/engine.rb" if engine? + end + + def config + template "config/routes.rb" if engine? + end + + def test + template "test/test_helper.rb" + template "test/%name%_test.rb" + append_file "Rakefile", <<-EOF +#{rakefile_test_tasks} + +task default: :test + EOF + if engine? + template "test/integration/navigation_test.rb" + end + end + + PASSTHROUGH_OPTIONS = [ + :skip_active_record, :skip_javascript, :database, :javascript, :quiet, :pretend, :force, :skip + ] + + def generate_test_dummy(force = false) + opts = (options || {}).slice(*PASSTHROUGH_OPTIONS) + opts[:force] = force + opts[:skip_bundle] = true + + invoke Rails::Generators::AppGenerator, + [ File.expand_path(dummy_path, destination_root) ], opts + end + + def test_dummy_config + template "rails/boot.rb", "#{dummy_path}/config/boot.rb", force: true + template "rails/application.rb", "#{dummy_path}/config/application.rb", force: true + if mountable? + template "rails/routes.rb", "#{dummy_path}/config/routes.rb", force: true + end + end + + def test_dummy_assets + template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true + template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true + end + + def test_dummy_clean + inside dummy_path do + remove_file ".gitignore" + remove_file "db/seeds.rb" + remove_file "doc" + remove_file "Gemfile" + remove_file "lib/tasks" + remove_file "public/robots.txt" + remove_file "README" + remove_file "test" + remove_file "vendor" + end + end + + def stylesheets + if mountable? + copy_file "rails/stylesheets.css", + "app/assets/stylesheets/#{name}/application.css" + elsif full? + empty_directory_with_keep_file "app/assets/stylesheets/#{name}" + end + end + + def javascripts + return if options.skip_javascript? + + if mountable? + template "rails/javascripts.js", + "app/assets/javascripts/#{name}/application.js" + elsif full? + empty_directory_with_keep_file "app/assets/javascripts/#{name}" + end + end + + def bin(force = false) + return unless engine? + + directory "bin", force: force do |content| + "#{shebang}\n" + content + end + chmod "bin", 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 + class PluginGenerator < AppBase # :nodoc: + add_shared_options_for "plugin" + + alias_method :plugin_path, :app_path + + class_option :dummy_path, type: :string, default: "test/dummy", + desc: "Create dummy application at given path" + + class_option :full, type: :boolean, default: false, + desc: "Generate a rails engine with bundled Rails application for testing" + + class_option :mountable, type: :boolean, default: false, + desc: "Generate mountable isolated application" + + 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) + @dummy_path = nil + super + + unless plugin_path + raise Error, "Plugin name should be provided in arguments. For details run: rails plugin new --help" + end + end + + public_task :set_default_accessors! + public_task :create_root + + def create_root_files + build(:readme) + build(:rakefile) + build(:gemspec) unless options[:skip_gemspec] + build(:license) + build(:gitignore) unless options[:skip_git] + build(:gemfile) unless options[:skip_gemfile] + end + + def create_app_files + build(:app) + end + + def create_config_files + build(:config) + end + + def create_lib_files + build(:lib) + end + + def create_public_stylesheets_files + build(:stylesheets) + end + + def create_javascript_files + build(:javascripts) + end + + def create_images_directory + build(:images) + end + + def create_bin_files + build(:bin) + end + + def create_test_files + build(:test) unless options[:skip_test_unit] + end + + def create_test_dummy_files + return unless with_dummy_app? + create_dummy_app + end + + def update_gemfile + build(:gemfile_entry) unless options[:skip_gemfile_entry] + end + + def finish_template + build(:leftovers) + end + + public_task :apply_rails_template, :run_bundle + + def name + @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 + + protected + + def app_templates_dir + "../../app/templates" + end + + def create_dummy_app(path = nil) + dummy_path(path) if path + + say_status :vendor_app, dummy_path + mute do + build(:generate_test_dummy) + store_application_definition! + build(:test_dummy_config) + build(:test_dummy_assets) + build(:test_dummy_clean) + # ensure that bin/rails has proper dummy_path + build(:bin, true) + end + end + + def engine? + full? || mountable? + end + + def full? + options[:full] + end + + def mountable? + options[:mountable] + end + + def skip_git? + options[:skip_git] + end + + def with_dummy_app? + options[:skip_test_unit].blank? || options[:dummy_path] != 'test/dummy' + end + + def self.banner + "rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]" + end + + def original_name + @original_name ||= File.basename(destination_root) + end + + def camelized + @camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize + end + + def author + default = "TODO: Write your name" + if skip_git? + @author = default + else + @author = `git config user.name`.chomp rescue default + end + end + + def email + default = "TODO: Write your email address" + if skip_git? + @email = default + else + @email = `git config user.email`.chomp rescue default + end + end + + def valid_const? + 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 #{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 #{original_name}, constant #{camelized} is already in use. Please choose another plugin name." + end + end + + def application_definition + @application_definition ||= begin + + dummy_application_path = File.expand_path("#{dummy_path}/config/application.rb", destination_root) + unless options[:pretend] || !File.exist?(dummy_application_path) + contents = File.read(dummy_application_path) + contents[(contents.index(/module ([\w]+)\n(.*)class Application/m))..-1] + end + end + end + alias :store_application_definition! :application_definition + + def get_builder_class + defined?(::PluginBuilder) ? ::PluginBuilder : Rails::PluginBuilder + end + + def rakefile_test_tasks + <<-RUBY +require 'rake/testtask' + +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = false +end + RUBY + end + + def dummy_path(path = nil) + @dummy_path = path if path + @dummy_path || options[:dummy_path] + 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/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec new file mode 100644 index 0000000000..919c349470 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/%name%.gemspec @@ -0,0 +1,27 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require "<%= name %>/version" + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "<%= name %>" + s.version = <%= camelized %>::VERSION + s.authors = ["<%= author %>"] + s.email = ["<%= email %>"] + s.homepage = "TODO" + s.summary = "TODO: Summary of <%= camelized %>." + s.description = "TODO: Description of <%= camelized %>." + s.license = "MIT" + + s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"] +<% unless options.skip_test_unit? -%> + s.test_files = Dir["test/**/*"] +<% end -%> + + <%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "~> <%= Rails::VERSION::STRING %>" +<% unless options[:skip_active_record] -%> + + s.add_development_dependency "<%= gem_for_database %>" +<% end -%> +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile new file mode 100644 index 0000000000..35ad9fbf9e --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile @@ -0,0 +1,47 @@ +source 'https://rubygems.org' + +<% if options[:skip_gemspec] -%> +<%= '# ' if options.dev? || options.edge? -%>gem 'rails', '~> <%= Rails::VERSION::STRING %>' +<% else -%> +# Declare your gem's dependencies in <%= name %>.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec +<% end -%> + +<% if options[:skip_gemspec] -%> +group :development do + gem '<%= gem_for_database %>' +end +<% else -%> +# Declare any dependencies that are still in development here instead of in +# your gemspec. These might include edge Rails or gems from your path or +# Git. Remember to move these dependencies to your gemspec before releasing +# your gem to rubygems.org. +<% end -%> + +<% if options.dev? || options.edge? -%> +# Your gem is dependent on dev or edge Rails. Once you can lock this +# dependency down to a specific version, move it to your gemspec. +<% max_width = gemfile_entries.map { |g| g.name.length }.max -%> +<% gemfile_entries.each do |gem| -%> +<% if gem.comment -%> + +# <%= gem.comment %> +<% end -%> +<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> +<% if gem.options.any? -%> +, <%= gem.options.map { |k,v| + "#{k}: #{v.inspect}" }.join(', ') %> +<% end -%> +<% end -%> + +<% end -%> +<% unless defined?(JRUBY_VERSION) -%> +# To use a debugger + <%- if RUBY_VERSION < '2.0.0' -%> +# gem 'debugger', group: [:development, :test] + <%- else -%> +# gem 'byebug', group: [:development, :test] + <%- end -%> +<% end -%> diff --git a/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE new file mode 100644 index 0000000000..ff2fb3ba4e --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright <%= Date.today.year %> <%= author %> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/railties/lib/rails/generators/rails/plugin/templates/README.rdoc b/railties/lib/rails/generators/rails/plugin/templates/README.rdoc new file mode 100644 index 0000000000..301d647731 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/README.rdoc @@ -0,0 +1,3 @@ += <%= camelized %> + +This project rocks and uses MIT-LICENSE.
\ No newline at end of file diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile new file mode 100644 index 0000000000..c338a0bdb1 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile @@ -0,0 +1,29 @@ +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end + +require 'rdoc/task' + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = '<%= camelized %>' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.rdoc') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +<% if engine? && !options[:skip_active_record] && with_dummy_app? -%> +APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__) +load 'rails/tasks/engine.rake' +<% end %> + +<% if engine? -%> +load 'rails/tasks/statistics.rake' +<% end %> + +<% unless options[:skip_gemspec] -%> + +Bundler::GemHelper.install_tasks +<% end %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%name%/application_controller.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%name%/application_controller.rb.tt new file mode 100644 index 0000000000..448ad7f989 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/app/controllers/%name%/application_controller.rb.tt @@ -0,0 +1,4 @@ +module <%= camelized %> + class ApplicationController < ActionController::Base + end +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%name%/application_helper.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%name%/application_helper.rb.tt new file mode 100644 index 0000000000..40ae9f52c2 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/app/helpers/%name%/application_helper.rb.tt @@ -0,0 +1,4 @@ +module <%= camelized %> + module ApplicationHelper + end +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/app/mailers/.empty_directory diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory b/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/app/models/.empty_directory diff --git a/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%name%/application.html.erb.tt new file mode 100644 index 0000000000..1d380420b4 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/app/views/layouts/%name%/application.html.erb.tt @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<head> + <title><%= camelized %></title> + <%%= stylesheet_link_tag "<%= name %>/application", media: "all" %> + <%%= javascript_include_tag "<%= name %>/application" %> + <%%= csrf_meta_tags %> +</head> +<body> + +<%%= yield %> + +</body> +</html> diff --git a/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt new file mode 100644 index 0000000000..c3314d7e68 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt @@ -0,0 +1,11 @@ +# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/<%= name -%>/engine', __FILE__) + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb new file mode 100644 index 0000000000..8e158d5831 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb @@ -0,0 +1,6 @@ +<% if mountable? -%> +<%= camelized %>::Engine.routes.draw do +<% else -%> +Rails.application.routes.draw do +<% end -%> +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore new file mode 100644 index 0000000000..086d87818a --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore @@ -0,0 +1,10 @@ +.bundle/ +log/*.log +pkg/ +<% unless options[:skip_test_unit] && options[:dummy_path] == 'test/dummy' -%> +<%= dummy_path %>/db/*.sqlite3 +<%= dummy_path %>/db/*.sqlite3-journal +<%= dummy_path %>/log/*.log +<%= dummy_path %>/tmp/ +<%= dummy_path %>/.sass-cache +<% end -%>
\ No newline at end of file diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%name%.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%.rb new file mode 100644 index 0000000000..40c074cced --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%.rb @@ -0,0 +1,6 @@ +<% if engine? -%> +require "<%= name %>/engine" + +<% end -%> +module <%= camelized %> +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/engine.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/engine.rb new file mode 100644 index 0000000000..967668fe66 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/engine.rb @@ -0,0 +1,7 @@ +module <%= camelized %> + class Engine < ::Rails::Engine +<% if mountable? -%> + isolate_namespace <%= camelized %> +<% end -%> + end +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/version.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/version.rb new file mode 100644 index 0000000000..ef07ef2e19 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%name%/version.rb @@ -0,0 +1,3 @@ +module <%= camelized %> + VERSION = "0.0.1" +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%name%_tasks.rake b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%name%_tasks.rake new file mode 100644 index 0000000000..7121f5ae23 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%name%_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :<%= name %> do +# # Task goes here +# end diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb new file mode 100644 index 0000000000..5508829f6b --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb @@ -0,0 +1,18 @@ +require File.expand_path('../boot', __FILE__) + +<% if include_all_railties? -%> +require 'rails/all' +<% else -%> +# Pick the frameworks you want: +<%= comment_if :skip_active_record %>require "active_record/railtie" +require "action_controller/railtie" +require "action_mailer/railtie" +<%= comment_if :skip_action_view %>require "action_view/railtie" +<%= comment_if :skip_sprockets %>require "sprockets/railtie" +<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie" +<% end -%> + +Bundler.require(*Rails.groups) +require "<%= name %>" + +<%= application_definition %> diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb new file mode 100644 index 0000000000..6266cfc509 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb @@ -0,0 +1,5 @@ +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js new file mode 100644 index 0000000000..5bc2e1c8b5 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/javascripts.js @@ -0,0 +1,13 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. +// +// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require_tree . diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb new file mode 100644 index 0000000000..730ee31c3d --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb @@ -0,0 +1,4 @@ +Rails.application.routes.draw do + + mount <%= camelized %>::Engine => "/<%= name %>" +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css b/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css new file mode 100644 index 0000000000..a443db3401 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/rails/stylesheets.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any styles + * defined in the other CSS/SCSS files in this directory. It is generally better to create a new + * file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/%name%_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/%name%_test.rb new file mode 100644 index 0000000000..0a8bbd4aaf --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/%name%_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class <%= camelized %>Test < ActiveSupport::TestCase + test "truth" do + assert_kind_of Module, <%= camelized %> + end +end diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb new file mode 100644 index 0000000000..824caecb24 --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class NavigationTest < ActionDispatch::IntegrationTest +<% unless options[:skip_active_record] -%> + fixtures :all +<% end -%> + + # test "the truth" do + # assert true + # end +end + diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb new file mode 100644 index 0000000000..1e26a313cd --- /dev/null +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb @@ -0,0 +1,15 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require File.expand_path("../dummy/config/environment.rb", __FILE__) +require "rails/test_help" + +Rails.backtrace_cleaner.remove_silencers! + +# Load support files +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } + +# Load fixtures from the engine +if ActiveSupport::TestCase.method_defined?(:fixture_path=) + ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) +end diff --git a/railties/lib/rails/generators/rails/resource/USAGE b/railties/lib/rails/generators/rails/resource/USAGE new file mode 100644 index 0000000000..e359cd574f --- /dev/null +++ b/railties/lib/rails/generators/rails/resource/USAGE @@ -0,0 +1,23 @@ +Description: + Stubs out a new resource including an empty model and controller suitable + for a restful, resource-oriented application. Pass the singular model name, + either CamelCased or 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'. + + 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 model immediately. + + This generator invokes your configured ORM and test framework, besides + creating helpers and add routes to config/routes.rb. + + Unlike the scaffold generator, the resource generator does not create + views or add any methods to the generated controller. + +Examples: + `rails generate resource post` # no attributes + `rails generate resource post title:string body:text published:boolean` + `rails generate resource purchase order_id:integer amount:decimal` diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb new file mode 100644 index 0000000000..8014feb75f --- /dev/null +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -0,0 +1,20 @@ +require 'rails/generators/resource_helpers' +require 'rails/generators/rails/model/model_generator' +require 'active_support/core_ext/object/blank' + +module Rails + module Generators + class ResourceGenerator < ModelGenerator # :nodoc: + include ResourceHelpers + + hook_for :resource_controller, required: true do |controller| + invoke controller, [ controller_name, options[:actions] ] + end + + class_option :actions, type: :array, banner: "ACTION ACTION", default: [], + desc: "Actions for the resource controller" + + hook_for :resource_route, required: true + end + end +end diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb new file mode 100644 index 0000000000..e4a2bc2b0f --- /dev/null +++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb @@ -0,0 +1,50 @@ +module Rails + module Generators + class ResourceRouteGenerator < NamedBase # :nodoc: + + # Properly nests namespaces passed into a generator + # + # $ rails generate resource admin/users/products + # + # should give you + # + # namespace :admin do + # namespace :users do + # resources :products + # end + # end + def add_resource_route + return if options[:actions].present? + + # iterates over all namespaces and opens up blocks + regular_class_path.each_with_index do |namespace, index| + write("namespace :#{namespace} do", index + 1) + end + + # inserts the primary resource + write("resources :#{file_name.pluralize}", route_length + 1) + + # ends blocks + regular_class_path.each_index do |index| + write("end", route_length - index) + end + + # route prepends two spaces onto the front of the string that is passed, this corrects that + route route_string[2..-1] + end + + private + def route_string + @route_string ||= "" + end + + def write(str, indent) + route_string << "#{" " * indent}#{str}\n" + end + + def route_length + regular_class_path.length + end + end + end +end diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE new file mode 100644 index 0000000000..1b2a944103 --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold/USAGE @@ -0,0 +1,41 @@ +Description: + Scaffolds an entire resource, from model and migration to controller and + views, along with a full test suite. The resource is ready to use as a + starting point for your RESTful, resource-oriented application. + + Pass the name of the model (in singular form), either CamelCased or + under_scored, as the first argument, and an optional list of attribute + pairs. + + 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. + + As a special case, specifying 'password:digest' will generate a + password_digest field of string type, and configure your generated model, + controller, views, and test suite for use with ActiveModel + has_secure_password (assuming they are using Rails defaults). + + 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 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. + + If you want to remove all the generated files, run + 'rails destroy scaffold ModelName'. + +Examples: + `rails generate scaffold post` + `rails generate scaffold post title body:text published:boolean` + `rails generate scaffold purchase amount:decimal tracking_id:integer:uniq` + `rails generate scaffold user email:uniq password:digest` diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..e89789e72b --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb @@ -0,0 +1,32 @@ +require 'rails/generators/rails/resource/resource_generator' + +module Rails + module Generators + class ScaffoldGenerator < ResourceGenerator # :nodoc: + remove_hook_for :resource_controller + remove_class_option :actions + + class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets" + class_option :stylesheet_engine, desc: "Engine for Stylesheets" + class_option :assets, type: :boolean + class_option :resource_route, type: :boolean + + def handle_skip + @options = @options.merge(stylesheets: false) unless options[:assets] + @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets] + end + + hook_for :scaffold_controller, required: true + + hook_for :assets do |assets| + invoke assets, [controller_name] + end + + hook_for :stylesheet_engine do |stylesheet_engine| + if behavior == :invoke + invoke stylesheet_engine, [controller_name] + end + end + end + end +end diff --git a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css new file mode 100644 index 0000000000..1ae7000299 --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css @@ -0,0 +1,56 @@ +body { background-color: #fff; color: #333; } + +body, p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { color: #000; } +a:visited { color: #666; } +a:hover { color: #fff; background-color:#000; } + +div.field, div.actions { + margin-bottom: 10px; +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; +} + +#error_explanation h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; +} + +#error_explanation ul li { + font-size: 12px; + list-style: square; +} diff --git a/railties/lib/rails/generators/rails/scaffold_controller/USAGE b/railties/lib/rails/generators/rails/scaffold_controller/USAGE new file mode 100644 index 0000000000..8ba4c5ccbc --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold_controller/USAGE @@ -0,0 +1,19 @@ +Description: + Stubs out a scaffolded controller, its seven RESTful actions and related + views. Pass the model name, either CamelCased or under_scored. The + controller name is retrieved as a pluralized version of the model name. + + To create a controller within a module, specify the model name as a + path like 'parent_module/controller_name'. + + This generates a controller class in app/controllers and invokes helper, + template engine and test framework generators. + +Example: + `rails generate scaffold_controller CreditCard` + + Credit card controller with URLs like /credit_card/debit. + Controller: app/controllers/credit_cards_controller.rb + Test: test/controllers/credit_cards_controller_test.rb + Views: app/views/credit_cards/index.html.erb [...] + Helper: app/helpers/credit_cards_helper.rb diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb new file mode 100644 index 0000000000..6bf0a33a5f --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -0,0 +1,27 @@ +require 'rails/generators/resource_helpers' + +module Rails + module Generators + class ScaffoldControllerGenerator < NamedBase # :nodoc: + include ResourceHelpers + + check_class_collision suffix: "Controller" + + class_option :orm, banner: "NAME", type: :string, required: true, + desc: "ORM to generate the controller for" + + argument :attributes, type: :array, default: [], banner: "field:type field:type" + + def create_controller_files + template "controller.rb", File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") + end + + hook_for :template_engine, :test_framework, as: :scaffold + + # Invoke the helper using the controller name (pluralized) + hook_for :helper, as: :scaffold do |invoked| + invoke invoked, [ controller_name ] + end + end + end +end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb new file mode 100644 index 0000000000..2c3b04043f --- /dev/null +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -0,0 +1,68 @@ +<% if namespaced? -%> +require_dependency "<%= namespaced_file_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= controller_class_name %>Controller < ApplicationController + before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] + + # GET <%= route_url %> + def index + @<%= plural_table_name %> = <%= orm_class.all(class_name) %> + end + + # GET <%= route_url %>/1 + def show + end + + # GET <%= route_url %>/new + def new + @<%= singular_table_name %> = <%= orm_class.build(class_name) %> + end + + # GET <%= route_url %>/1/edit + def edit + end + + # POST <%= route_url %> + def create + @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> + + if @<%= orm_instance.save %> + redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> + else + render :new + end + end + + # PATCH/PUT <%= route_url %>/1 + def update + if @<%= orm_instance.update("#{singular_table_name}_params") %> + redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> + else + render :edit + end + end + + # DELETE <%= route_url %>/1 + def destroy + @<%= orm_instance.destroy %> + redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_<%= singular_table_name %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> + end + + # Only allow a trusted parameter "white list" through. + def <%= "#{singular_table_name}_params" %> + <%- if attributes_names.empty? -%> + params[:<%= singular_table_name %>] + <%- else -%> + params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + <%- end -%> + end +end +<% end -%> 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..754824ca0c --- /dev/null +++ b/railties/lib/rails/generators/rails/task/task_generator.rb @@ -0,0 +1,12 @@ +module Rails + module Generators + class TaskGenerator < NamedBase # :nodoc: + 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..1e3ed5f158 --- /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 new file mode 100644 index 0000000000..4669935156 --- /dev/null +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -0,0 +1,82 @@ +require 'rails/generators/active_model' +require 'rails/generators/model_helpers' + +module Rails + module Generators + # Deal with controller names on scaffold and add some helpers to deal with + # ActiveModel. + module ResourceHelpers # :nodoc: + + def self.included(base) #:nodoc: + base.send :include, Rails::Generators::ModelHelpers + base.class_option :model_name, type: :string, desc: "ModelName to be used" + end + + # Set controller variables on initialization. + def initialize(*args) #:nodoc: + super + controller_name = name + if options[:model_name] + self.name = options[:model_name] + assign_names!(self.name) + end + + assign_controller_names!(controller_name.pluralize) + end + + protected + + attr_reader :controller_name, :controller_file_name + + def controller_class_path + if options[:model_name] + @controller_class_path + else + class_path + end + end + + def assign_controller_names!(name) + @controller_name = name + @controller_class_path = name.include?('/') ? name.split('/') : name.split('::') + @controller_class_path.map! { |m| m.underscore } + @controller_file_name = @controller_class_path.pop + end + + def controller_file_path + @controller_file_path ||= (controller_class_path + [controller_file_name]).join('/') + end + + def controller_class_name + (controller_class_path + [controller_file_name]).map!{ |m| m.camelize }.join('::') + end + + def controller_i18n_scope + @controller_i18n_scope ||= controller_file_path.tr('/', '.') + end + + # Loads the ORM::Generators::ActiveModel class. This class is responsible + # to tell scaffold entities how to generate an specific method for the + # ORM. Check Rails::Generators::ActiveModel for more information. + def orm_class + @orm_class ||= begin + # Raise an error if the class_option :orm was not defined. + unless self.class.class_options[:orm] + raise "You need to have :orm as class option to invoke orm_class and orm_instance" + end + + begin + "#{options[:orm].to_s.camelize}::Generators::ActiveModel".constantize + rescue NameError + Rails::Generators::ActiveModel + end + end + end + + # Initialize ORM::Generators::ActiveModel to access instance methods. + def orm_instance(name=singular_table_name) + @orm_instance ||= orm_class.new(name) + end + end + end +end diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb new file mode 100644 index 0000000000..58592b4f8e --- /dev/null +++ b/railties/lib/rails/generators/test_case.rb @@ -0,0 +1,36 @@ +require 'rails/generators' +require 'rails/generators/testing/behaviour' +require 'rails/generators/testing/setup_and_teardown' +require 'rails/generators/testing/assertions' +require 'fileutils' + +module Rails + module Generators + # Disable color in output. Easier to debug. + no_color! + + # This class provides a TestCase for testing generators. To setup, you need + # just to configure the destination and set which generator is being tested: + # + # class AppGeneratorTest < Rails::Generators::TestCase + # tests AppGenerator + # destination File.expand_path("../tmp", File.dirname(__FILE__)) + # end + # + # If you want to ensure your destination root is clean before running each test, + # you can set a setup callback: + # + # class AppGeneratorTest < Rails::Generators::TestCase + # tests AppGenerator + # destination File.expand_path("../tmp", File.dirname(__FILE__)) + # setup :prepare_destination + # end + class TestCase < ActiveSupport::TestCase + include Rails::Generators::Testing::Behaviour + include Rails::Generators::Testing::SetupAndTeardown + include Rails::Generators::Testing::Assertions + include FileUtils + + end + end +end diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb new file mode 100644 index 0000000000..fe45c9e15d --- /dev/null +++ b/railties/lib/rails/generators/test_unit.rb @@ -0,0 +1,8 @@ +require 'rails/generators/named_base' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class Base < Rails::Generators::NamedBase # :nodoc: + end + end +end diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb new file mode 100644 index 0000000000..b5aa581769 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb @@ -0,0 +1,15 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class ControllerGenerator < Base # :nodoc: + argument :actions, type: :array, default: [], banner: "action action" + check_class_collision suffix: "ControllerTest" + + def create_test_files + template 'functional_test.rb', + File.join('test/controllers', class_path, "#{file_name}_controller_test.rb") + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb new file mode 100644 index 0000000000..509bd60564 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb @@ -0,0 +1,19 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>ControllerTest < ActionController::TestCase +<% if actions.empty? -%> + # test "the truth" do + # assert true + # end +<% else -%> +<% actions.each do |action| -%> + test "should get <%= action %>" do + get :<%= action %> + assert_response :success + end + +<% end -%> +<% end -%> +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb new file mode 100644 index 0000000000..d7307398ce --- /dev/null +++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb @@ -0,0 +1,26 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class GeneratorGenerator < Base # :nodoc: + check_class_collision suffix: "GeneratorTest" + + class_option :namespace, type: :boolean, default: true, + desc: "Namespace generator under lib/generators/name" + + def create_generator_files + template 'generator_test.rb', File.join('test/lib/generators', class_path, "#{file_name}_generator_test.rb") + end + + protected + + def generator_path + if options[:namespace] + File.join("generators", regular_class_path, file_name, "#{file_name}_generator") + else + File.join("generators", regular_class_path, "#{file_name}_generator") + end + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb new file mode 100644 index 0000000000..a7f1fc4fba --- /dev/null +++ b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb @@ -0,0 +1,16 @@ +require 'test_helper' +require '<%= generator_path %>' + +<% module_namespacing do -%> +class <%= class_name %>GeneratorTest < Rails::Generators::TestCase + tests <%= class_name %>Generator + destination Rails.root.join('tmp/generators') + setup :prepare_destination + + # test "generator runs without errors" do + # assert_nothing_raised do + # run_generator ["arguments"] + # end + # end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb new file mode 100644 index 0000000000..bde4e88915 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb @@ -0,0 +1,9 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class HelperGenerator < Base # :nodoc: + # Rails does not generate anything here. + end + end +end diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb new file mode 100644 index 0000000000..e004835bd5 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb @@ -0,0 +1,13 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class IntegrationGenerator < Base # :nodoc: + check_class_collision suffix: "Test" + + def create_test_files + template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb") + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb new file mode 100644 index 0000000000..dea7e22196 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class <%= class_name %>Test < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb new file mode 100644 index 0000000000..85dee1a066 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb @@ -0,0 +1,21 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class MailerGenerator < Base # :nodoc: + argument :actions, type: :array, default: [], banner: "method method" + + def check_class_collision + class_collisions "#{class_name}Test", "#{class_name}Preview" + end + + def create_test_files + template "functional_test.rb", File.join('test/mailers', class_path, "#{file_name}_test.rb") + end + + def create_preview_files + template "preview.rb", File.join('test/mailers/previews', class_path, "#{file_name}_preview.rb") + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb new file mode 100644 index 0000000000..7e204105a3 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb @@ -0,0 +1,21 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>Test < ActionMailer::TestCase +<% actions.each do |action| -%> + test "<%= action %>" do + mail = <%= class_name %>.<%= action %> + assert_equal <%= action.to_s.humanize.inspect %>, mail.subject + assert_equal ["to@example.org"], mail.to + assert_equal ["from@example.com"], mail.from + assert_match "Hi", mail.body.encoded + end + +<% end -%> +<% if actions.blank? -%> + # test "the truth" do + # assert true + # end +<% end -%> +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb new file mode 100644 index 0000000000..3bfd5426e8 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb @@ -0,0 +1,13 @@ +<% module_namespacing do -%> +# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %> +class <%= class_name %>Preview < ActionMailer::Preview +<% actions.each do |action| -%> + + # Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>/<%= action %> + def <%= action %> + <%= class_name %>.<%= action %> + end +<% end -%> + +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb new file mode 100644 index 0000000000..2826a3ffa1 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb @@ -0,0 +1,36 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class ModelGenerator < Base # :nodoc: + + RESERVED_YAML_KEYWORDS = %w(y yes n no true false on off null) + + argument :attributes, type: :array, default: [], banner: "field:type field:type" + class_option :fixture, type: :boolean + + check_class_collision suffix: "Test" + + def create_test_file + template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_test.rb") + end + + hook_for :fixture_replacement + + def create_fixture_file + if options[:fixture] && options[:fixture_replacement].nil? + template 'fixtures.yml', File.join('test/fixtures', class_path, "#{plural_file_name}.yml") + end + end + + private + def yaml_key_value(key, value) + if RESERVED_YAML_KEYWORDS.include?(key.downcase) + "'#{key}': #{value}" + else + "#{key}: #{value}" + end + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml new file mode 100644 index 0000000000..f19e9d1d87 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml @@ -0,0 +1,27 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html +<% unless attributes.empty? -%> +<% %w(one two).each do |name| %> +<%= name %>: +<% attributes.each do |attribute| -%> + <%- if attribute.password_digest? -%> + password_digest: <%%= BCrypt::Password.create('secret') %> + <%- else -%> + <%= yaml_key_value(attribute.column_name, attribute.default) %> + <%- end -%> + <%- if attribute.polymorphic? -%> + <%= yaml_key_value("#{attribute.name}_type", attribute.human_name) %> + <%- end -%> +<% end -%> +<% end -%> +<% else -%> + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb new file mode 100644 index 0000000000..c9bc7d5b90 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>Test < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb new file mode 100644 index 0000000000..b5d4f38444 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb @@ -0,0 +1,13 @@ +require 'rails/generators/test_unit' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class PluginGenerator < Base # :nodoc: + check_class_collision suffix: "Test" + + def create_test_files + directory '.', 'test' + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt b/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt new file mode 100644 index 0000000000..0cbae1120e --- /dev/null +++ b/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt @@ -0,0 +1,7 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end 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 new file mode 100644 index 0000000000..30a861f09d --- /dev/null +++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb @@ -0,0 +1,2 @@ +require 'active_support/testing/autorun' +require 'active_support' diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..2e1f55f2a6 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -0,0 +1,33 @@ +require 'rails/generators/test_unit' +require 'rails/generators/resource_helpers' + +module TestUnit # :nodoc: + module Generators # :nodoc: + class ScaffoldGenerator < Base # :nodoc: + include Rails::Generators::ResourceHelpers + + check_class_collision suffix: "ControllerTest" + + argument :attributes, type: :array, default: [], banner: "field:type field:type" + + def create_test_files + template "functional_test.rb", + File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb") + end + + private + + def attributes_hash + return if attributes_names.empty? + + attributes_names.map do |name| + if %w(password password_confirmation).include?(name) && attributes.any?(&:password_digest?) + "#{name}: 'secret'" + else + "#{name}: @#{singular_table_name}.#{name}" + end + end.sort.join(', ') + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb new file mode 100644 index 0000000000..18bd1ece9d --- /dev/null +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb @@ -0,0 +1,51 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= controller_class_name %>ControllerTest < ActionController::TestCase + setup do + @<%= singular_table_name %> = <%= table_name %>(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:<%= table_name %>) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create <%= singular_table_name %>" do + assert_difference('<%= class_name %>.count') do + post :create, <%= "#{singular_table_name}: { #{attributes_hash} }" %> + end + + assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>)) + end + + test "should show <%= singular_table_name %>" do + get :show, id: <%= "@#{singular_table_name}" %> + assert_response :success + end + + test "should get edit" do + get :edit, id: <%= "@#{singular_table_name}" %> + assert_response :success + end + + test "should update <%= singular_table_name %>" do + patch :update, id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> + assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>)) + end + + test "should destroy <%= singular_table_name %>" do + assert_difference('<%= class_name %>.count', -1) do + delete :destroy, id: <%= "@#{singular_table_name}" %> + end + + assert_redirected_to <%= index_helper %>_path + end +end +<% end -%> diff --git a/railties/lib/rails/generators/testing/assertions.rb b/railties/lib/rails/generators/testing/assertions.rb new file mode 100644 index 0000000000..bd069e4bd0 --- /dev/null +++ b/railties/lib/rails/generators/testing/assertions.rb @@ -0,0 +1,123 @@ +require 'shellwords' + +module Rails + module Generators + module Testing + module Assertions + # Asserts a given file exists. You need to supply an absolute path or a path relative + # to the configured destination: + # + # assert_file "config/environment.rb" + # + # You can also give extra arguments. If the argument is a regexp, it will check if the + # regular expression matches the given file content. If it's a string, it compares the + # file with the given string: + # + # assert_file "config/environment.rb", /initialize/ + # + # Finally, when a block is given, it yields the file content: + # + # assert_file "app/controllers/products_controller.rb" do |controller| + # assert_instance_method :index, controller do |index| + # assert_match(/Product\.all/, index) + # end + # end + def assert_file(relative, *contents) + absolute = File.expand_path(relative, destination_root).shellescape + assert File.exist?(absolute), "Expected file #{relative.inspect} to exist, but does not" + + read = File.read(absolute) if block_given? || !contents.empty? + yield read if block_given? + + contents.each do |content| + case content + when String + assert_equal content, read + when Regexp + assert_match content, read + end + end + end + alias :assert_directory :assert_file + + # Asserts a given file does not exist. You need to supply an absolute path or a + # path relative to the configured destination: + # + # assert_no_file "config/random.rb" + def assert_no_file(relative) + absolute = File.expand_path(relative, destination_root) + assert !File.exist?(absolute), "Expected file #{relative.inspect} to not exist, but does" + end + alias :assert_no_directory :assert_no_file + + # Asserts a given migration exists. You need to supply an absolute path or a + # path relative to the configured destination: + # + # assert_migration "db/migrate/create_products.rb" + # + # This method manipulates the given path and tries to find any migration which + # matches the migration name. For example, the call above is converted to: + # + # assert_file "db/migrate/003_create_products.rb" + # + # Consequently, assert_migration accepts the same arguments has assert_file. + def assert_migration(relative, *contents, &block) + file_name = migration_file_name(relative) + assert file_name, "Expected migration #{relative} to exist, but was not found" + assert_file file_name, *contents, &block + end + + # Asserts a given migration does not exist. You need to supply an absolute path or a + # path relative to the configured destination: + # + # assert_no_migration "db/migrate/create_products.rb" + def assert_no_migration(relative) + file_name = migration_file_name(relative) + assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}" + end + + # Asserts the given class method exists in the given content. This method does not detect + # class methods inside (class << self), only class methods which starts with "self.". + # When a block is given, it yields the content of the method. + # + # assert_migration "db/migrate/create_products.rb" do |migration| + # assert_class_method :up, migration do |up| + # assert_match(/create_table/, up) + # end + # end + def assert_class_method(method, content, &block) + assert_instance_method "self.#{method}", content, &block + end + + # Asserts the given method exists in the given content. When a block is given, + # it yields the content of the method. + # + # assert_file "app/controllers/products_controller.rb" do |controller| + # assert_instance_method :index, controller do |index| + # assert_match(/Product\.all/, index) + # end + # end + def assert_instance_method(method, content) + assert content =~ /(\s+)def #{method}(\(.+\))?(.*?)\n\1end/m, "Expected to have method #{method}" + yield $3.strip if block_given? + end + alias :assert_method :assert_instance_method + + # Asserts the given attribute type gets translated to a field type + # properly: + # + # assert_field_type :date, :date_select + def assert_field_type(attribute_type, field_type) + assert_equal(field_type, create_generated_attribute(attribute_type).field_type) + end + + # Asserts the given attribute type gets a proper default value: + # + # assert_field_default_value :string, "MyString" + def assert_field_default_value(attribute_type, value) + assert_equal(value, create_generated_attribute(attribute_type).default) + end + end + end + end +end diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb new file mode 100644 index 0000000000..e0600d0b59 --- /dev/null +++ b/railties/lib/rails/generators/testing/behaviour.rb @@ -0,0 +1,123 @@ +require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/hash/reverse_merge' +require 'active_support/core_ext/kernel/reporting' +require 'active_support/concern' +require 'rails/generators' + +module Rails + module Generators + module Testing + module Behaviour + extend ActiveSupport::Concern + + included do + class_attribute :destination_root, :current_path, :generator_class, :default_arguments + + # Generators frequently change the current path using +FileUtils.cd+. + # So we need to store the path at file load and revert back to it after each test. + self.current_path = File.expand_path(Dir.pwd) + self.default_arguments = [] + end + + module ClassMethods + # Sets which generator should be tested: + # + # tests AppGenerator + def tests(klass) + self.generator_class = klass + end + + # Sets default arguments on generator invocation. This can be overwritten when + # invoking it. + # + # arguments %w(app_name --skip-active-record) + def arguments(array) + self.default_arguments = array + end + + # Sets the destination of generator files: + # + # destination File.expand_path("../tmp", File.dirname(__FILE__)) + def destination(path) + self.destination_root = path + end + end + + # Runs the generator configured for this class. The first argument is an array like + # command line arguments: + # + # class AppGeneratorTest < Rails::Generators::TestCase + # tests AppGenerator + # destination File.expand_path("../tmp", File.dirname(__FILE__)) + # teardown :cleanup_destination_root + # + # test "database.yml is not created when skipping Active Record" do + # run_generator %w(myapp --skip-active-record) + # assert_no_file "config/database.yml" + # end + # end + # + # You can provide a configuration hash as second argument. This method returns the output + # printed by the generator. + def run_generator(args=self.default_arguments, config={}) + capture(:stdout) do + args += ['--skip-bundle'] unless args.include? '--dev' + self.generator_class.start(args, config.reverse_merge(destination_root: destination_root)) + end + end + + # Instantiate the generator. + def generator(args=self.default_arguments, options={}, config={}) + @generator ||= self.generator_class.new(args, options, config.reverse_merge(destination_root: destination_root)) + end + + # Create a Rails::Generators::GeneratedAttribute by supplying the + # attribute type and, optionally, the attribute name: + # + # create_generated_attribute(:string, 'name') + def create_generated_attribute(attribute_type, name = 'test', index = nil) + Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(':')) + end + + protected + + def destination_root_is_set? # :nodoc: + raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root + end + + def ensure_current_path # :nodoc: + cd current_path + end + + def prepare_destination # :nodoc: + rm_rf(destination_root) + mkdir_p(destination_root) + end + + def migration_file_name(relative) # :nodoc: + absolute = File.expand_path(relative, destination_root) + dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '') + Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first + end + + def capture(stream) + stream = stream.to_s + captured_stream = Tempfile.new(stream) + stream_io = eval("$#{stream}") + origin_stream = stream_io.dup + stream_io.reopen(captured_stream) + + yield + + stream_io.rewind + return captured_stream.read + ensure + captured_stream.close + captured_stream.unlink + stream_io.reopen(origin_stream) + end + end + end + end +end diff --git a/railties/lib/rails/generators/testing/setup_and_teardown.rb b/railties/lib/rails/generators/testing/setup_and_teardown.rb new file mode 100644 index 0000000000..73102a283f --- /dev/null +++ b/railties/lib/rails/generators/testing/setup_and_teardown.rb @@ -0,0 +1,18 @@ +module Rails + module Generators + module Testing + module SetupAndTeardown + def setup # :nodoc: + destination_root_is_set? + ensure_current_path + super + end + + def teardown # :nodoc: + ensure_current_path + super + end + end + end + end +end |