rubigen 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|