rubigen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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