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.
- data/History.txt +3 -0
- data/License.txt +17 -0
- data/Manifest.txt +119 -0
- data/README.txt +204 -0
- data/Rakefile +142 -0
- data/bin/ruby_app +12 -0
- data/examples/rails_generators/applications/app/USAGE +16 -0
- data/examples/rails_generators/applications/app/app_generator.rb +177 -0
- data/examples/rails_generators/components/controller/USAGE +29 -0
- data/examples/rails_generators/components/controller/controller_generator.rb +37 -0
- data/examples/rails_generators/components/controller/templates/controller.rb +10 -0
- data/examples/rails_generators/components/controller/templates/functional_test.rb +18 -0
- data/examples/rails_generators/components/controller/templates/helper.rb +2 -0
- data/examples/rails_generators/components/controller/templates/view.html.erb +2 -0
- data/examples/rails_generators/components/integration_test/USAGE +8 -0
- data/examples/rails_generators/components/integration_test/integration_test_generator.rb +16 -0
- data/examples/rails_generators/components/integration_test/templates/integration_test.rb +10 -0
- data/examples/rails_generators/components/mailer/USAGE +16 -0
- data/examples/rails_generators/components/mailer/mailer_generator.rb +34 -0
- data/examples/rails_generators/components/mailer/templates/fixture.erb +3 -0
- data/examples/rails_generators/components/mailer/templates/fixture.rhtml +0 -0
- data/examples/rails_generators/components/mailer/templates/mailer.rb +13 -0
- data/examples/rails_generators/components/mailer/templates/unit_test.rb +37 -0
- data/examples/rails_generators/components/mailer/templates/view.erb +3 -0
- data/examples/rails_generators/components/mailer/templates/view.rhtml +0 -0
- data/examples/rails_generators/components/migration/USAGE +24 -0
- data/examples/rails_generators/components/migration/migration_generator.rb +20 -0
- data/examples/rails_generators/components/migration/templates/migration.rb +7 -0
- data/examples/rails_generators/components/model/USAGE +27 -0
- data/examples/rails_generators/components/model/model_generator.rb +38 -0
- data/examples/rails_generators/components/model/templates/fixtures.yml +15 -0
- data/examples/rails_generators/components/model/templates/migration.rb +14 -0
- data/examples/rails_generators/components/model/templates/model.rb +2 -0
- data/examples/rails_generators/components/model/templates/unit_test.rb +10 -0
- data/examples/rails_generators/components/observer/USAGE +13 -0
- data/examples/rails_generators/components/observer/observer_generator.rb +16 -0
- data/examples/rails_generators/components/observer/templates/observer.rb +2 -0
- data/examples/rails_generators/components/observer/templates/unit_test.rb +10 -0
- data/examples/rails_generators/components/plugin/USAGE +25 -0
- data/examples/rails_generators/components/plugin/plugin_generator.rb +39 -0
- data/examples/rails_generators/components/plugin/templates/MIT-LICENSE +20 -0
- data/examples/rails_generators/components/plugin/templates/README +13 -0
- data/examples/rails_generators/components/plugin/templates/Rakefile +22 -0
- data/examples/rails_generators/components/plugin/templates/USAGE +8 -0
- data/examples/rails_generators/components/plugin/templates/generator.rb +8 -0
- data/examples/rails_generators/components/plugin/templates/init.rb +1 -0
- data/examples/rails_generators/components/plugin/templates/install.rb +1 -0
- data/examples/rails_generators/components/plugin/templates/plugin.rb +1 -0
- data/examples/rails_generators/components/plugin/templates/tasks.rake +4 -0
- data/examples/rails_generators/components/plugin/templates/uninstall.rb +1 -0
- data/examples/rails_generators/components/plugin/templates/unit_test.rb +8 -0
- data/examples/rails_generators/components/resource/USAGE +23 -0
- data/examples/rails_generators/components/resource/resource_generator.rb +72 -0
- data/examples/rails_generators/components/resource/templates/USAGE +18 -0
- data/examples/rails_generators/components/resource/templates/controller.rb +2 -0
- data/examples/rails_generators/components/resource/templates/fixtures.yml +0 -0
- data/examples/rails_generators/components/resource/templates/functional_test.rb +20 -0
- data/examples/rails_generators/components/resource/templates/helper.rb +2 -0
- data/examples/rails_generators/components/scaffold/USAGE +25 -0
- data/examples/rails_generators/components/scaffold/scaffold_generator.rb +90 -0
- data/examples/rails_generators/components/scaffold/templates/controller.rb +85 -0
- data/examples/rails_generators/components/scaffold/templates/functional_test.rb +57 -0
- data/examples/rails_generators/components/scaffold/templates/helper.rb +2 -0
- data/examples/rails_generators/components/scaffold/templates/layout.html.erb +17 -0
- data/examples/rails_generators/components/scaffold/templates/style.css +74 -0
- data/examples/rails_generators/components/scaffold/templates/view_edit.html.erb +19 -0
- data/examples/rails_generators/components/scaffold/templates/view_index.html.erb +24 -0
- data/examples/rails_generators/components/scaffold/templates/view_new.html.erb +18 -0
- data/examples/rails_generators/components/scaffold/templates/view_show.html.erb +10 -0
- data/examples/rails_generators/components/session_migration/USAGE +10 -0
- data/examples/rails_generators/components/session_migration/session_migration_generator.rb +18 -0
- data/examples/rails_generators/components/session_migration/templates/migration.rb +16 -0
- data/examples/rails_generators/components/web_service/USAGE +24 -0
- data/examples/rails_generators/components/web_service/templates/api_definition.rb +5 -0
- data/examples/rails_generators/components/web_service/templates/controller.rb +8 -0
- data/examples/rails_generators/components/web_service/templates/functional_test.rb +19 -0
- data/examples/rails_generators/components/web_service/web_service_generator.rb +29 -0
- data/lib/rubigen/base.rb +168 -0
- data/lib/rubigen/commands.rb +632 -0
- data/lib/rubigen/generated_attribute.rb +40 -0
- data/lib/rubigen/generators/applications/ruby_app/USAGE +10 -0
- data/lib/rubigen/generators/applications/ruby_app/ruby_app_generator.rb +78 -0
- data/lib/rubigen/generators/applications/ruby_app/templates/README.txt +1 -0
- data/lib/rubigen/generators/applications/ruby_app/templates/configs/empty.log +0 -0
- data/lib/rubigen/generators/applications/ruby_app/templates/fresh_rakefile +10 -0
- data/lib/rubigen/generators/applications/ruby_app/templates/module.rb +5 -0
- data/lib/rubigen/generators/applications/ruby_app/templates/script/generate +13 -0
- data/lib/rubigen/generators/applications/ruby_app/templates/test_helper.rb +2 -0
- data/lib/rubigen/generators/components/test_unit/USAGE +14 -0
- data/lib/rubigen/generators/components/test_unit/templates/test +14 -0
- data/lib/rubigen/generators/components/test_unit/test_unit_generator.rb +26 -0
- data/lib/rubigen/helpers/generator_test_helper.rb +133 -0
- data/lib/rubigen/lookup.rb +296 -0
- data/lib/rubigen/manifest.rb +51 -0
- data/lib/rubigen/options.rb +136 -0
- data/lib/rubigen/scripts/destroy.rb +27 -0
- data/lib/rubigen/scripts/generate.rb +7 -0
- data/lib/rubigen/scripts/update.rb +12 -0
- data/lib/rubigen/scripts.rb +69 -0
- data/lib/rubigen/simple_logger.rb +44 -0
- data/lib/rubigen/spec.rb +42 -0
- data/lib/rubigen/version.rb +9 -0
- data/lib/rubigen.rb +44 -0
- data/script/destroy +9 -0
- data/script/generate +9 -0
- data/script/txt2html +65 -0
- data/setup.rb +1585 -0
- data/test/examples_from_rails/generator_test_helper.rb +195 -0
- data/test/examples_from_rails/test_rails_resource_generator.rb +106 -0
- data/test/examples_from_rails/test_rails_scaffold_generator.rb +185 -0
- data/test/test_generate_builtin_application.rb +24 -0
- data/test/test_generate_builtin_test_unit.rb +22 -0
- data/test/test_helper.rb +33 -0
- data/test/test_lookup.rb +103 -0
- data/website/index.html +352 -0
- data/website/index.txt +252 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +44 -0
- 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 @@
|
|
|
1
|
+
README
|
|
File without changes
|
|
@@ -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,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,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
|