rubigen 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/History.txt +3 -0
  2. data/License.txt +17 -0
  3. data/Manifest.txt +119 -0
  4. data/README.txt +204 -0
  5. data/Rakefile +142 -0
  6. data/bin/ruby_app +12 -0
  7. data/examples/rails_generators/applications/app/USAGE +16 -0
  8. data/examples/rails_generators/applications/app/app_generator.rb +177 -0
  9. data/examples/rails_generators/components/controller/USAGE +29 -0
  10. data/examples/rails_generators/components/controller/controller_generator.rb +37 -0
  11. data/examples/rails_generators/components/controller/templates/controller.rb +10 -0
  12. data/examples/rails_generators/components/controller/templates/functional_test.rb +18 -0
  13. data/examples/rails_generators/components/controller/templates/helper.rb +2 -0
  14. data/examples/rails_generators/components/controller/templates/view.html.erb +2 -0
  15. data/examples/rails_generators/components/integration_test/USAGE +8 -0
  16. data/examples/rails_generators/components/integration_test/integration_test_generator.rb +16 -0
  17. data/examples/rails_generators/components/integration_test/templates/integration_test.rb +10 -0
  18. data/examples/rails_generators/components/mailer/USAGE +16 -0
  19. data/examples/rails_generators/components/mailer/mailer_generator.rb +34 -0
  20. data/examples/rails_generators/components/mailer/templates/fixture.erb +3 -0
  21. data/examples/rails_generators/components/mailer/templates/fixture.rhtml +0 -0
  22. data/examples/rails_generators/components/mailer/templates/mailer.rb +13 -0
  23. data/examples/rails_generators/components/mailer/templates/unit_test.rb +37 -0
  24. data/examples/rails_generators/components/mailer/templates/view.erb +3 -0
  25. data/examples/rails_generators/components/mailer/templates/view.rhtml +0 -0
  26. data/examples/rails_generators/components/migration/USAGE +24 -0
  27. data/examples/rails_generators/components/migration/migration_generator.rb +20 -0
  28. data/examples/rails_generators/components/migration/templates/migration.rb +7 -0
  29. data/examples/rails_generators/components/model/USAGE +27 -0
  30. data/examples/rails_generators/components/model/model_generator.rb +38 -0
  31. data/examples/rails_generators/components/model/templates/fixtures.yml +15 -0
  32. data/examples/rails_generators/components/model/templates/migration.rb +14 -0
  33. data/examples/rails_generators/components/model/templates/model.rb +2 -0
  34. data/examples/rails_generators/components/model/templates/unit_test.rb +10 -0
  35. data/examples/rails_generators/components/observer/USAGE +13 -0
  36. data/examples/rails_generators/components/observer/observer_generator.rb +16 -0
  37. data/examples/rails_generators/components/observer/templates/observer.rb +2 -0
  38. data/examples/rails_generators/components/observer/templates/unit_test.rb +10 -0
  39. data/examples/rails_generators/components/plugin/USAGE +25 -0
  40. data/examples/rails_generators/components/plugin/plugin_generator.rb +39 -0
  41. data/examples/rails_generators/components/plugin/templates/MIT-LICENSE +20 -0
  42. data/examples/rails_generators/components/plugin/templates/README +13 -0
  43. data/examples/rails_generators/components/plugin/templates/Rakefile +22 -0
  44. data/examples/rails_generators/components/plugin/templates/USAGE +8 -0
  45. data/examples/rails_generators/components/plugin/templates/generator.rb +8 -0
  46. data/examples/rails_generators/components/plugin/templates/init.rb +1 -0
  47. data/examples/rails_generators/components/plugin/templates/install.rb +1 -0
  48. data/examples/rails_generators/components/plugin/templates/plugin.rb +1 -0
  49. data/examples/rails_generators/components/plugin/templates/tasks.rake +4 -0
  50. data/examples/rails_generators/components/plugin/templates/uninstall.rb +1 -0
  51. data/examples/rails_generators/components/plugin/templates/unit_test.rb +8 -0
  52. data/examples/rails_generators/components/resource/USAGE +23 -0
  53. data/examples/rails_generators/components/resource/resource_generator.rb +72 -0
  54. data/examples/rails_generators/components/resource/templates/USAGE +18 -0
  55. data/examples/rails_generators/components/resource/templates/controller.rb +2 -0
  56. data/examples/rails_generators/components/resource/templates/fixtures.yml +0 -0
  57. data/examples/rails_generators/components/resource/templates/functional_test.rb +20 -0
  58. data/examples/rails_generators/components/resource/templates/helper.rb +2 -0
  59. data/examples/rails_generators/components/scaffold/USAGE +25 -0
  60. data/examples/rails_generators/components/scaffold/scaffold_generator.rb +90 -0
  61. data/examples/rails_generators/components/scaffold/templates/controller.rb +85 -0
  62. data/examples/rails_generators/components/scaffold/templates/functional_test.rb +57 -0
  63. data/examples/rails_generators/components/scaffold/templates/helper.rb +2 -0
  64. data/examples/rails_generators/components/scaffold/templates/layout.html.erb +17 -0
  65. data/examples/rails_generators/components/scaffold/templates/style.css +74 -0
  66. data/examples/rails_generators/components/scaffold/templates/view_edit.html.erb +19 -0
  67. data/examples/rails_generators/components/scaffold/templates/view_index.html.erb +24 -0
  68. data/examples/rails_generators/components/scaffold/templates/view_new.html.erb +18 -0
  69. data/examples/rails_generators/components/scaffold/templates/view_show.html.erb +10 -0
  70. data/examples/rails_generators/components/session_migration/USAGE +10 -0
  71. data/examples/rails_generators/components/session_migration/session_migration_generator.rb +18 -0
  72. data/examples/rails_generators/components/session_migration/templates/migration.rb +16 -0
  73. data/examples/rails_generators/components/web_service/USAGE +24 -0
  74. data/examples/rails_generators/components/web_service/templates/api_definition.rb +5 -0
  75. data/examples/rails_generators/components/web_service/templates/controller.rb +8 -0
  76. data/examples/rails_generators/components/web_service/templates/functional_test.rb +19 -0
  77. data/examples/rails_generators/components/web_service/web_service_generator.rb +29 -0
  78. data/lib/rubigen/base.rb +168 -0
  79. data/lib/rubigen/commands.rb +632 -0
  80. data/lib/rubigen/generated_attribute.rb +40 -0
  81. data/lib/rubigen/generators/applications/ruby_app/USAGE +10 -0
  82. data/lib/rubigen/generators/applications/ruby_app/ruby_app_generator.rb +78 -0
  83. data/lib/rubigen/generators/applications/ruby_app/templates/README.txt +1 -0
  84. data/lib/rubigen/generators/applications/ruby_app/templates/configs/empty.log +0 -0
  85. data/lib/rubigen/generators/applications/ruby_app/templates/fresh_rakefile +10 -0
  86. data/lib/rubigen/generators/applications/ruby_app/templates/module.rb +5 -0
  87. data/lib/rubigen/generators/applications/ruby_app/templates/script/generate +13 -0
  88. data/lib/rubigen/generators/applications/ruby_app/templates/test_helper.rb +2 -0
  89. data/lib/rubigen/generators/components/test_unit/USAGE +14 -0
  90. data/lib/rubigen/generators/components/test_unit/templates/test +14 -0
  91. data/lib/rubigen/generators/components/test_unit/test_unit_generator.rb +26 -0
  92. data/lib/rubigen/helpers/generator_test_helper.rb +133 -0
  93. data/lib/rubigen/lookup.rb +296 -0
  94. data/lib/rubigen/manifest.rb +51 -0
  95. data/lib/rubigen/options.rb +136 -0
  96. data/lib/rubigen/scripts/destroy.rb +27 -0
  97. data/lib/rubigen/scripts/generate.rb +7 -0
  98. data/lib/rubigen/scripts/update.rb +12 -0
  99. data/lib/rubigen/scripts.rb +69 -0
  100. data/lib/rubigen/simple_logger.rb +44 -0
  101. data/lib/rubigen/spec.rb +42 -0
  102. data/lib/rubigen/version.rb +9 -0
  103. data/lib/rubigen.rb +44 -0
  104. data/script/destroy +9 -0
  105. data/script/generate +9 -0
  106. data/script/txt2html +65 -0
  107. data/setup.rb +1585 -0
  108. data/test/examples_from_rails/generator_test_helper.rb +195 -0
  109. data/test/examples_from_rails/test_rails_resource_generator.rb +106 -0
  110. data/test/examples_from_rails/test_rails_scaffold_generator.rb +185 -0
  111. data/test/test_generate_builtin_application.rb +24 -0
  112. data/test/test_generate_builtin_test_unit.rb +22 -0
  113. data/test/test_helper.rb +33 -0
  114. data/test/test_lookup.rb +103 -0
  115. data/website/index.html +352 -0
  116. data/website/index.txt +252 -0
  117. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  118. data/website/stylesheets/screen.css +138 -0
  119. data/website/template.rhtml +44 -0
  120. metadata +183 -0
@@ -0,0 +1,78 @@
1
+ require 'rbconfig'
2
+
3
+ class RubyAppGenerator < RubiGen::Base
4
+ DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
5
+ Config::CONFIG['ruby_install_name'])
6
+
7
+ default_options :shebang => DEFAULT_SHEBANG
8
+
9
+ attr_accessor :app_name
10
+ attr_accessor :module_name
11
+
12
+ def initialize(runtime_args, runtime_options = {})
13
+ super
14
+ usage if args.empty?
15
+ @destination_root = args.shift
16
+ self.app_name = File.basename(File.expand_path(@destination_root))
17
+ self.module_name = app_name.camelize
18
+ end
19
+
20
+ def manifest
21
+ # Use /usr/bin/env if no special shebang was specified
22
+ script_options = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
23
+ dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] }
24
+ windows = (RUBY_PLATFORM =~ /dos|win32|cygwin/i) || (RUBY_PLATFORM =~ /(:?mswin|mingw)/)
25
+
26
+ record do |m|
27
+ # Root directory and all subdirectories.
28
+ m.directory ''
29
+ BASEDIRS.each { |path| m.directory path }
30
+
31
+ # Root
32
+ m.file "fresh_rakefile", "Rakefile"
33
+ m.file "README.txt", "README.txt"
34
+
35
+ # Scripts
36
+ %w( generate ).each do |file|
37
+ m.file "script/#{file}", "script/#{file}", script_options
38
+ m.template "script/win_script.cmd", "script/#{file}.cmd",
39
+ :assigns => { :filename => file } if windows
40
+ end
41
+
42
+ # Default module for app
43
+ m.template "module.rb", "lib/#{app_name}.rb", script_options
44
+
45
+ # Test helper
46
+ m.template "test_helper.rb", "test/test_helper.rb", script_options
47
+
48
+ %w(debug).each { |file|
49
+ m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666
50
+ }
51
+ end
52
+ end
53
+
54
+ protected
55
+ def banner
56
+ "Usage: #{$0} /path/to/your/app [options]"
57
+ end
58
+
59
+ def add_options!(opt)
60
+ opt.separator ''
61
+ opt.separator 'Options:'
62
+ opt.on("-r", "--ruby=path", String,
63
+ "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
64
+ "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
65
+ end
66
+
67
+
68
+ # Installation skeleton. Intermediate directories are automatically
69
+ # created so don't sweat their absence here.
70
+ BASEDIRS = %w(
71
+ doc
72
+ lib
73
+ log
74
+ script
75
+ test
76
+ tmp
77
+ )
78
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ begin
3
+ require 'rake'
4
+ rescue LoadError
5
+ puts 'This script should only be accessed via the "rake" command.'
6
+ puts 'Installation: gem install rake -y'
7
+ exit
8
+ end
9
+ require 'rake/clean'
10
+
@@ -0,0 +1,5 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ module <%= module_name %>
4
+
5
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/<%= app_name %>'
@@ -0,0 +1,14 @@
1
+ Description:
2
+ Stubs out a new test (using Test::Unit). Pass the model name, either
3
+ CamelCased or under_scored.
4
+
5
+ This generates a unit test in /test.
6
+
7
+ Examples:
8
+ `./script/generate test_unit account_receiver`
9
+
10
+ creates an Account test:
11
+ Test: test/test_account_receiver.rb
12
+
13
+ The same result will occur for:
14
+ `./script/generate test_unit AccountReceiver`
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class <%= class_name %> < Test::Unit::TestCase
4
+ def setup
5
+ end
6
+
7
+ def teardown
8
+ end
9
+
10
+ # Replace this with your real tests.
11
+ def test_truth
12
+ assert true
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ class TestUnitGenerator < RubiGen::Base
2
+
3
+ attr_reader :name, :test_name, :class_name
4
+
5
+ def initialize(runtime_args, runtime_options = {})
6
+ super
7
+ usage if args.empty?
8
+ @name = args.shift
9
+ @test_name = "test_#{name}".underscore
10
+ @class_name = test_name.camelize
11
+ end
12
+
13
+ def manifest
14
+ record do |m|
15
+ m.directory 'test'
16
+
17
+ # Model class, unit test, and fixtures.
18
+ m.template 'test', "test/#{test_name}.rb"
19
+ end
20
+ end
21
+
22
+ protected
23
+ def banner
24
+ "Usage: #{$0} #{spec.name} NameOfTest"
25
+ end
26
+ end
@@ -0,0 +1,133 @@
1
+ module RubiGen
2
+ module GeneratorTestHelper
3
+ # Runs the create command (like the command line does)
4
+ def run_generator(name, params, sources, options = {})
5
+ silence_generator do
6
+ build_generator(name, params, sources, options).command(:create).invoke!
7
+ end
8
+ end
9
+
10
+ # Instatiates the Generator
11
+ def build_generator(name, params, sources, options)
12
+ # if !options.first
13
+ # type, sources = :component, []
14
+ # elsif options.first.is_a?(Symbol)
15
+ # type, sources = options.first, []
16
+ # else
17
+ # type, sources = :component, options.select { |s| s.is_a?(Source) }
18
+ # end
19
+ if sources.is_a?(Symbol)
20
+ if sources == :app
21
+ RubiGen::Base.use_application_sources!
22
+ else
23
+ RubiGen::Base.use_component_sources!
24
+ end
25
+ else
26
+ RubiGen::Base.reset_sources
27
+ RubiGen::Base.append_sources(*sources) unless sources.blank?
28
+ end
29
+ # generator_options = options.find { |o| o.is_a?(Hash) } || {}
30
+ RubiGen::Base.instance(name, params, options)
31
+ end
32
+
33
+ # Silences the logger temporarily and returns the output as a String
34
+ def silence_generator
35
+ logger_original=RubiGen::Base.logger
36
+ myout=StringIO.new
37
+ RubiGen::Base.logger=RubiGen::SimpleLogger.new(myout)
38
+ yield if block_given?
39
+ RubiGen::Base.logger=logger_original
40
+ myout.string
41
+ end
42
+
43
+ # asserts that the given file was generated.
44
+ # the contents of the file is passed to a block.
45
+ def assert_generated_file(path)
46
+ assert_file_exists(path)
47
+ File.open("#{APP_ROOT}/#{path}") do |f|
48
+ yield f.read if block_given?
49
+ end
50
+ end
51
+
52
+ # asserts that the given file exists
53
+ def assert_file_exists(path)
54
+ assert File.exists?("#{APP_ROOT}/#{path}"),"The file '#{path}' should exist"
55
+ end
56
+
57
+ # asserts that the given directory exists
58
+ def assert_directory_exists(path)
59
+ assert File.directory?("#{APP_ROOT}/#{path}"),"The directory '#{path}' should exist"
60
+ end
61
+
62
+ # asserts that the given class source file was generated.
63
+ # It takes a path without the <tt>.rb</tt> part and an optional super class.
64
+ # the contents of the class source file is passed to a block.
65
+ def assert_generated_class(path,parent=nil)
66
+ path=~/\/?(\d+_)?(\w+)$/
67
+ class_name=$2.camelize
68
+ assert_generated_file("#{path}.rb") do |body|
69
+ assert body=~/class #{class_name}#{parent.nil? ? '':" < #{parent}"}/,"the file '#{path}.rb' should be a class"
70
+ yield body if block_given?
71
+ end
72
+ end
73
+
74
+ # asserts that the given module source file was generated.
75
+ # It takes a path without the <tt>.rb</tt> part.
76
+ # the contents of the class source file is passed to a block.
77
+ def assert_generated_module(path)
78
+ path=~/\/?(\w+)$/
79
+ module_name=$1.camelize
80
+ assert_generated_file("#{path}.rb") do |body|
81
+ assert body=~/module #{module_name}/,"the file '#{path}.rb' should be a module"
82
+ yield body if block_given?
83
+ end
84
+ end
85
+
86
+ # asserts that the given unit test was generated.
87
+ # It takes a name or symbol without the <tt>test_</tt> part and an optional super class.
88
+ # the contents of the class source file is passed to a block.
89
+ def assert_generated_test_for(name, parent="Test::Unit::TestCase")
90
+ assert_generated_class "test/test_#{name.to_s.underscore}",parent do |body|
91
+ yield body if block_given?
92
+ end
93
+ end
94
+
95
+ # asserts that the given methods are defined in the body.
96
+ # This does assume standard rails code conventions with regards to the source code.
97
+ # The body of each individual method is passed to a block.
98
+ def assert_has_method(body,*methods)
99
+ methods.each do |name|
100
+ assert body=~/^ def #{name.to_s}\n((\n| .*\n)*) end/,"should have method #{name.to_s}"
101
+ yield( name, $1 ) if block_given?
102
+ end
103
+ end
104
+
105
+ def app_root_files
106
+ Dir[APP_ROOT + '/**/*']
107
+ end
108
+
109
+ def rubygem_folders
110
+ %w[bin examples lib test]
111
+ end
112
+
113
+ def rubygems_setup
114
+ bare_setup
115
+ rubygem_folders.each do |folder|
116
+ Dir.mkdir("#{APP_ROOT}/#{folder}") unless File.exists?("#{APP_ROOT}/#{folder}")
117
+ end
118
+ end
119
+
120
+ def rubygems_teardown
121
+ bare_teardown
122
+ end
123
+
124
+ def bare_setup
125
+ FileUtils.mkdir_p(APP_ROOT)
126
+ end
127
+
128
+ def bare_teardown
129
+ FileUtils.rm_rf TMP_ROOT || APP_ROOT
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,296 @@
1
+ require File.dirname(__FILE__) + '/spec'
2
+
3
+ class Object
4
+ class << self
5
+ # Lookup missing generators using const_missing. This allows any
6
+ # generator to reference another without having to know its location:
7
+ # RubyGems, ~/.rubigen/generators, and APP_ROOT/generators.
8
+ def lookup_missing_generator(class_id)
9
+ if md = /(.+)Generator$/.match(class_id.to_s)
10
+ name = md.captures.first.demodulize.underscore
11
+ RubiGen::Base.lookup(name).klass
12
+ else
13
+ const_missing_before_generators(class_id)
14
+ end
15
+ end
16
+
17
+ unless respond_to?(:const_missing_before_generators)
18
+ alias_method :const_missing_before_generators, :const_missing
19
+ alias_method :const_missing, :lookup_missing_generator
20
+ end
21
+ end
22
+ end
23
+
24
+ # User home directory lookup adapted from RubyGems.
25
+ def Dir.user_home
26
+ if ENV['HOME']
27
+ ENV['HOME']
28
+ elsif ENV['USERPROFILE']
29
+ ENV['USERPROFILE']
30
+ elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
31
+ "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
32
+ else
33
+ File.expand_path '~'
34
+ end
35
+ end
36
+
37
+
38
+ module RubiGen
39
+
40
+ # Generator lookup is managed by a list of sources which return specs
41
+ # describing where to find and how to create generators. This module
42
+ # provides class methods for manipulating the source list and looking up
43
+ # generator specs, and an #instance wrapper for quickly instantiating
44
+ # generators by name.
45
+ #
46
+ # A spec is not a generator: it's a description of where to find
47
+ # the generator and how to create it. A source is anything that
48
+ # yields generators from #each. PathSource and GemGeneratorSource are provided.
49
+ module Lookup
50
+ def self.included(base)
51
+ base.extend(ClassMethods)
52
+ base.use_component_sources!
53
+ end
54
+
55
+ # Convenience method to instantiate another generator.
56
+ def instance(generator_name, args, runtime_options = {})
57
+ self.class.instance(generator_name, args, runtime_options)
58
+ end
59
+
60
+ module ClassMethods
61
+ # The list of sources where we look, in order, for generators.
62
+ def sources
63
+ if read_inheritable_attribute(:sources).blank?
64
+ if superclass == RubiGen::Base
65
+ superclass_sources = superclass.sources
66
+ diff = superclass_sources.inject([]) do |mem, source|
67
+ found = false
68
+ application_sources.each { |app_source| found ||= true if app_source == source}
69
+ mem << source unless found
70
+ mem
71
+ end
72
+ write_inheritable_attribute(:sources, diff)
73
+ end
74
+ use_component_sources! if read_inheritable_attribute(:sources).blank?
75
+ end
76
+ read_inheritable_attribute(:sources)
77
+ end
78
+
79
+ # Add a source to the end of the list.
80
+ def append_sources(*args)
81
+ sources.concat(args.flatten)
82
+ invalidate_cache!
83
+ end
84
+
85
+ # Add a source to the beginning of the list.
86
+ def prepend_sources(*args)
87
+ write_inheritable_array(:sources, args.flatten + sources)
88
+ invalidate_cache!
89
+ end
90
+
91
+ # Reset the source list.
92
+ def reset_sources
93
+ write_inheritable_attribute(:sources, [])
94
+ invalidate_cache!
95
+ end
96
+
97
+ # Use application generators (app, ?).
98
+ def use_application_sources!(*filters)
99
+ reset_sources
100
+ write_inheritable_attribute(:sources, application_sources(filters))
101
+ end
102
+
103
+ def application_sources(filters = [])
104
+ filters.unshift 'app'
105
+ app_sources = []
106
+ app_sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
107
+ app_sources << filtered_sources(filters)
108
+ app_sources.flatten
109
+ end
110
+
111
+ # Use component generators (test_unit, etc).
112
+ # 1. Current application. If APP_ROOT is defined we know we're
113
+ # generating in the context of this application, so search
114
+ # APP_ROOT/generators.
115
+ # 2. User home directory. Search ~/.rubigen/generators.
116
+ # 3. RubyGems. Search for gems containing /generators folder.
117
+ # 4. Builtins. test_unit.
118
+ #
119
+ # Search can be filtered by passing one or more prefixes.
120
+ # e.g. use_component_sources!(:rubygems) means it will also search in the following
121
+ # folders:
122
+ # 5. User home directory. Search ~/.rubigen/rubygems_generators.
123
+ # 6. RubyGems. Search for gems containing /rubygems_generators folder.
124
+ def use_component_sources!(*filters)
125
+ reset_sources
126
+ new_sources = []
127
+ if defined? ::APP_ROOT
128
+ new_sources << PathSource.new(:root, "#{::APP_ROOT}/generators")
129
+ new_sources << PathSource.new(:vendor, "#{::APP_ROOT}/vendor/generators")
130
+ new_sources << PathSource.new(:plugins, "#{::APP_ROOT}/vendor/plugins/*/**/generators")
131
+ end
132
+ new_sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
133
+ new_sources << filtered_sources(filters)
134
+ write_inheritable_attribute(:sources, new_sources.flatten)
135
+ end
136
+
137
+ def filtered_sources(filters)
138
+ new_sources = []
139
+ new_sources << PathFilteredSource.new(:user, "#{Dir.user_home}/.rubigen/", *filters)
140
+ if Object.const_defined?(:Gem)
141
+ new_sources << GemPathSource.new(*filters)
142
+ end
143
+ new_sources
144
+ end
145
+
146
+ # Lookup knows how to find generators' Specs from a list of Sources.
147
+ # Searches the sources, in order, for the first matching name.
148
+ def lookup(generator_name)
149
+ @found ||= {}
150
+ generator_name = generator_name.to_s.downcase
151
+ @found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
152
+ unless @found[generator_name]
153
+ chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
154
+ rx = /^#{chars}$/
155
+ gns = cache.select{|spec| spec.name =~ rx }
156
+ @found[generator_name] ||= gns.first if gns.length == 1
157
+ raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
158
+ end
159
+ @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
160
+ end
161
+
162
+ # Convenience method to lookup and instantiate a generator.
163
+ def instance(generator_name, args = [], runtime_options = {})
164
+ lookup(generator_name).klass.new(args, full_options(runtime_options))
165
+ end
166
+
167
+ private
168
+ # Lookup and cache every generator from the source list.
169
+ def cache
170
+ @cache ||= sources.inject([]) { |cache, source| cache + source.map }
171
+ end
172
+
173
+ # Clear the cache whenever the source list changes.
174
+ def invalidate_cache!
175
+ @cache = nil
176
+ end
177
+ end
178
+ end
179
+
180
+ # Sources enumerate (yield from #each) generator specs which describe
181
+ # where to find and how to create generators. Enumerable is mixed in so,
182
+ # for example, source.collect will retrieve every generator.
183
+ # Sources may be assigned a label to distinguish them.
184
+ class Source
185
+ include Enumerable
186
+
187
+ attr_reader :label
188
+ def initialize(label)
189
+ @label = label
190
+ end
191
+
192
+ # The each method must be implemented in subclasses.
193
+ # The base implementation raises an error.
194
+ def each
195
+ raise NotImplementedError
196
+ end
197
+
198
+ # Return a convenient sorted list of all generator names.
199
+ def names
200
+ map { |spec| spec.name }.sort
201
+ end
202
+ end
203
+
204
+
205
+ # PathSource looks for generators in a filesystem directory.
206
+ class PathSource < Source
207
+ attr_reader :path
208
+
209
+ def initialize(label, path)
210
+ super label
211
+ @path = File.expand_path path
212
+ end
213
+
214
+ # Yield each eligible subdirectory.
215
+ def each
216
+ Dir["#{path}/[a-z]*"].each do |dir|
217
+ if File.directory?(dir)
218
+ yield Spec.new(File.basename(dir), dir, label)
219
+ end
220
+ end
221
+ end
222
+
223
+ def ==(source)
224
+ self.class == source.class && path == source.path
225
+ end
226
+ end
227
+
228
+ class PathFilteredSource < PathSource
229
+ attr_reader :filters
230
+
231
+ def initialize(label, path, *filters)
232
+ super label, File.join(path, "#{filter_str(filters)}generators")
233
+ end
234
+
235
+ def filter_str(filters)
236
+ @filters = filters.first.is_a?(Array) ? filters.first : filters
237
+ return "" if @filters.blank?
238
+ filter_str = @filters.map {|filter| "#{filter}_"}.join(",")
239
+ "{#{filter_str}}"
240
+ end
241
+
242
+ def ==(source)
243
+ self.class == source.class && path == source.path && filters == source.filters
244
+ end
245
+ end
246
+
247
+ class AbstractGemSource < Source
248
+ def initialize
249
+ super :RubyGems
250
+ end
251
+ end
252
+
253
+ # GemPathSource looks for generators within any RubyGem's /{filter_}generators/**/<generator_name>_generator.rb file.
254
+ class GemPathSource < AbstractGemSource
255
+ attr_accessor :filters
256
+
257
+ def initialize(*filters)
258
+ super()
259
+ @filters = filters
260
+ end
261
+
262
+ # Yield each generator within rails_generator subdirectories.
263
+ def each
264
+ generator_full_paths.each do |generator|
265
+ yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label)
266
+ end
267
+ end
268
+
269
+ def ==(source)
270
+ self.class == source.class && filters == source.filters
271
+ end
272
+
273
+ private
274
+ def generator_full_paths
275
+ @generator_full_paths ||=
276
+ Gem::cache.inject({}) do |latest, name_gem|
277
+ name, gem = name_gem
278
+ hem = latest[gem.name]
279
+ latest[gem.name] = gem if hem.nil? or gem.version > hem.version
280
+ latest
281
+ end.values.inject([]) do |mem, gem|
282
+ Dir[gem.full_gem_path + "/#{filter_str}generators/**/*_generator.rb"].each do |generator|
283
+ mem << generator
284
+ end
285
+ mem
286
+ end
287
+ end
288
+
289
+ def filter_str
290
+ @filters = filters.first.is_a?(Array) ? filters.first : filters
291
+ return "" if filters.blank?
292
+ filter_str = filters.map {|filter| "#{filter}_"}.join(",")
293
+ "{#{filter_str}}"
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,51 @@
1
+ module RubiGen
2
+
3
+ # Manifest captures the actions a generator performs. Instantiate
4
+ # a manifest with an optional target object, hammer it with actions,
5
+ # then replay or rewind on the object of your choice.
6
+ #
7
+ # Example:
8
+ # manifest = Manifest.new { |m|
9
+ # m.make_directory '/foo'
10
+ # m.create_file '/foo/bar.txt'
11
+ # }
12
+ # manifest.replay(creator)
13
+ # manifest.rewind(destroyer)
14
+ class Manifest
15
+ attr_reader :target
16
+
17
+ # Take a default action target. Yield self if block given.
18
+ def initialize(target = nil)
19
+ @target, @actions = target, []
20
+ yield self if block_given?
21
+ end
22
+
23
+ # Record an action.
24
+ def method_missing(action, *args, &block)
25
+ @actions << [action, args, block]
26
+ end
27
+
28
+ # Replay recorded actions.
29
+ def replay(target = nil)
30
+ send_actions(target || @target, @actions)
31
+ end
32
+
33
+ # Rewind recorded actions.
34
+ def rewind(target = nil)
35
+ send_actions(target || @target, @actions.reverse)
36
+ end
37
+
38
+ # Erase recorded actions.
39
+ def erase
40
+ @actions = []
41
+ end
42
+
43
+ private
44
+ def send_actions(target, actions)
45
+ actions.each do |method, args, block|
46
+ target.send(method, *args, &block)
47
+ end
48
+ end
49
+ end
50
+
51
+ end