adaptation 0.0.1
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/CHANGELOG +1 -0
- data/README +90 -0
- data/bin/about +5 -0
- data/bin/adaptation +19 -0
- data/bin/destroy +5 -0
- data/bin/generate +5 -0
- data/bin/mom +8 -0
- data/bin/subscribe +8 -0
- data/configs/boot.rb +10 -0
- data/configs/databases/mysql.yml +48 -0
- data/configs/empty.log +0 -0
- data/configs/mom.yml +8 -0
- data/dispatches/dispatch.rb +8 -0
- data/dispatches/publish.rb +11 -0
- data/doc/README_FOR_APP +2 -0
- data/fresh_rakefile +10 -0
- data/helpers/publish.rb +24 -0
- data/helpers/test_helper.rb +6 -0
- data/lib/adaptation/adaptor.rb +32 -0
- data/lib/adaptation/base.rb +70 -0
- data/lib/adaptation/message.rb +328 -0
- data/lib/adaptation/mom.rb +70 -0
- data/lib/adaptation/oapdaemon.rb +38 -0
- data/lib/adaptation/test/test_help.rb +282 -0
- data/lib/adaptation/version.rb +9 -0
- data/lib/adaptation.rb +5 -0
- data/lib/commands/about.rb +3 -0
- data/lib/commands/destroy.rb +6 -0
- data/lib/commands/generate.rb +6 -0
- data/lib/commands/mom.rb +8 -0
- data/lib/commands/subscribe.rb +11 -0
- data/lib/commands.rb +17 -0
- data/lib/rails_generator/base.rb +262 -0
- data/lib/rails_generator/commands.rb +582 -0
- data/lib/rails_generator/generated_attribute.rb +42 -0
- data/lib/rails_generator/generators/applications/app/USAGE +13 -0
- data/lib/rails_generator/generators/applications/app/app_generator.rb +133 -0
- data/lib/rails_generator/generators/components/adaptor/USAGE +25 -0
- data/lib/rails_generator/generators/components/adaptor/adaptor_generator.rb +21 -0
- data/lib/rails_generator/generators/components/adaptor/templates/adaptor.rb +6 -0
- data/lib/rails_generator/generators/components/adaptor/templates/functional_test.rb +16 -0
- data/lib/rails_generator/generators/components/message/USAGE +16 -0
- data/lib/rails_generator/generators/components/message/message_generator.rb +28 -0
- data/lib/rails_generator/generators/components/message/templates/fixtures.xml +3 -0
- data/lib/rails_generator/generators/components/message/templates/message.rb +2 -0
- data/lib/rails_generator/generators/components/message/templates/unit_test.rb +25 -0
- data/lib/rails_generator/generators/components/model/USAGE +26 -0
- data/lib/rails_generator/generators/components/model/model_generator.rb +38 -0
- data/lib/rails_generator/generators/components/model/templates/fixtures.yml +11 -0
- data/lib/rails_generator/generators/components/model/templates/migration.rb +13 -0
- data/lib/rails_generator/generators/components/model/templates/model.rb +2 -0
- data/lib/rails_generator/generators/components/model/templates/unit_test.rb +10 -0
- data/lib/rails_generator/lookup.rb +209 -0
- data/lib/rails_generator/manifest.rb +53 -0
- data/lib/rails_generator/options.rb +143 -0
- data/lib/rails_generator/scripts/destroy.rb +7 -0
- data/lib/rails_generator/scripts/generate.rb +7 -0
- data/lib/rails_generator/scripts/update.rb +12 -0
- data/lib/rails_generator/scripts.rb +85 -0
- data/lib/rails_generator/simple_logger.rb +46 -0
- data/lib/rails_generator/spec.rb +44 -0
- data/lib/rails_generator.rb +43 -0
- data/lib/ruby_version_check.rb +17 -0
- metadata +142 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
class AppGenerator < Rails::Generator::Base
|
4
|
+
DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
|
5
|
+
Config::CONFIG['ruby_install_name'])
|
6
|
+
|
7
|
+
DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase)
|
8
|
+
|
9
|
+
default_options :db => "mysql", :shebang => DEFAULT_SHEBANG, :freeze => false
|
10
|
+
mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.."
|
11
|
+
|
12
|
+
def initialize(runtime_args, runtime_options = {})
|
13
|
+
super
|
14
|
+
usage if args.empty?
|
15
|
+
usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db]))
|
16
|
+
@destination_root = args.shift
|
17
|
+
@app_name = File.basename(File.expand_path(@destination_root))
|
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
|
+
|
25
|
+
record do |m|
|
26
|
+
# Root directory and all subdirectories.
|
27
|
+
m.directory ''
|
28
|
+
BASEDIRS.each { |path| m.directory path }
|
29
|
+
|
30
|
+
# Root
|
31
|
+
m.file "README", "README"
|
32
|
+
|
33
|
+
# Application
|
34
|
+
m.file "helpers/test_helper.rb", "test/test_helper.rb"
|
35
|
+
m.file "helpers/publish.rb", "test/mocks/test/publish.rb"
|
36
|
+
|
37
|
+
# database.yml
|
38
|
+
m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => {
|
39
|
+
:app_name => @app_name,
|
40
|
+
:socket => options[:db] == "mysql" ? mysql_socket_location : nil
|
41
|
+
}
|
42
|
+
|
43
|
+
# mom.yml
|
44
|
+
m.template "configs/mom.yml", "config/mom.yml"
|
45
|
+
|
46
|
+
# boot.rb, needed by some scripts
|
47
|
+
m.file "configs/boot.rb", "config/boot.rb"
|
48
|
+
|
49
|
+
# Environments
|
50
|
+
#m.file "environments/boot.rb", "config/boot.rb"
|
51
|
+
#m.template "environments/environment.rb", "config/environment.rb", :assigns => { :freeze => options[:freeze] }
|
52
|
+
#m.file "environments/production.rb", "config/environments/production.rb"
|
53
|
+
#m.file "environments/development.rb", "config/environments/development.rb"
|
54
|
+
#m.file "environments/test.rb", "config/environments/test.rb"
|
55
|
+
|
56
|
+
# Scripts
|
57
|
+
%w( mom generate destroy about subscribe ).each do |file|
|
58
|
+
m.file "bin/#{file}", "script/#{file}", script_options
|
59
|
+
end
|
60
|
+
|
61
|
+
# Dispatches
|
62
|
+
m.file "dispatches/dispatch.rb", "public/dispatch.rb", script_options
|
63
|
+
m.file "dispatches/publish.rb", "public/publish.rb", script_options
|
64
|
+
|
65
|
+
# Docs
|
66
|
+
m.file "doc/README_FOR_APP", "doc/README_FOR_APP"
|
67
|
+
|
68
|
+
# Logs
|
69
|
+
%w(server production development test).each { |file|
|
70
|
+
m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
def banner
|
77
|
+
"Usage: #{$0} /path/to/your/app [options]"
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_options!(opt)
|
81
|
+
opt.separator ''
|
82
|
+
opt.separator 'Options:'
|
83
|
+
opt.on("-r", "--ruby=path", String,
|
84
|
+
"Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
|
85
|
+
"Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
|
86
|
+
|
87
|
+
opt.on("-d", "--database=name", String,
|
88
|
+
"Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3).",
|
89
|
+
"Default: mysql") { |v| options[:db] = v }
|
90
|
+
|
91
|
+
#opt.on("-f", "--freeze",
|
92
|
+
# "Freeze Rails in vendor/rails from the gems generating the skeleton",
|
93
|
+
# "Default: false") { |v| options[:freeze] = v }
|
94
|
+
end
|
95
|
+
|
96
|
+
def mysql_socket_location
|
97
|
+
MYSQL_SOCKET_LOCATIONS.find { |f| File.exists?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Installation skeleton. Intermediate directories are automatically
|
102
|
+
# created so don't sweat their absence here.
|
103
|
+
BASEDIRS = %w(
|
104
|
+
app/adaptors
|
105
|
+
app/messages
|
106
|
+
app/models
|
107
|
+
config/environments
|
108
|
+
db
|
109
|
+
doc
|
110
|
+
lib
|
111
|
+
lib/tasks
|
112
|
+
log
|
113
|
+
public
|
114
|
+
script
|
115
|
+
test/fixtures
|
116
|
+
test/functional
|
117
|
+
test/mocks/development
|
118
|
+
test/mocks/test
|
119
|
+
test/unit
|
120
|
+
)
|
121
|
+
|
122
|
+
MYSQL_SOCKET_LOCATIONS = [
|
123
|
+
"/tmp/mysql.sock", # default
|
124
|
+
"/var/run/mysqld/mysqld.sock", # debian/gentoo
|
125
|
+
"/var/tmp/mysql.sock", # freebsd
|
126
|
+
"/var/lib/mysql/mysql.sock", # fedora
|
127
|
+
"/opt/local/lib/mysql/mysql.sock", # fedora
|
128
|
+
"/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
|
129
|
+
"/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
|
130
|
+
"/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
|
131
|
+
"/opt/lampp/var/mysql/mysql.sock" # xampp for linux
|
132
|
+
]
|
133
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Description:
|
2
|
+
The adaptor generator creates stubs for a new adaptor.
|
3
|
+
|
4
|
+
The generator takes an adaptor name.
|
5
|
+
The adaptor name may be given in CamelCase or under_score and should
|
6
|
+
not be suffixed with 'Adaptor'. To create an adaptor within a
|
7
|
+
module, specify the adaptor name as 'module/adaptor'.
|
8
|
+
|
9
|
+
The generator creates an adaptor class in app/adaptors
|
10
|
+
and a functional test suite in test/functional.
|
11
|
+
|
12
|
+
Example:
|
13
|
+
./script/generate adaptor AddClient
|
14
|
+
|
15
|
+
Add client adaptor.
|
16
|
+
Adaptor: app/adaptors/add_client_adaptor.rb
|
17
|
+
Test: test/functional/add_client_adaptor_test.rb
|
18
|
+
|
19
|
+
Modules Example:
|
20
|
+
./script/generate adaptor 'admin/add_client'
|
21
|
+
|
22
|
+
Add client adaptor.
|
23
|
+
Adaptor: app/adaptors/admin/add_client_adaptor.rb
|
24
|
+
Test: test/functional/admin/add_client_adaptor.rb
|
25
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class AdaptorGenerator < Rails::Generator::NamedBase
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
# Check for class naming collisions.
|
5
|
+
m.class_collisions class_path, "#{class_name}Adaptor", "#{class_name}AdaptorTest"
|
6
|
+
|
7
|
+
# Adaptors and test directories.
|
8
|
+
m.directory File.join('app/adaptors', class_path)
|
9
|
+
m.directory File.join('test/functional', class_path)
|
10
|
+
# Adaptor class and functional test.
|
11
|
+
m.template 'adaptor.rb',
|
12
|
+
File.join('app/adaptors',
|
13
|
+
class_path,
|
14
|
+
"#{file_name}_adaptor.rb")
|
15
|
+
m.template 'functional_test.rb',
|
16
|
+
File.join('test/functional',
|
17
|
+
class_path,
|
18
|
+
"#{file_name}_adaptor_test.rb")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
|
2
|
+
require '<%= file_path %>_adaptor'
|
3
|
+
|
4
|
+
class <%= class_name %>AdaptorTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
fixtures :<%= table_name %>
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@adaptor = <%= class_name %>Adaptor.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Replace this with your real tests.
|
13
|
+
def test_truth
|
14
|
+
assert true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Description:
|
2
|
+
The message generator creates stubs for a new message.
|
3
|
+
|
4
|
+
The generator takes a message name as its argument. The message name may be given in CamelCase or under_score and
|
5
|
+
should not be suffixed with 'Message'.
|
6
|
+
|
7
|
+
The generator creates a message class in app/messages, a test suite in test/unit and test fixtures in
|
8
|
+
test/fixtures/singular_name.xml.
|
9
|
+
|
10
|
+
Examples:
|
11
|
+
./script/generate message add_client
|
12
|
+
|
13
|
+
This will create an AddClient message:
|
14
|
+
Message: app/messages/add_client.rb
|
15
|
+
Test: test/unit/add_client_test.rb
|
16
|
+
Fixtures: test/fixtures/add_client.xml
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class MessageGenerator < Rails::Generator::NamedBase
|
2
|
+
|
3
|
+
def manifest
|
4
|
+
record do |m|
|
5
|
+
# Check for class naming collisions.
|
6
|
+
m.class_collisions class_path, class_name, "#{class_name}Test"
|
7
|
+
|
8
|
+
# Model, test, and fixture directories.
|
9
|
+
m.directory File.join('app/mmessages', class_path)
|
10
|
+
m.directory File.join('test/unit', class_path)
|
11
|
+
m.directory File.join('test/fixtures', class_path)
|
12
|
+
|
13
|
+
# Model class, unit test, and fixtures.
|
14
|
+
m.template 'message.rb', File.join('app/messages', class_path, "#{file_name}.rb")
|
15
|
+
m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
|
16
|
+
m.template 'fixtures.xml', File.join('test/fixtures', class_path, "#{file_name}.xml")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def banner
|
22
|
+
"Usage: #{$0} generate MessageName [field:type, field:type]"
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_options!(opt)
|
26
|
+
opt.separator ''
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
|
2
|
+
|
3
|
+
class <%= class_name %>Test < Test::Unit::TestCase
|
4
|
+
fixtures :<%= file_name %>
|
5
|
+
|
6
|
+
# Replace this with your real tests.
|
7
|
+
def test_truth
|
8
|
+
assert true
|
9
|
+
end
|
10
|
+
|
11
|
+
# assert messages with unexpected parts are detected
|
12
|
+
#def test_not_parsed
|
13
|
+
# assert_parsed :<%= file_name %>
|
14
|
+
# assert_not_parsed :<%= file_name %>_with_unknown_element
|
15
|
+
# assert_not_parsed :<%= file_name %>_with_unknown_attribute
|
16
|
+
#end
|
17
|
+
|
18
|
+
# assert message passes our validations
|
19
|
+
#def test_validates
|
20
|
+
# assert_validates :<%= file_name %>
|
21
|
+
# assert_not_validates :<%= file_name %>_without_gid
|
22
|
+
#end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Description:
|
2
|
+
The model generator creates stubs for a new model.
|
3
|
+
|
4
|
+
The generator takes a model name as its argument. The model name may be given in CamelCase or under_score and
|
5
|
+
should not be suffixed with 'Model'.
|
6
|
+
|
7
|
+
As additional parameters, the generator will take attribute pairs described by name and type. These attributes will
|
8
|
+
be used to prepopulate the migration to create the table for the model and give you a set of predefined fixture.
|
9
|
+
You don't have to think up all attributes up front, but it's a good idea of adding just the baseline of what's
|
10
|
+
needed to start really working with the resource.
|
11
|
+
|
12
|
+
The generator creates a model class in app/models, a test suite in test/unit, test fixtures in
|
13
|
+
test/fixtures/singular_name.yml, and a migration in db/migrate.
|
14
|
+
|
15
|
+
Examples:
|
16
|
+
./script/generate model account
|
17
|
+
|
18
|
+
This will create an Account model:
|
19
|
+
Model: app/models/account.rb
|
20
|
+
Test: test/unit/account_test.rb
|
21
|
+
Fixtures: test/fixtures/accounts.yml
|
22
|
+
Migration: db/migrate/XXX_add_accounts.rb
|
23
|
+
|
24
|
+
./script/generate model post title:string created_on:date body:text published:boolean
|
25
|
+
|
26
|
+
Creates post model with predefined attributes.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class ModelGenerator < Rails::Generator::NamedBase
|
2
|
+
default_options :skip_migration => false
|
3
|
+
|
4
|
+
def manifest
|
5
|
+
record do |m|
|
6
|
+
# Check for class naming collisions.
|
7
|
+
m.class_collisions class_path, class_name, "#{class_name}Test"
|
8
|
+
|
9
|
+
# Model, test, and fixture directories.
|
10
|
+
m.directory File.join('app/models', class_path)
|
11
|
+
#m.directory File.join('test/unit', class_path)
|
12
|
+
m.directory File.join('test/fixtures', class_path)
|
13
|
+
|
14
|
+
# Model class, unit test, and fixtures.
|
15
|
+
m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
|
16
|
+
#m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
|
17
|
+
#m.template 'fixtures.yml', File.join('test/fixtures', class_path, "#{table_name}.yml")
|
18
|
+
|
19
|
+
#unless options[:skip_migration]
|
20
|
+
# m.migration_template 'migration.rb', 'db/migrate', :assigns => {
|
21
|
+
# :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
|
22
|
+
# }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
|
23
|
+
#end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
def banner
|
29
|
+
"Usage: #{$0} generate ModelName [field:type, field:type]"
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_options!(opt)
|
33
|
+
opt.separator ''
|
34
|
+
opt.separator 'Options:'
|
35
|
+
opt.on("--skip-migration",
|
36
|
+
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
2
|
+
one:
|
3
|
+
id: 1
|
4
|
+
<% for attribute in attributes -%>
|
5
|
+
<%= attribute.name %>: <%= attribute.default %>
|
6
|
+
<% end -%>
|
7
|
+
two:
|
8
|
+
id: 2
|
9
|
+
<% for attribute in attributes -%>
|
10
|
+
<%= attribute.name %>: <%= attribute.default %>
|
11
|
+
<% end -%>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class <%= migration_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :<%= table_name %> do |t|
|
4
|
+
<% for attribute in attributes -%>
|
5
|
+
t.column :<%= attribute.name %>, :<%= attribute.type %>
|
6
|
+
<% end -%>
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down
|
11
|
+
drop_table :<%= table_name %>
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,209 @@
|
|
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, ~/.rails/generators, and RAILS_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
|
+
Rails::Generator::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 Rails
|
39
|
+
module Generator
|
40
|
+
|
41
|
+
# Generator lookup is managed by a list of sources which return specs
|
42
|
+
# describing where to find and how to create generators. This module
|
43
|
+
# provides class methods for manipulating the source list and looking up
|
44
|
+
# generator specs, and an #instance wrapper for quickly instantiating
|
45
|
+
# generators by name.
|
46
|
+
#
|
47
|
+
# A spec is not a generator: it's a description of where to find
|
48
|
+
# the generator and how to create it. A source is anything that
|
49
|
+
# yields generators from #each. PathSource and GemSource are provided.
|
50
|
+
module Lookup
|
51
|
+
def self.included(base)
|
52
|
+
base.extend(ClassMethods)
|
53
|
+
base.use_component_sources!
|
54
|
+
end
|
55
|
+
|
56
|
+
# Convenience method to instantiate another generator.
|
57
|
+
def instance(generator_name, args, runtime_options = {})
|
58
|
+
self.class.instance(generator_name, args, runtime_options)
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
# The list of sources where we look, in order, for generators.
|
63
|
+
def sources
|
64
|
+
read_inheritable_attribute(:sources) or use_component_sources!
|
65
|
+
end
|
66
|
+
|
67
|
+
# Add a source to the end of the list.
|
68
|
+
def append_sources(*args)
|
69
|
+
sources.concat(args.flatten)
|
70
|
+
invalidate_cache!
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add a source to the beginning of the list.
|
74
|
+
def prepend_sources(*args)
|
75
|
+
write_inheritable_array(:sources, args.flatten + sources)
|
76
|
+
invalidate_cache!
|
77
|
+
end
|
78
|
+
|
79
|
+
# Reset the source list.
|
80
|
+
def reset_sources
|
81
|
+
write_inheritable_attribute(:sources, [])
|
82
|
+
invalidate_cache!
|
83
|
+
end
|
84
|
+
|
85
|
+
# Use application generators (app, ?).
|
86
|
+
def use_application_sources!
|
87
|
+
reset_sources
|
88
|
+
sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Use component generators (model, controller, etc).
|
92
|
+
# 1. Rails application. If RAILS_ROOT is defined we know we're
|
93
|
+
# generating in the context of a Rails application, so search
|
94
|
+
# RAILS_ROOT/generators.
|
95
|
+
# 2. User home directory. Search ~/.rails/generators.
|
96
|
+
# 3. RubyGems. Search for gems named *_generator.
|
97
|
+
# 4. Builtins. Model, controller, mailer, scaffold.
|
98
|
+
def use_component_sources!
|
99
|
+
reset_sources
|
100
|
+
if defined? ::RAILS_ROOT
|
101
|
+
sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators")
|
102
|
+
sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators")
|
103
|
+
sources << PathSource.new(:plugins, "#{::RAILS_ROOT}/vendor/plugins/**/generators")
|
104
|
+
end
|
105
|
+
sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
|
106
|
+
sources << GemSource.new if Object.const_defined?(:Gem)
|
107
|
+
sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
|
108
|
+
end
|
109
|
+
|
110
|
+
# Lookup knows how to find generators' Specs from a list of Sources.
|
111
|
+
# Searches the sources, in order, for the first matching name.
|
112
|
+
def lookup(generator_name)
|
113
|
+
@found ||= {}
|
114
|
+
generator_name = generator_name.to_s.downcase
|
115
|
+
@found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
|
116
|
+
unless @found[generator_name]
|
117
|
+
chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
|
118
|
+
rx = /^#{chars}$/
|
119
|
+
gns = cache.select{|spec| spec.name =~ rx }
|
120
|
+
@found[generator_name] ||= gns.first if gns.length == 1
|
121
|
+
raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
|
122
|
+
end
|
123
|
+
@found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
|
124
|
+
end
|
125
|
+
|
126
|
+
# Convenience method to lookup and instantiate a generator.
|
127
|
+
def instance(generator_name, args = [], runtime_options = {})
|
128
|
+
lookup(generator_name).klass.new(args, full_options(runtime_options))
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
# Lookup and cache every generator from the source list.
|
133
|
+
def cache
|
134
|
+
@cache ||= sources.inject([]) { |cache, source| cache + source.map }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Clear the cache whenever the source list changes.
|
138
|
+
def invalidate_cache!
|
139
|
+
@cache = nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Sources enumerate (yield from #each) generator specs which describe
|
145
|
+
# where to find and how to create generators. Enumerable is mixed in so,
|
146
|
+
# for example, source.collect will retrieve every generator.
|
147
|
+
# Sources may be assigned a label to distinguish them.
|
148
|
+
class Source
|
149
|
+
include Enumerable
|
150
|
+
|
151
|
+
attr_reader :label
|
152
|
+
def initialize(label)
|
153
|
+
@label = label
|
154
|
+
end
|
155
|
+
|
156
|
+
# The each method must be implemented in subclasses.
|
157
|
+
# The base implementation raises an error.
|
158
|
+
def each
|
159
|
+
raise NotImplementedError
|
160
|
+
end
|
161
|
+
|
162
|
+
# Return a convenient sorted list of all generator names.
|
163
|
+
def names
|
164
|
+
map { |spec| spec.name }.sort
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
# PathSource looks for generators in a filesystem directory.
|
170
|
+
class PathSource < Source
|
171
|
+
attr_reader :path
|
172
|
+
|
173
|
+
def initialize(label, path)
|
174
|
+
super label
|
175
|
+
@path = path
|
176
|
+
end
|
177
|
+
|
178
|
+
# Yield each eligible subdirectory.
|
179
|
+
def each
|
180
|
+
Dir["#{path}/[a-z]*"].each do |dir|
|
181
|
+
if File.directory?(dir)
|
182
|
+
yield Spec.new(File.basename(dir), dir, label)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# GemSource hits the mines to quarry for generators. The latest versions
|
190
|
+
# of gems named *_generator are selected.
|
191
|
+
class GemSource < Source
|
192
|
+
def initialize
|
193
|
+
super :RubyGems
|
194
|
+
end
|
195
|
+
|
196
|
+
# Yield latest versions of generator gems.
|
197
|
+
def each
|
198
|
+
Gem::cache.search(/_generator$/).inject({}) { |latest, gem|
|
199
|
+
hem = latest[gem.name]
|
200
|
+
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
|
201
|
+
latest
|
202
|
+
}.values.each { |gem|
|
203
|
+
yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
|
204
|
+
}
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Rails
|
2
|
+
module Generator
|
3
|
+
|
4
|
+
# Manifest captures the actions a generator performs. Instantiate
|
5
|
+
# a manifest with an optional target object, hammer it with actions,
|
6
|
+
# then replay or rewind on the object of your choice.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# manifest = Manifest.new { |m|
|
10
|
+
# m.make_directory '/foo'
|
11
|
+
# m.create_file '/foo/bar.txt'
|
12
|
+
# }
|
13
|
+
# manifest.replay(creator)
|
14
|
+
# manifest.rewind(destroyer)
|
15
|
+
class Manifest
|
16
|
+
attr_reader :target
|
17
|
+
|
18
|
+
# Take a default action target. Yield self if block given.
|
19
|
+
def initialize(target = nil)
|
20
|
+
@target, @actions = target, []
|
21
|
+
yield self if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Record an action.
|
25
|
+
def method_missing(action, *args, &block)
|
26
|
+
@actions << [action, args, block]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Replay recorded actions.
|
30
|
+
def replay(target = nil)
|
31
|
+
send_actions(target || @target, @actions)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Rewind recorded actions.
|
35
|
+
def rewind(target = nil)
|
36
|
+
send_actions(target || @target, @actions.reverse)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Erase recorded actions.
|
40
|
+
def erase
|
41
|
+
@actions = []
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def send_actions(target, actions)
|
46
|
+
actions.each do |method, args, block|
|
47
|
+
target.send(method, *args, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|