muding 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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