muding 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/CHANGELOG +11 -0
  2. data/MANIFEST +77 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README +21 -0
  5. data/bin/console +4 -0
  6. data/bin/destroy +4 -0
  7. data/bin/generate +4 -0
  8. data/bin/muding +16 -0
  9. data/bin/server +5 -0
  10. data/configs/boot.rb +4 -0
  11. data/configs/databases/mysql.yml +47 -0
  12. data/configs/databases/oracle.yml +30 -0
  13. data/configs/databases/postgresql.yml +44 -0
  14. data/configs/databases/sqlite2.yml +16 -0
  15. data/configs/databases/sqlite3.yml +16 -0
  16. data/doc/README_FOR_MUD +3 -0
  17. data/fresh_rakefile +4 -0
  18. data/helpers/mud.rb +8 -0
  19. data/helpers/mud_helper.rb +4 -0
  20. data/helpers/test_helper.rb +29 -0
  21. data/lib/acts/container.rb +70 -0
  22. data/lib/acts/expireable.rb +82 -0
  23. data/lib/commands/destroy.rb +7 -0
  24. data/lib/commands/generate.rb +7 -0
  25. data/lib/commands/server.rb +45 -0
  26. data/lib/commands/update.rb +5 -0
  27. data/lib/controller.rb +132 -0
  28. data/lib/handle.rb +42 -0
  29. data/lib/model.rb +11 -0
  30. data/lib/muding.rb +46 -0
  31. data/lib/muding_generator.rb +22 -0
  32. data/lib/muding_generator/base.rb +162 -0
  33. data/lib/muding_generator/commands.rb +519 -0
  34. data/lib/muding_generator/generators/applications/app/USAGE +14 -0
  35. data/lib/muding_generator/generators/applications/app/app_generator.rb +132 -0
  36. data/lib/muding_generator/generators/components/controller/USAGE +30 -0
  37. data/lib/muding_generator/generators/components/controller/controller_generator.rb +38 -0
  38. data/lib/muding_generator/generators/components/controller/templates/controller.rb +7 -0
  39. data/lib/muding_generator/generators/components/controller/templates/functional_test.rb +18 -0
  40. data/lib/muding_generator/generators/components/controller/templates/helper.rb +2 -0
  41. data/lib/muding_generator/generators/components/controller/templates/view.rhtml +2 -0
  42. data/lib/muding_generator/generators/components/migration/USAGE +14 -0
  43. data/lib/muding_generator/generators/components/migration/migration_generator.rb +7 -0
  44. data/lib/muding_generator/generators/components/migration/templates/migration.rb +7 -0
  45. data/lib/muding_generator/generators/components/model/USAGE +19 -0
  46. data/lib/muding_generator/generators/components/model/model_generator.rb +34 -0
  47. data/lib/muding_generator/generators/components/model/templates/fixtures.yml +5 -0
  48. data/lib/muding_generator/generators/components/model/templates/migration.rb +11 -0
  49. data/lib/muding_generator/generators/components/model/templates/model.rb +2 -0
  50. data/lib/muding_generator/generators/components/model/templates/unit_test.rb +10 -0
  51. data/lib/muding_generator/lookup.rb +210 -0
  52. data/lib/muding_generator/manifest.rb +53 -0
  53. data/lib/muding_generator/options.rb +140 -0
  54. data/lib/muding_generator/scripts.rb +83 -0
  55. data/lib/muding_generator/scripts/destroy.rb +7 -0
  56. data/lib/muding_generator/scripts/generate.rb +7 -0
  57. data/lib/muding_generator/scripts/update.rb +12 -0
  58. data/lib/muding_generator/simple_logger.rb +46 -0
  59. data/lib/muding_generator/spec.rb +44 -0
  60. data/lib/ruby_version_check.rb +12 -0
  61. data/lib/tasks/migrate.rake +33 -0
  62. metadata +149 -0
@@ -0,0 +1,14 @@
1
+ Description:
2
+ The 'muding' command creates a new MUD with a default
3
+ directory structure and configuration at the path you specify.
4
+
5
+ Example:
6
+ muding ~/Code/Ruby/mymud
7
+
8
+ This generates a skeletal muding installation in ~/Code/Ruby/mymud.
9
+ See the README in the newly created application to get going.
10
+
11
+ WARNING:
12
+ Only specify --without-gems if you did not use gems to install muding.
13
+ Your application will expect to find it's dependencies in the vendor
14
+ directory.
@@ -0,0 +1,132 @@
1
+ require 'rbconfig'
2
+
3
+ class AppGenerator < Muding::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 )
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
+ end
18
+
19
+ def manifest
20
+ # Use /usr/bin/env if no special shebang was specified
21
+ script_options = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
22
+ dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] }
23
+
24
+ record do |m|
25
+ # Root directory and all subdirectories.
26
+ m.directory ''
27
+ BASEDIRS.each { |path| m.directory path }
28
+
29
+ # Root
30
+ m.file "fresh_rakefile", "Rakefile"
31
+ m.file "README", "README"
32
+
33
+ # Application
34
+ m.template "helpers/mud.rb", "mud/controllers/mud.rb"
35
+ m.template "helpers/mud_helper.rb", "mud/helpers/mud_helper.rb"
36
+ m.template "helpers/test_helper.rb", "test/test_helper.rb"
37
+
38
+ # database.yml and .htaccess
39
+ m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => {
40
+ :app_name => File.basename(File.expand_path(@destination_root)),
41
+ :socket => options[:db] == "mysql" ? mysql_socket_location : nil
42
+ }
43
+ #m.template "configs/routes.rb", "config/routes.rb"
44
+ #m.template "configs/apache.conf", "public/.htaccess"
45
+ m.file "configs/boot.rb", "config/boot.rb"
46
+
47
+ # Environments
48
+
49
+ #m.template "environments/environment.rb", "config/environment.rb", :assigns => { :freeze => options[:freeze] }
50
+ #m.file "environments/production.rb", "config/environments/production.rb"
51
+ #m.file "environments/development.rb", "config/environments/development.rb"
52
+ #m.file "environments/test.rb", "config/environments/test.rb"
53
+
54
+ # Scripts
55
+ %w( server generate console destroy ).each do |file|
56
+ m.file "bin/#{file}", "script/#{file}", script_options
57
+ end
58
+
59
+
60
+
61
+ # Docs
62
+ m.file "doc/README_FOR_MUD", "doc/README_FOR_MUD"
63
+
64
+ # Logs
65
+ #%w(server production development test).each { |file|
66
+ # m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666
67
+ #}
68
+ end
69
+ end
70
+
71
+ protected
72
+ def banner
73
+ "Usage: #{$0} /path/to/your/mud [options]"
74
+ end
75
+
76
+ def add_options!(opt)
77
+ opt.separator ''
78
+ opt.separator 'Options:'
79
+ opt.on("-r", "--ruby=path", String,
80
+ "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).",
81
+ "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v }
82
+
83
+ opt.on("-d", "--database=name", String,
84
+ "Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3).",
85
+ "Default: mysql") { |v| options[:db] = v }
86
+
87
+ opt.on("-f", "--freeze",
88
+ "Freeze Muding in vendor/muding from the gems generating the skeleton",
89
+ "Default: false") { |v| options[:freeze] = v }
90
+ end
91
+
92
+ def mysql_socket_location
93
+ RUBY_PLATFORM =~ /mswin32/ ? MYSQL_SOCKET_LOCATIONS.find { |f| File.exists?(f) } : nil
94
+ end
95
+
96
+
97
+ # Installation skeleton. Intermediate directories are automatically
98
+ # created so don't sweat their absence here.
99
+ BASEDIRS = %w(
100
+ mud/controllers
101
+ mud/helpers
102
+ mud/models
103
+ mud/views/layouts
104
+ config
105
+ db
106
+ doc
107
+ lib
108
+ lib/tasks
109
+ log
110
+ script/performance
111
+ script/process
112
+ test/fixtures
113
+ test/functional
114
+ test/integration
115
+ test/mocks/development
116
+ test/mocks/test
117
+ test/unit
118
+ vendor
119
+ vendor/plugins
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
+ ]
132
+ end
@@ -0,0 +1,30 @@
1
+ Description:
2
+ The controller generator creates stubs for a new controller and its views.
3
+
4
+ The generator takes a controller name and a list of views as arguments.
5
+ The controller name may be given in CamelCase or under_score and should
6
+ not be suffixed with 'Controller'. To create a controller within a
7
+ module, specify the controller name as 'module/controller'.
8
+
9
+ The generator creates a controller class in mud/controllers with view
10
+ templates in mud/views/controller_name, a helper class in mud/helpers,
11
+ and a functional test suite in test/functional.
12
+
13
+ Example:
14
+ ./script/generate controller CreditCard open debit credit close
15
+
16
+ Credit card controller with URLs like /credit_card/debit.
17
+ Controller: mud/controllers/credit_card_controller.rb
18
+ Views: mud/views/credit_card/debit.rhtml [...]
19
+ Helper: mud/helpers/credit_card_helper.rb
20
+ Test: test/functional/credit_card_controller_test.rb
21
+
22
+ Modules Example:
23
+ ./script/generate controller 'admin/credit_card' suspend late_fee
24
+
25
+ Credit card admin controller with URLs /admin/credit_card/suspend.
26
+ Controller: mud/controllers/admin/credit_card_controller.rb
27
+ Views: mud/views/admin/credit_card/debit.rhtml [...]
28
+ Helper: mud/helpers/admin/credit_card_helper.rb
29
+ Test: test/functional/admin/credit_card_controller_test.rb
30
+
@@ -0,0 +1,38 @@
1
+ class ControllerGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ # Check for class naming collisions.
5
+ m.class_collisions class_path, "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper"
6
+
7
+ # Controller, helper, views, and test directories.
8
+ m.directory File.join('mud/controllers', class_path)
9
+ m.directory File.join('mud/helpers', class_path)
10
+ m.directory File.join('mud/views', class_path, file_name)
11
+ m.directory File.join('test/functional', class_path)
12
+
13
+ # Controller class, functional test, and helper class.
14
+ m.template 'controller.rb',
15
+ File.join('mud/controllers',
16
+ class_path,
17
+ "#{file_name}_controller.rb")
18
+
19
+ m.template 'functional_test.rb',
20
+ File.join('test/functional',
21
+ class_path,
22
+ "#{file_name}_controller_test.rb")
23
+
24
+ m.template 'helper.rb',
25
+ File.join('mud/helpers',
26
+ class_path,
27
+ "#{file_name}_helper.rb")
28
+
29
+ # View template for each action.
30
+ actions.each do |action|
31
+ path = File.join('mud/views', class_path, file_name, "#{action}.rhtml")
32
+ m.template 'view.rhtml',
33
+ path,
34
+ :assigns => { :action => action, :path => path }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ class <%= class_name %>Controller < MudController
2
+ <% for action in actions -%>
3
+
4
+ def <%= action %>
5
+ end
6
+ <% end -%>
7
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
2
+ require '<%= file_path %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= class_name %>ControllerTest < Test::Unit::TestCase
8
+ def setup
9
+ @controller = <%= class_name %>Controller.new
10
+ @request = ActionController::TestRequest.new
11
+ @response = ActionController::TestResponse.new
12
+ end
13
+
14
+ # Replace this with your real tests.
15
+ def test_truth
16
+ assert true
17
+ end
18
+ end
@@ -0,0 +1,2 @@
1
+ module <%= class_name %>Helper
2
+ end
@@ -0,0 +1,2 @@
1
+ <h1><%= class_name %>#<%= action %></h1>
2
+ <p>Find me in <%= path %></p>
@@ -0,0 +1,14 @@
1
+ Description:
2
+ The migration generator creates a stub for a new database migration.
3
+
4
+ The generator takes a migration name as its argument. The migration name may be
5
+ given in CamelCase or under_score.
6
+
7
+ The generator creates a migration class in db/migrate prefixed by its number
8
+ in the queue.
9
+
10
+ Example:
11
+ ./script/generate migration AddSslFlag
12
+
13
+ With 4 existing migrations, this will create an AddSslFlag migration in the
14
+ file db/migrate/005_add_ssl_flag.rb
@@ -0,0 +1,7 @@
1
+ class MigrationGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'migration.rb', 'db/migrate'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class <%= class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ end
4
+
5
+ def self.down
6
+ end
7
+ end
@@ -0,0 +1,19 @@
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
5
+ given in CamelCase or under_score and should not be suffixed with 'Model'.
6
+
7
+ The generator creates a model class in mud/models, a test suite in
8
+ test/unit, test fixtures in test/fixtures/singular_name.yml, and a migration
9
+ in db/migrate.
10
+
11
+ Example:
12
+ ./script/generate model Account
13
+
14
+ This will create an Account model:
15
+ Model: mud/models/account.rb
16
+ Test: test/unit/account_test.rb
17
+ Fixtures: test/fixtures/accounts.yml
18
+ Migration: db/migrate/XXX_add_accounts.rb
19
+
@@ -0,0 +1,34 @@
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('mud/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('mud/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 add_options!(opt)
29
+ opt.separator ''
30
+ opt.separator 'Options:'
31
+ opt.on("--skip-migration",
32
+ "Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ first:
3
+ id: 1
4
+ another:
5
+ id: 2
@@ -0,0 +1,11 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :<%= table_name %> do |t|
4
+ # t.column :name, :string
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :<%= table_name %>
10
+ end
11
+ 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,210 @@
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
+ Muding::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 Muding
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.append_features(base)
52
+ super
53
+ base.extend(ClassMethods)
54
+ base.use_component_sources!
55
+ end
56
+
57
+ # Convenience method to instantiate another generator.
58
+ def instance(generator_name, args, runtime_options = {})
59
+ self.class.instance(generator_name, args, runtime_options)
60
+ end
61
+
62
+ module ClassMethods
63
+ # The list of sources where we look, in order, for generators.
64
+ def sources
65
+ read_inheritable_attribute(:sources) or use_component_sources!
66
+ end
67
+
68
+ # Add a source to the end of the list.
69
+ def append_sources(*args)
70
+ sources.concat(args.flatten)
71
+ invalidate_cache!
72
+ end
73
+
74
+ # Add a source to the beginning of the list.
75
+ def prepend_sources(*args)
76
+ write_inheritable_array(:sources, args.flatten + sources)
77
+ invalidate_cache!
78
+ end
79
+
80
+ # Reset the source list.
81
+ def reset_sources
82
+ write_inheritable_attribute(:sources, [])
83
+ invalidate_cache!
84
+ end
85
+
86
+ # Use application generators (app, ?).
87
+ def use_application_sources!
88
+ reset_sources
89
+ sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications")
90
+ end
91
+
92
+ # Use component generators (model, controller, etc).
93
+ # 1. Rails application. If RAILS_ROOT is defined we know we're
94
+ # generating in the context of a Rails application, so search
95
+ # RAILS_ROOT/generators.
96
+ # 2. User home directory. Search ~/.rails/generators.
97
+ # 3. RubyGems. Search for gems named *_generator.
98
+ # 4. Builtins. Model, controller, mailer, scaffold.
99
+ def use_component_sources!
100
+ reset_sources
101
+ if defined? ::RAILS_ROOT
102
+ sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators")
103
+ sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators")
104
+ sources << PathSource.new(:plugins, "#{::RAILS_ROOT}/vendor/plugins/**/generators")
105
+ end
106
+ sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
107
+ sources << GemSource.new if Object.const_defined?(:Gem)
108
+ sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
109
+ end
110
+
111
+ # Lookup knows how to find generators' Specs from a list of Sources.
112
+ # Searches the sources, in order, for the first matching name.
113
+ def lookup(generator_name)
114
+ @found ||= {}
115
+ generator_name = generator_name.to_s.downcase
116
+ @found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
117
+ unless @found[generator_name]
118
+ chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
119
+ rx = /^#{chars}$/
120
+ gns = cache.select{|spec| spec.name =~ rx }
121
+ @found[generator_name] ||= gns.first if gns.length == 1
122
+ raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
123
+ end
124
+ @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
125
+ end
126
+
127
+ # Convenience method to lookup and instantiate a generator.
128
+ def instance(generator_name, args = [], runtime_options = {})
129
+ lookup(generator_name).klass.new(args, full_options(runtime_options))
130
+ end
131
+
132
+ private
133
+ # Lookup and cache every generator from the source list.
134
+ def cache
135
+ @cache ||= sources.inject([]) { |cache, source| cache + source.map }
136
+ end
137
+
138
+ # Clear the cache whenever the source list changes.
139
+ def invalidate_cache!
140
+ @cache = nil
141
+ end
142
+ end
143
+ end
144
+
145
+ # Sources enumerate (yield from #each) generator specs which describe
146
+ # where to find and how to create generators. Enumerable is mixed in so,
147
+ # for example, source.collect will retrieve every generator.
148
+ # Sources may be assigned a label to distinguish them.
149
+ class Source
150
+ include Enumerable
151
+
152
+ attr_reader :label
153
+ def initialize(label)
154
+ @label = label
155
+ end
156
+
157
+ # The each method must be implemented in subclasses.
158
+ # The base implementation raises an error.
159
+ def each
160
+ raise NotImplementedError
161
+ end
162
+
163
+ # Return a convenient sorted list of all generator names.
164
+ def names
165
+ map { |spec| spec.name }.sort
166
+ end
167
+ end
168
+
169
+
170
+ # PathSource looks for generators in a filesystem directory.
171
+ class PathSource < Source
172
+ attr_reader :path
173
+
174
+ def initialize(label, path)
175
+ super label
176
+ @path = path
177
+ end
178
+
179
+ # Yield each eligible subdirectory.
180
+ def each
181
+ Dir["#{path}/[a-z]*"].each do |dir|
182
+ if File.directory?(dir)
183
+ yield Spec.new(File.basename(dir), dir, label)
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+
190
+ # GemSource hits the mines to quarry for generators. The latest versions
191
+ # of gems named *_generator are selected.
192
+ class GemSource < Source
193
+ def initialize
194
+ super :RubyGems
195
+ end
196
+
197
+ # Yield latest versions of generator gems.
198
+ def each
199
+ Gem::cache.search(/_generator$/).inject({}) { |latest, gem|
200
+ hem = latest[gem.name]
201
+ latest[gem.name] = gem if hem.nil? or gem.version > hem.version
202
+ latest
203
+ }.values.each { |gem|
204
+ yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
205
+ }
206
+ end
207
+ end
208
+
209
+ end
210
+ end