require 'rails/generators/migration' require 'rails/generators/generated_attribute' module Stager class ScaffoldGenerator < Rails::Generators::Base include Rails::Generators::Migration no_tasks { attr_accessor :scaffold_name, :model_attributes, :controller_actions } source_root File.expand_path('../templates', __FILE__) argument :scaffold_name, :type => :string, :required => true argument :model_and_controller_args, :type => :array, :default => [] class_option :namespaced_model, :type => :boolean, :default => false class_option :skip_migration, :type => :boolean, :default => false class_option :skip_timestamps, :type => :boolean, :default => false class_option :skip_presenter, :type => :boolean, :default => false def set_options @controller_actions = [] @model_attributes = [] @invert_actions = false @skip_model = false @namespaced_model = false model_and_controller_args.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 if options[:namespaced_model] @namespaced_model = true end if @invert_actions || @controller_actions.empty? @controller_actions = all_actions - @controller_actions end if @model_attributes.empty? @skip_model = true 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 create_model unless @skip_model template 'model.rb', "app/models/#{model_path}.rb" template "spec/model.rb", "spec/models/#{model_path}_spec.rb" template 'spec/fixtures.yml', "spec/fixtures/#{model_path.pluralize}.yml" end end def create_migration unless @skip_model || options[:skip_migration] migration_template 'migration.rb', "db/migrate/create_#{model_path.pluralize.gsub('/', '_')}.rb" end end def create_controller template 'controller.rb', "app/controllers/#{plural_name}_controller.rb" template 'helper.rb', "app/helpers/#{plural_name}_helper.rb" namespaces = plural_name.split('/') resource = namespaces.pop if namespaces.present? route "namespace(:#{namespaces.first}) { resources :#{resource} }" else route "resources :#{resource}" end template "spec/controller.rb", "spec/controllers/#{plural_name}_controller_spec.rb" end def create_views controller_actions.each do |action| if %w[index show new edit].include?(action) template "views/#{action}.html.haml", "app/views/#{plural_name}/#{action}.html.haml" end end if form_partial? template "views/_form.html.haml", "app/views/#{plural_name}/_form.html.haml" end end def create_presenter unless options[:skip_presenter] run "rails g exhibit:presenter #{scaffold_name} #{@namespaced_model ? nil : "--no_namespace"}" end end private def model_columns_for_attributes class_name.constantize.columns.reject do |column| column.name.to_s =~ /^(id|created_at|updated_at)$/ end end def model_exists? File.exist?(destination_path("app/models/#{singular_name}.rb")) end def model_path class_name.underscore end def destination_path(path) File.join(destination_root, path) 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 form_partial? actions?(:new, :edit) end def render_form if form_partial? "= render(\"form\")" else read_template("views/_form.html.#{view_language}") end end def controller_methods(dir_name) controller_actions.map do |action| read_template("#{dir_name}/#{action}.rb") end.join("\n").strip end def item_resource scaffold_name.underscore.gsub('/','_') end def items_path if action? :index "#{item_resource.pluralize}_path" else "root_path" end end def item_path(options = {}) name = options[:instance_variable] ? "@#{instance_name}" : instance_name suffix = options[:full_url] ? "url" : "path" if options[:action].to_s == "new" "new_#{item_resource}_#{suffix}" elsif options[:action].to_s == "edit" "edit_#{item_resource}_#{suffix}(#{name})" else if scaffold_name.include?('::') && !@namespaced_model namespace = singular_name.split('/')[0..-2] "[:#{namespace.join(', :')}, #{name}]" else name end end end def item_url if action? :show item_path(:full_url => true, :instance_variable => true) else items_url end end def items_url if action? :index item_resource.pluralize + '_url' else "root_url" end end def item_path_for_spec(suffix = 'path') if action? :show "#{item_resource}_#{suffix}(assigns[:#{instance_name}])" else if suffix == 'path' items_path else items_url end end end def table_name if @namespaced_model plural_name.gsub('/', '_') end end def singular_name scaffold_name.underscore end def plural_name scaffold_name.underscore.pluralize end def class_name if @namespaced_model singular_name.camelize else singular_name.split('/').last.camelize end end def plural_class_name plural_name.camelize end def instance_name if @namespaced_model singular_name.gsub('/', '_') else singular_name.split('/').last end end def instances_name instance_name.pluralize end def titleized_name singular_name.split('/').last.titleize end def read_template(relative_path) ERB.new(File.read(find_in_source_paths(relative_path)), nil, '-').result(binding) end def self.next_migration_number(dirname) 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