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.
Files changed (64) hide show
  1. data/CHANGELOG +1 -0
  2. data/README +90 -0
  3. data/bin/about +5 -0
  4. data/bin/adaptation +19 -0
  5. data/bin/destroy +5 -0
  6. data/bin/generate +5 -0
  7. data/bin/mom +8 -0
  8. data/bin/subscribe +8 -0
  9. data/configs/boot.rb +10 -0
  10. data/configs/databases/mysql.yml +48 -0
  11. data/configs/empty.log +0 -0
  12. data/configs/mom.yml +8 -0
  13. data/dispatches/dispatch.rb +8 -0
  14. data/dispatches/publish.rb +11 -0
  15. data/doc/README_FOR_APP +2 -0
  16. data/fresh_rakefile +10 -0
  17. data/helpers/publish.rb +24 -0
  18. data/helpers/test_helper.rb +6 -0
  19. data/lib/adaptation/adaptor.rb +32 -0
  20. data/lib/adaptation/base.rb +70 -0
  21. data/lib/adaptation/message.rb +328 -0
  22. data/lib/adaptation/mom.rb +70 -0
  23. data/lib/adaptation/oapdaemon.rb +38 -0
  24. data/lib/adaptation/test/test_help.rb +282 -0
  25. data/lib/adaptation/version.rb +9 -0
  26. data/lib/adaptation.rb +5 -0
  27. data/lib/commands/about.rb +3 -0
  28. data/lib/commands/destroy.rb +6 -0
  29. data/lib/commands/generate.rb +6 -0
  30. data/lib/commands/mom.rb +8 -0
  31. data/lib/commands/subscribe.rb +11 -0
  32. data/lib/commands.rb +17 -0
  33. data/lib/rails_generator/base.rb +262 -0
  34. data/lib/rails_generator/commands.rb +582 -0
  35. data/lib/rails_generator/generated_attribute.rb +42 -0
  36. data/lib/rails_generator/generators/applications/app/USAGE +13 -0
  37. data/lib/rails_generator/generators/applications/app/app_generator.rb +133 -0
  38. data/lib/rails_generator/generators/components/adaptor/USAGE +25 -0
  39. data/lib/rails_generator/generators/components/adaptor/adaptor_generator.rb +21 -0
  40. data/lib/rails_generator/generators/components/adaptor/templates/adaptor.rb +6 -0
  41. data/lib/rails_generator/generators/components/adaptor/templates/functional_test.rb +16 -0
  42. data/lib/rails_generator/generators/components/message/USAGE +16 -0
  43. data/lib/rails_generator/generators/components/message/message_generator.rb +28 -0
  44. data/lib/rails_generator/generators/components/message/templates/fixtures.xml +3 -0
  45. data/lib/rails_generator/generators/components/message/templates/message.rb +2 -0
  46. data/lib/rails_generator/generators/components/message/templates/unit_test.rb +25 -0
  47. data/lib/rails_generator/generators/components/model/USAGE +26 -0
  48. data/lib/rails_generator/generators/components/model/model_generator.rb +38 -0
  49. data/lib/rails_generator/generators/components/model/templates/fixtures.yml +11 -0
  50. data/lib/rails_generator/generators/components/model/templates/migration.rb +13 -0
  51. data/lib/rails_generator/generators/components/model/templates/model.rb +2 -0
  52. data/lib/rails_generator/generators/components/model/templates/unit_test.rb +10 -0
  53. data/lib/rails_generator/lookup.rb +209 -0
  54. data/lib/rails_generator/manifest.rb +53 -0
  55. data/lib/rails_generator/options.rb +143 -0
  56. data/lib/rails_generator/scripts/destroy.rb +7 -0
  57. data/lib/rails_generator/scripts/generate.rb +7 -0
  58. data/lib/rails_generator/scripts/update.rb +12 -0
  59. data/lib/rails_generator/scripts.rb +85 -0
  60. data/lib/rails_generator/simple_logger.rb +46 -0
  61. data/lib/rails_generator/spec.rb +44 -0
  62. data/lib/rails_generator.rb +43 -0
  63. data/lib/ruby_version_check.rb +17 -0
  64. 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,6 @@
1
+ class <%= class_name %>Adaptor < Adaptation::Adaptor
2
+
3
+ def process
4
+ end
5
+
6
+ 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,3 @@
1
+ <<%= file_name %>>
2
+ # put your xml here
3
+ </<%= file_name %>>
@@ -0,0 +1,2 @@
1
+ class <%= class_name %> < Adaptation::Message
2
+ 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,2 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ end
@@ -0,0 +1,10 @@
1
+ require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
2
+
3
+ class <%= class_name %>Test < Test::Unit::TestCase
4
+ fixtures :<%= table_name %>
5
+
6
+ # Replace this with your real tests.
7
+ def test_truth
8
+ assert true
9
+ end
10
+ 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