#!/usr/bin/env ruby # bivouac # (c)2007, 2008 Grégoire Lejeune require 'rubygems' require 'optparse' require 'ostruct' require 'bivouac/template' require 'bivouac/utils' require 'active_support' require 'active_record' require 'find' require 'fileutils' require File.dirname($0) + '/../config/environment.rb' module Bivouac class Generate include Bivouac::Template # Initialize the optionnal generators table add_generator [] def initialize( argv ) # Generator type (controller, model, migration, view, ...) @generator_type = argv.shift # Display help if asked if ['-h', '--help'].include?( @generator_type ) help exit end # Generator arguments. # Example : # script/generate controller Hello World # @script_arguments = ['Hello', 'World'] @script_arguments = argv.dup # Application environment @app = Bivouac::Environment.new( ) # Database configuration @database_configuration = @app.environment.db if @database_configuration[:adapter] =~ /sqlite/ @database_configuration[:database] = File.expand_path( File.dirname( $0 ) + "/../" + @database_configuration[:database] ) end end def run # If the générator is not installed, display help and exit unless ( ["controller", "model", "migration", "view", "test", "helper"] + @@plugins_generators ).include?( @generator_type ) help exit end # Call generator send( @generator_type.to_sym ) return end private def help puts "Usage: script/generate generator [options] [args]" puts "" puts "Generators" puts " Builtin: controller, view, helper, model, migration, test" puts " Plugins: " + @@plugins_generators.join(", ") if @@plugins_generators.size > 0 puts "" puts "Commun options:" puts " -h, --help Show general help message" end def controller # controller generator options controller_options = OpenStruct.new( :noview => false, :notest => false, :default_route => true, :routes => [] ) # Controller options argument_options = OptionParser.new do |opts| opts.banner = "" opts.separator "controller generator options:" opts.on("-v", "--no-view", "Do not generate any view for the controller") { |controller_options.noview| controller_options.noview = true } opts.on("-t", "--no-test", "Do not generate any test for the controller") { |controller_options.notest| controller_options.notest = true } opts.separator "" opts.on_tail("-h", "--help", "Show this message") do help puts opts puts "" puts "Description:" puts " Stubs out a new controller and its views. Pass the controller name, either" puts " CamelCased or under_scored, and a list of views as arguments." puts "" puts "Example:" puts " ruby script/generate controller my_controller my_view_one my_view_two ..." exit end end # parse options argument_options.parse! @script_arguments if @script_arguments.length < 1 help puts argument_options puts "" puts "Description:" puts " Stubs out a new controller and its views. Pass the controller name, either" puts " CamelCased or under_scored, and a list of views as arguments." puts "" puts "Example:" puts " ruby script/generate controller my_controller my_view_one my_view_two ..." exit end # List of routes for test routes = [] # Get controller controller = @script_arguments.shift # File name for the template controller_file_name = controller.underscore # Views directory views_directory = File.dirname( $0 ) + "/../app/views/" + controller_file_name # Create views directory createDir( views_directory ) # Destination file controller_file = File.dirname( $0 ) + "/../app/controllers/" + controller_file_name + ".rb" # Create controller createFile( controller_file ) do |io| io.puts template( "generate/controller_begin", binding ) # Create views @script_arguments.each do |view| # Controller class name, passed to the template @controller_class_name = controller.classify + view.classify # Set view name, passed to the template @view_name = @controller_class_name.underscore # File name for the view @view_file = views_directory + "/" + view.underscore + ".rb" # Set controller route new_route = controller.underscore + "/" + view.underscore routes << new_route @controller_routes = " < R '/" + new_route + "'" # Update controller io.puts template( "generate/controller_action", binding ) # Create view view_name createFile( @view_file ) do |io_view| io_view.puts template( "generate/view", binding ) end end # Create test for @view_name with routes controller_options.routes test( controller, routes ) if controller_options.notest == false # Write controller end io.puts template( "generate/controller_end", binding ) end end def view( view_name = nil ) if @app.environment.orgtype.downcase == "erb" raise "ERB applications are no longer supported. Sorry!" end # View options argument_options = OptionParser.new do |opts| opts.banner = "" opts.separator "view generator options:" opts.on_tail("-h", "--help", "Show this message") do help puts opts exit end end # parse options if view name is not passed if view_name.nil? argument_options.parse! @script_arguments if @script_arguments.length < 1 help puts argument_options exit end view_name = @script_arguments else view_name = [ view_name ] end view_name.each do |view| # Set view name, passed to the template @view_name = view.underscore # Set destination file, passed to the template @view_file = File.dirname( $0 ) + "/../app/views/" + @view_name.underscore + ".rb" # Create view file createFile( @view_file ) do |io| io.puts template( "generate/view", binding ) end end end def helper( helper_name = nil ) if @app.environment.orgtype.downcase == "erb" raise "ERB applications are no longer supported. Sorry!" end # Helper options argument_options = OptionParser.new do |opts| opts.banner = "" opts.separator "helper generator options:" opts.on_tail("-h", "--help", "Show this message") do help puts opts exit end end # parse options if view name is not passed if helper_name.nil? argument_options.parse! @script_arguments if @script_arguments.length < 1 help puts argument_options exit end helper_name = @script_arguments else helper_name = [ view_name ] end helper_name.each do |helper| # Set helper name, passed to the template @helper_name = helper.underscore # Set destination file, passed to the template @destination_file = File.dirname( $0 ) + "/../app/helpers/" + @helper_name.underscore + ".rb" # Create helper file createFile( @destination_file ) do |io| io.puts template( "generate/helper", binding ) end end end def test( test_name = nil, routes = nil ) # Test options argument_options = OptionParser.new do |opts| opts.banner = "" opts.separator "test generator options:" opts.on_tail("-h", "--help", "Show this message") do help puts opts puts "" puts "Description:" puts " Stubs out a new test. Pass the name of the test, either" puts " CamelCased or under_scored, as an argument." puts "" puts "Example:" puts " ruby script/generate test my_test /route/one /route/two ..." exit end end # parse options if view name is not passed if test_name.nil? argument_options.parse! @script_arguments if @script_arguments.length < 1 help puts argument_options puts "" puts "Description:" puts " Stubs out a new test. Pass the name of the test, either" puts " CamelCased or under_scored, as an argument." puts "" puts "Example:" puts " ruby script/generate test my_test /route/one /route/two ..." exit end test_name = @script_arguments.shift routes = @script_arguments end # Create test file destination_file = File.dirname( $0 ) + "/../test/test_" + test_name.underscore + ".rb" createFile( destination_file ) do |io| io.puts template( "generate/test_begin", binding ) routes.each do |route| @route = route @test_name = route.gsub( /\//, '_' ) io.puts template( "generate/test_views", binding ) end io.puts template( "generate/test_end", binding ) end end def model( model = nil ) # Model options argument_options = OptionParser.new do |opts| opts.banner = "" opts.separator "model generator options:" opts.on_tail("-h", "--help", "Show this message") do help puts opts puts "" puts "Description:" puts " Stubs out a new model. Pass the model name, either CamelCased or" puts " under_scored, and an optional list of attribute pairs as arguments." puts "" puts " Attribute pairs are column_name:sql_type arguments specifying the" puts " model's attributes." puts "" puts "Example:" puts " ruby script/generate model MyModel name:string age:integer ..." exit end end # parse options if model.nil? argument_options.parse! @script_arguments if @script_arguments.length < 1 help puts argument_options puts "" puts "Description:" puts " Stubs out a new model. Pass the model name, either CamelCased or" puts " under_scored, and an optional list of attribute pairs as arguments." puts "" puts " Attribute pairs are column_name:sql_type arguments specifying the" puts " model's attributes." puts "" puts "Example:" puts " ruby script/generate model MyModel name:string age:integer ..." exit end model_list = @script_arguments else model_list = [ model ] end model_name = model_list.shift #model_list.each do |model_name| # Model class name, passed to the template @model_class_name = model_name.classify destination_file = File.dirname( $0 ) + "/../app/models/" + @model_class_name.underscore + ".rb" createFile( destination_file ) do |io| io.puts template( "generate/model", binding ) end migration( [model_name] + model_list ) #end end def migration( model = nil ) # Test options argument_options = OptionParser.new do |opts| opts.banner = "" opts.separator "migration generator options:" opts.on_tail("-h", "--help", "Show this message") do help puts opts puts "" puts "Description:" puts " Stubs out a new database migration. Pass the migration name, either" puts " CamelCased or under_scored, and an optional list of attribute pairs as arguments." puts "" puts " You can name your migration in either of these formats to generate add/remove" puts " column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable" puts "" puts "Example:" puts " ruby script/generate migration AddTrucToPost truc:string" puts " ruby script/generate migration RemoveTrucFromPost truc:string" exit end end # parse options if view name is not passed if model.nil? argument_options.parse! @script_arguments if @script_arguments.length < 1 help puts argument_options puts "" puts "Description:" puts " Stubs out a new database migration. Pass the migration name, either" puts " CamelCased or under_scored, and an optional list of attribute pairs as arguments." puts "" puts " You can name your migration in either of these formats to generate add/remove" puts " column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable" puts "" puts "Example:" puts " ruby script/generate migration AddTrucToPost truc:string" puts " ruby script/generate migration RemoveTrucFromPost truc:string" exit end model = @script_arguments from_model = false else # model = [ model ] from_model = true end # Get old and new version old_version = 0.0 begin ActiveRecord::Base.establish_connection @database_configuration conn = ActiveRecord::Base.connection table = nil conn.tables.each { |t| table = t if t =~ /_schema_infos$/ } version_info = conn.select_all( "select * from #{table}" ) old_version = version_info[0]['version'].to_f rescue end @new_version = old_version + 1 # Get prefix for file files = [] Find.find( File.dirname( $0 ) + "/../db/migrate/" ) do |f| fn = File.basename( f ) files << fn if fn =~ /^\d\d\d/ end file_prefix = "001" if files.size > 0 files = files.sort {|x,y| y[0,3] <=> x[0,3]} file_prefix = sprintf( "%03d", (files[0][0,3].to_i + 1) ) end puts "\t* Migration from version #{old_version} to version #{@new_version}" if old_version > 0.0 model_name = model.shift # model.each do |model_name| # Model class name @model_class_name = model_name.classify destination_file = File.dirname( $0 ) + "/../db/migrate/" + file_prefix + "_" + model_name.underscore + ".rb" createFile( destination_file ) do |io| if from_model # Model table name @model_table_name = (@app.environment.appname.downcase + "_" + model_name).tableize io.puts template( "generate/migrate_begin", binding ) model.each do |column| @model_data_name, @model_data_type = column.split( /:/ ) io.puts template( "generate/migrate_column", binding ) end io.puts template( "generate/migrate_end", binding ) else # Model table name @model_table_name = (@app.environment.appname.downcase + "_" + (model_name.underscore.split( /_/ ))[-1]).tableize io.puts template( "generate/migration_begin", binding ) # AddColumnsToTable if r = /add_([^_]*)_to_([^_]*)/.match( model_name.underscore ) model.each do |column| @model_data_name, @model_data_type = column.split( /:/ ) io.puts template( "generate/migration_add", binding ) end io.puts template( "generate/migration_middle", binding ) model.each do |column| @model_data_name, @model_data_type = column.split( /:/ ) io.puts template( "generate/migration_remove", binding ) end # RemoveColumnsToTable elsif r = /remove_([^_]*)_from_([^_]*)/.match( model_name.underscore ) model.each do |column| @model_data_name, @model_data_type = column.split( /:/ ) io.puts template( "generate/migration_remove", binding ) end io.puts template( "generate/migration_middle", binding ) model.each do |column| @model_data_name, @model_data_type = column.split( /:/ ) io.puts template( "generate/migration_add", binding ) end # Nor Add or Remove else io.puts template( "generate/migration_middle", binding ) end io.puts template( "generate/migration_end", binding ) end end # end destination_file = File.dirname( $0 ) + "/../db/create.rb" createFile( destination_file, false, false ) do |io| io.puts template( "generate/create", binding ) end end end end Bivouac::Generate.new( ARGV ).run( )