shattered 0.3

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 (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