require 'generators/dust' require 'rails/generators/migration' require 'rails/generators/generated_attribute' module Dust module Generators class ScaffoldGenerator < Base include Rails::Generators::Migration no_tasks { attr_accessor :model_name, :model_attributes, :controller_actions } argument :model_name, :type => :string, :required => true, :banner => 'ModelName' argument :args_for_c_m, :type => :array, :default => [], :banner => 'controller_actions and model:attributes' class_option :skip_model, :desc => 'Don\'t generate a model or migration file.', :type => :boolean class_option :skip_migration, :desc => 'Dont generate migration file for model.', :type => :boolean class_option :skip_timestamps, :desc => 'Don\'t add timestamps to migration file.', :type => :boolean class_option :skip_controller, :desc => 'Don\'t generate controller, helper, or views.', :type => :boolean class_option :invert, :desc => 'Generate all controller actions except these mentioned.', :type => :boolean class_option :haml, :desc => 'Generate HAML views instead of ERB.', :type => :boolean class_option :testunit, :desc => 'Use test/unit for test files.', :group => 'Test framework', :type => :boolean class_option :rspec, :desc => 'Use RSpec for test files.', :group => 'Test framework', :type => :boolean class_option :shoulda, :desc => 'Use shoulda for test files.', :group => 'Test framework', :type => :boolean def initialize(*args, &block) super @controller_actions = [] @model_attributes = [] @skip_model = options.skip_model? @invert_actions = options.invert? args_for_c_m.each do |arg| if arg == '!' @invert_actions = true elsif arg.include?(':') @model_attributes << Rails::Generators::GeneratedAttribute.new(*arg.split(':')) else @controller_actions << arg @controller_actions << 'create' if arg == 'new' @controller_actions << 'update' if arg == 'edit' end end @controller_actions.uniq! @model_attributes.uniq! if @invert_actions || @controller_actions.empty? @controller_actions = all_actions - @controller_actions end if @model_attributes.empty? @skip_model = true # skip model if no attributes if model_exists? model_columns_for_attributes.each do |column| @model_attributes << Rails::Generators::GeneratedAttribute.new(column.name.to_s, column.type.to_s) end else @model_attributes << Rails::Generators::GeneratedAttribute.new('name', 'string') end end end def add_gems gem "mocha", :group => :test unless File.read(destination_path("Gemfile")).include? "mocha" end def create_model unless @skip_model template 'model.rb', "app/models/#{singular_name}.rb" if test_framework == :rspec template "tests/rspec/model.rb", "spec/models/#{singular_name}_spec.rb" template 'fixtures.yml', "spec/fixtures/#{plural_name}.yml" else template "tests/#{test_framework}/model.rb", "test/unit/#{singular_name}_test.rb" template 'fixtures.yml', "test/fixtures/#{plural_name}.yml" end end end def create_migration unless @skip_model || options.skip_migration? migration_template 'migration.rb', "db/migrate/create_#{plural_name}.rb" end end def create_controller unless options.skip_controller? template 'controller.rb', "app/controllers/#{plural_name}_controller.rb" template 'helper.rb', "app/helpers/#{plural_name}_helper.rb" controller_actions.each do |action| if %w[index show new edit].include?(action) # Actions with templates template "views/#{view_language}/#{action}.html.#{view_language}", "app/views/#{plural_name}/#{action}.html.#{view_language}" end end template "views/#{view_language}/_search.html.erb", "app/views/#{plural_name}/_search.html.erb" if form_partial? template "views/#{view_language}/_form.html.#{view_language}", "app/views/#{plural_name}/_form.html.#{view_language}" end route "resources #{plural_name.to_sym.inspect}" if test_framework == :rspec template "tests/#{test_framework}/controller.rb", "spec/controllers/#{plural_name}_controller_spec.rb" else template "tests/#{test_framework}/controller.rb", "test/functional/#{plural_name}_controller_test.rb" end end end private def form_partial? actions? :new, :edit end def all_actions %w[index show new create edit update destroy] end def action?(name) controller_actions.include? name.to_s end def actions?(*names) names.all? { |name| action? name } end def singular_name model_name.underscore end def plural_name model_name.underscore.pluralize end def class_name model_name.camelize end def plural_class_name plural_name.camelize end def controller_methods(dir_name) controller_actions.map do |action| read_template("#{dir_name}/#{action}.rb") end.join(" \n").strip end def render_form if form_partial? if options.haml? "= render 'form'" else "<%= render 'form' %>" end else read_template("views/#{view_language}/_form.html.#{view_language}") end end def items_path(suffix = 'path') if action? :index "#{plural_name}_#{suffix}" else "root_#{suffix}" end end def item_path(suffix = 'path') if action? :show "@#{singular_name}" else items_path(suffix) end end def item_path_for_spec(suffix = 'path') if action? :show "#{singular_name}_#{suffix}(assigns[:#{singular_name}])" else items_path(suffix) end end def item_path_for_test(suffix = 'path') if action? :show "#{singular_name}_#{suffix}(assigns(:#{singular_name}))" else items_path(suffix) end end def model_columns_for_attributes class_name.constantize.columns.reject do |column| column.name.to_s =~ /^(id|created_at|updated_at)$/ end end def view_language options.haml? ? 'haml' : 'erb' end def test_framework return @test_framework if defined?(@test_framework) if options.testunit? return @test_framework = :testunit elsif options.rspec? return @test_framework = :rspec elsif options.shoulda? return @test_framework = :shoulda else return @test_framework = default_test_framework end end def default_test_framework File.exist?(destination_path("spec")) ? :rspec : :testunit end def model_exists? File.exist? destination_path("app/models/#{singular_name}.rb") end def read_template(relative_path) ERB.new(File.read(find_in_source_paths(relative_path)), nil, '-').result(binding) end def destination_path(path) File.join(destination_root, path) end # FIXME: Should be proxied to ActiveRecord::Generators::Base # Implement the required interface for Rails::Generators::Migration. def self.next_migration_number(dirname) #:nodoc: if ActiveRecord::Base.timestamped_migrations Time.now.utc.strftime("%Y%m%d%H%M%S") else "%.3d" % (current_migration_number(dirname) + 1) end end end end end