shattered 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/bin/console +22 -0
  2. data/bin/generate +7 -0
  3. data/bin/runner +9 -0
  4. data/bin/shatter +29 -0
  5. data/lib/game_loader.rb +49 -0
  6. data/lib/rails_generator.rb +43 -0
  7. data/lib/rails_generator/base.rb +203 -0
  8. data/lib/rails_generator/commands.rb +445 -0
  9. data/lib/rails_generator/generators/applications/shattered_app/USAGE +10 -0
  10. data/lib/rails_generator/generators/applications/shattered_app/shattered_app_generator.rb +104 -0
  11. data/lib/rails_generator/generators/components/actor/actor_generator.rb +23 -0
  12. data/lib/rails_generator/generators/components/controller/USAGE +30 -0
  13. data/lib/rails_generator/generators/components/controller/controller_generator.rb +32 -0
  14. data/lib/rails_generator/generators/components/controller/templates/controller.rb +5 -0
  15. data/lib/rails_generator/generators/components/controller/templates/functional_test.rb +18 -0
  16. data/lib/rails_generator/generators/components/controller/templates/helper.rb +2 -0
  17. data/lib/rails_generator/generators/components/controller/templates/view.rhtml +2 -0
  18. data/lib/rails_generator/generators/components/model/USAGE +17 -0
  19. data/lib/rails_generator/generators/components/model/model_generator.rb +17 -0
  20. data/lib/rails_generator/generators/components/model/templates/fixtures.yml +5 -0
  21. data/lib/rails_generator/generators/components/model/templates/model.rb +2 -0
  22. data/lib/rails_generator/generators/components/model/templates/unit_test.rb +11 -0
  23. data/lib/rails_generator/generators/components/state/USAGE +30 -0
  24. data/lib/rails_generator/generators/components/state/state_generator.rb +19 -0
  25. data/lib/rails_generator/generators/components/state/templates/state.rb +4 -0
  26. data/lib/rails_generator/generators/components/view/USAGE +30 -0
  27. data/lib/rails_generator/generators/components/view/templates/material +4 -0
  28. data/lib/rails_generator/generators/components/view/templates/view.rb +3 -0
  29. data/lib/rails_generator/generators/components/view/view_generator.rb +18 -0
  30. data/lib/rails_generator/lookup.rb +206 -0
  31. data/lib/rails_generator/manifest.rb +53 -0
  32. data/lib/rails_generator/options.rb +135 -0
  33. data/lib/rails_generator/scripts.rb +83 -0
  34. data/lib/rails_generator/scripts/destroy.rb +7 -0
  35. data/lib/rails_generator/scripts/generate.rb +7 -0
  36. data/lib/rails_generator/scripts/update.rb +12 -0
  37. data/lib/rails_generator/simple_logger.rb +46 -0
  38. data/lib/rails_generator/spec.rb +44 -0
  39. data/lib/shatter.rb +11 -0
  40. data/lib/templates/MIT-LICENSE +20 -0
  41. data/lib/templates/README +35 -0
  42. data/lib/templates/Rakefile +15 -0
  43. data/lib/templates/configs/Mac/shattered.app/Contents/Info.plist +22 -0
  44. data/lib/templates/configs/Mac/shattered.app/Contents/MacOS/shattered_mac +0 -0
  45. data/lib/templates/configs/Mac/shattered.app/Contents/PkgInfo +1 -0
  46. data/lib/templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/InfoPlist.strings +0 -0
  47. data/lib/templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib +4 -0
  48. data/lib/templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/info.nib +20 -0
  49. data/lib/templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/objects.nib +0 -0
  50. data/lib/templates/configs/Mac/shattered.app/Contents/Resources/rb_main.rb +3 -0
  51. data/lib/templates/configs/Mac/shattered.app/Contents/pbdevelopment.plist +8 -0
  52. data/lib/templates/configs/Mac/shattered.app/OgreLeaks.log +144 -0
  53. data/lib/templates/configs/Mac/shattered.app/OgreMemory.log +29 -0
  54. data/lib/templates/configs/Mac/shattered.app/config +1 -0
  55. data/lib/templates/configs/boot.rb +33 -0
  56. data/lib/templates/configs/empty.log +0 -0
  57. data/lib/templates/configs/ogre.cfg +4 -0
  58. data/lib/templates/configs/plugins.cfg +16 -0
  59. data/lib/templates/configs/plugins.darwin.cfg +13 -0
  60. data/lib/templates/configs/plugins.linux.cfg +16 -0
  61. data/lib/templates/configs/plugins.win32.cfg +9 -0
  62. data/lib/templates/configs/runner.rb +5 -0
  63. data/lib/templates/doc/README_FOR_APP +2 -0
  64. data/lib/templates/environments/environment.rb +7 -0
  65. data/lib/templates/media/basic.rmaterial +17 -0
  66. data/lib/templates/media/offset_map.rmaterial +117 -0
  67. metadata +194 -0
@@ -0,0 +1,10 @@
1
+ Description:
2
+ The 'shatter' command creates a new Shattered application with a default
3
+ directory structure and configuration at the path you specify.
4
+
5
+ Example:
6
+ shatter ~/Code/Ruby/Quake15
7
+
8
+ This will create a skeletal Shattered application for the next version of
9
+ Quake. See the README in the newly created application to get going.
10
+
@@ -0,0 +1,104 @@
1
+ require 'rbconfig'
2
+
3
+ class ShatteredAppGenerator < Rails::Generator::Base
4
+ DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
5
+ Config::CONFIG['ruby_install_name'])
6
+
7
+ default_options :gem => true, :shebang => DEFAULT_SHEBANG
8
+ mandatory_options :source => "#{File.dirname(__FILE__)}/../../../.."
9
+
10
+ def initialize(runtime_args, runtime_options = {})
11
+ super
12
+ usage if args.empty?
13
+ @destination_root = args.shift
14
+ end
15
+
16
+ def manifest
17
+ script_options = { :chmod => 0755, :shebang => options[:shebang] }
18
+
19
+ record do |m|
20
+ # Root directory and all subdirectories.
21
+ m.directory ''
22
+ BASEDIRS.each { |path| m.directory path }
23
+
24
+ # Root
25
+ m.file "templates/Rakefile", "Rakefile"
26
+ m.file "templates/README", "README"
27
+
28
+ # ogre plugins and configuration
29
+ m.template "templates/configs/plugins.cfg", "config/plugins.cfg"
30
+ m.template "templates/configs/plugins.linux.cfg", "config/plugins.linux.cfg"
31
+ m.template "templates/configs/plugins.darwin.cfg", "config/plugins.darwin.cfg"
32
+ m.template "templates/configs/plugins.win32.cfg", "config/plugins.win32.cfg"
33
+ m.template "templates/configs/ogre.cfg", "config/ogre.cfg"
34
+ m.template "templates/configs/boot.rb", "config/boot.rb"
35
+
36
+ # Environments
37
+ m.file "templates/environments/environment.rb", "config/environment.rb"
38
+
39
+ # Mac OSX
40
+ m.file "templates/configs/Mac/shattered.app/Contents/Info.plist", "config/Mac/shattered.app/Contents/Info.plist"
41
+ m.file "templates/configs/Mac/shattered.app/Contents/MacOS/shattered_mac", "config/Mac/shattered.app/Contents/MacOS/shattered_mac"
42
+ m.file "templates/configs/Mac/shattered.app/Contents/pbdevelopment.plist", "config/Mac/shattered.app/Contents/pbdevelopment.plist"
43
+ m.file "templates/configs/Mac/shattered.app/Contents/PkgInfo", "config/Mac/shattered.app/Contents/PkgInfo"
44
+ m.file "templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/InfoPlist.strings", "config/Mac/shattered.app/Contents/Resources/English.lproj/InfoPlist.strings"
45
+ m.file "templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib", "config/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib"
46
+ m.file "templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/info.nib", "config/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/info.nib"
47
+ m.file "templates/configs/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/objects.nib", "config/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib/objects.nib"
48
+ m.file "templates/configs/Mac/shattered.app/Contents/Resources/rb_main.rb", "config/Mac/shattered.app/Contents/Resources/rb_main.rb"
49
+
50
+ # Scripts
51
+ %w( generate runner console ).each do |file|
52
+ m.file "../bin/#{file}", "script/#{file}", script_options
53
+ end
54
+
55
+ m.file "templates/configs/runner.rb", "script/runner.rb"
56
+
57
+ # Docs
58
+ m.file "templates/doc/README_FOR_APP", "doc/README_FOR_APP"
59
+
60
+ # Logs
61
+ %w(ogre shattered).each { |file|
62
+ m.file "templates/configs/empty.log", "log/#{file}.log", :chmod => 0666
63
+ }
64
+
65
+ # basic rmaterial
66
+ m.file "templates/media/basic.rmaterial", "media/templates/basic.rmaterial"
67
+ m.file "templates/media/offset_map.rmaterial", "media/templates/offset_map.rmaterial"
68
+ end
69
+ end
70
+
71
+ protected
72
+ def banner
73
+ "Usage: #{$0} /path/to/your/app [options]"
74
+ end
75
+
76
+ def add_options!(opt)
77
+ opt.separator ''
78
+ opt.separator 'Options:'
79
+ opt.on("--ruby [#{DEFAULT_SHEBANG}]",
80
+ "Path to the Ruby binary of your choice.") { |options[:shebang]| }
81
+ opt.on("--without-gems",
82
+ "Don't use the Shattered gems for your app.",
83
+ "This does not currently work.") { |options[:gem]| }
84
+ end
85
+
86
+
87
+ # Installation skeleton. Intermediate directories are automatically
88
+ # created so don't sweat their absence here.
89
+ BASEDIRS = %w(
90
+ app/controllers
91
+ app/models
92
+ app/views
93
+ media/programs
94
+ media/templates
95
+ config/Mac/shattered.app/Contents/MacOS
96
+ config/Mac/shattered.app/Contents/Resources/English.lproj/MainMenu.nib
97
+ doc
98
+ log
99
+ script
100
+ test/unit
101
+ vendor
102
+ vendor/plugins
103
+ )
104
+ end
@@ -0,0 +1,23 @@
1
+ # require File.dirname(__FILE__) + '/../config/environment'
2
+ # require 'rails_generator'
3
+ # require 'rails_generator/scripts/generate'
4
+
5
+ # ARGV.shift if ['--help', '-h'].include?(ARGV[0])
6
+ # Rails::Generator::Scripts::Generate.new.run(ARGV)
7
+ require File.dirname(__FILE__) + '/../model/model_generator'
8
+ require File.dirname(__FILE__) + '/../view/view_generator'
9
+ require File.dirname(__FILE__) + '/../controller/controller_generator'
10
+
11
+ class ActorGenerator < Rails::Generator::NamedBase
12
+ def manifest
13
+ model=["model", file_name]
14
+ view=["view", file_name]
15
+ controller=["controller", file_name]
16
+ Rails::Generator::Scripts::Generate.new.run(model)
17
+ Rails::Generator::Scripts::Generate.new.run(view)
18
+ Rails::Generator::Scripts::Generate.new.run(controller)
19
+
20
+ record do |m|
21
+ end
22
+ end
23
+ 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 app/controllers with view
10
+ templates in app/views/controller_name, a helper class in app/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: app/controllers/credit_card_controller.rb
18
+ Views: app/views/credit_card/debit.rhtml [...]
19
+ Helper: app/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: app/controllers/admin/credit_card_controller.rb
27
+ Views: app/views/admin/credit_card/debit.rhtml [...]
28
+ Helper: app/helpers/admin/credit_card_helper.rb
29
+ Test: test/functional/admin/credit_card_controller_test.rb
30
+
@@ -0,0 +1,32 @@
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('app/controllers', class_path)
9
+ #m.directory File.join('app/helpers', class_path)
10
+ #m.directory File.join('app/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('app/controllers',
16
+ class_path,
17
+ "#{file_name}_controller.rb")
18
+
19
+ # Functional tests are a good idea, but not yet encompassed
20
+ # m.template 'functional_test.rb',
21
+ # File.join('test/functional',
22
+ # class_path,
23
+ # "#{file_name}_controller_test.rb")
24
+
25
+ # m.template 'helper.rb',
26
+ # File.join('app/helpers',
27
+ # class_path,
28
+ # "#{file_name}_helper.rb")
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ class <%= class_name %>Controller < ShatteredController::Base
2
+ key :pressed => :w, :action => :move_forward
3
+ def move_forward
4
+ end
5
+ 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,17 @@
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 app/models, a test suite in
8
+ test/unit, and test fixtures in test/fixtures/singular_name.yml.
9
+
10
+ Example:
11
+ ./script/generate model Account
12
+
13
+ This will create an Account model:
14
+ Model: app/models/account.rb
15
+ Test: test/unit/account_test.rb
16
+ Fixtures: test/fixtures/accounts.yml
17
+
@@ -0,0 +1,17 @@
1
+ class ModelGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ # Check for class naming collisions.
5
+ m.class_collisions class_path, class_name, "#{class_name}Test"
6
+
7
+ # Model, test, and fixture directories.
8
+ m.directory File.join('app/models', class_path)
9
+ m.directory File.join('test/unit', class_path)
10
+ m.directory File.join('test/fixtures', class_path)
11
+
12
+ # Model class, unit test, and fixtures.
13
+ m.template 'model.rb', File.join('app/models', class_path, "#{file_name}_model.rb")
14
+ m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ first_<%= singular_name %>:
3
+ id: 1
4
+ another_<%= singular_name %>:
5
+ id: 2
@@ -0,0 +1,2 @@
1
+ class <%= class_name %>Model < ShatteredModel::Base
2
+ end
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class <%= class_name %>Test < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ # Replace this with your real tests.
9
+ def test_truth
10
+ end
11
+ 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 app/controllers with view
10
+ templates in app/views/controller_name, a helper class in app/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: app/controllers/credit_card_controller.rb
18
+ Views: app/views/credit_card/debit.rhtml [...]
19
+ Helper: app/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: app/controllers/admin/credit_card_controller.rb
27
+ Views: app/views/admin/credit_card/debit.rhtml [...]
28
+ Helper: app/helpers/admin/credit_card_helper.rb
29
+ Test: test/functional/admin/credit_card_controller_test.rb
30
+
@@ -0,0 +1,19 @@
1
+ class StateGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ # Check for class naming collisions.
5
+ m.class_collisions class_path, "#{class_name}State"
6
+
7
+ # Controller, helper, views, and test directories.
8
+ m.directory File.join('app/states', class_path)
9
+
10
+ # Controller class, functional test, and helper class.
11
+ m.template 'state.rb',
12
+ File.join('app/states',
13
+ class_path,
14
+ "#{file_name}_state.rb")
15
+
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ class <%= class_name %>State < ShatteredController::State
2
+ def update(time_elapsed)
3
+ end
4
+ 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 app/controllers with view
10
+ templates in app/views/controller_name, a helper class in app/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: app/controllers/credit_card_controller.rb
18
+ Views: app/views/credit_card/debit.rhtml [...]
19
+ Helper: app/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: app/controllers/admin/credit_card_controller.rb
27
+ Views: app/views/admin/credit_card/debit.rhtml [...]
28
+ Helper: app/helpers/admin/credit_card_helper.rb
29
+ Test: test/functional/admin/credit_card_controller_test.rb
30
+
@@ -0,0 +1,4 @@
1
+ <%=class_name%>Material
2
+ {
3
+
4
+ }
@@ -0,0 +1,3 @@
1
+ class <%= class_name %>View < ShatteredView::Base
2
+ mesh "<%= file_name %>"
3
+ end
@@ -0,0 +1,18 @@
1
+ class ViewGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ # Check for class naming collisions.
5
+ m.class_collisions class_path, "#{class_name}View"
6
+
7
+ # Controller, helper, views, and test directories.
8
+ m.directory File.join('app/views', class_path)
9
+ m.directory File.join('app/views', class_path, file_name)
10
+
11
+ # Controller class, functional test, and helper class.
12
+ m.template 'view.rb',
13
+ File.join('app/views',
14
+ class_path,
15
+ "#{file_name}_view.rb")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,206 @@
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 SHATTERED_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.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 SHATTERED_ROOT is defined we know we're
94
+ # generating in the context of a Rails application, so search
95
+ # SHATTERED_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
+ sources << PathSource.new(:app, "#{::SHATTERED_ROOT}/generators") if defined? ::SHATTERED_ROOT
102
+ sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators")
103
+ sources << GemSource.new if Object.const_defined?(:Gem)
104
+ sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components")
105
+ end
106
+
107
+ # Lookup knows how to find generators' Specs from a list of Sources.
108
+ # Searches the sources, in order, for the first matching name.
109
+ def lookup(generator_name)
110
+ @found ||= {}
111
+ generator_name = generator_name.to_s.downcase
112
+ @found[generator_name] ||= cache.find { |spec| spec.name == generator_name }
113
+ unless @found[generator_name]
114
+ chars = generator_name.scan(/./).map{|c|"#{c}.*?"}
115
+ rx = /^#{chars}$/
116
+ gns = cache.select{|spec| spec.name =~ rx }
117
+ @found[generator_name] ||= gns.first if gns.length == 1
118
+ raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1
119
+ end
120
+ @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator"
121
+ end
122
+
123
+ # Convenience method to lookup and instantiate a generator.
124
+ def instance(generator_name, args = [], runtime_options = {})
125
+ lookup(generator_name).klass.new(args, full_options(runtime_options))
126
+ end
127
+
128
+ private
129
+ # Lookup and cache every generator from the source list.
130
+ def cache
131
+ @cache ||= sources.inject([]) { |cache, source| cache + source.map }
132
+ end
133
+
134
+ # Clear the cache whenever the source list changes.
135
+ def invalidate_cache!
136
+ @cache = nil
137
+ end
138
+ end
139
+ end
140
+
141
+ # Sources enumerate (yield from #each) generator specs which describe
142
+ # where to find and how to create generators. Enumerable is mixed in so,
143
+ # for example, source.collect will retrieve every generator.
144
+ # Sources may be assigned a label to distinguish them.
145
+ class Source
146
+ include Enumerable
147
+
148
+ attr_reader :label
149
+ def initialize(label)
150
+ @label = label
151
+ end
152
+
153
+ # The each method must be implemented in subclasses.
154
+ # The base implementation raises an error.
155
+ def each
156
+ raise NotImplementedError
157
+ end
158
+
159
+ # Return a convenient sorted list of all generator names.
160
+ def names
161
+ map { |spec| spec.name }.sort
162
+ end
163
+ end
164
+
165
+
166
+ # PathSource looks for generators in a filesystem directory.
167
+ class PathSource < Source
168
+ attr_reader :path
169
+
170
+ def initialize(label, path)
171
+ super label
172
+ @path = path
173
+ end
174
+
175
+ # Yield each eligible subdirectory.
176
+ def each
177
+ Dir["#{path}/[a-z]*"].each do |dir|
178
+ if File.directory?(dir)
179
+ yield Spec.new(File.basename(dir), dir, label)
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+
186
+ # GemSource hits the mines to quarry for generators. The latest versions
187
+ # of gems named *_generator are selected.
188
+ class GemSource < Source
189
+ def initialize
190
+ super :RubyGems
191
+ end
192
+
193
+ # Yield latest versions of generator gems.
194
+ def each
195
+ Gem::cache.search(/_generator$/).inject({}) { |latest, gem|
196
+ hem = latest[gem.name]
197
+ latest[gem.name] = gem if hem.nil? or gem.version > hem.version
198
+ latest
199
+ }.values.each { |gem|
200
+ yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label)
201
+ }
202
+ end
203
+ end
204
+
205
+ end
206
+ end