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,22 @@
1
+ #!/usr/bin/ruby18
2
+ irb = RUBY_PLATFORM =~ /mswin32/ ? 'irb.bat' : 'irb'
3
+
4
+ require 'optparse'
5
+ options = { :sandbox => false, :irb => irb }
6
+ OptionParser.new do |opt|
7
+ opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |options[:sandbox]| }
8
+ opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |options[:irb]| }
9
+ opt.parse!(ARGV)
10
+ end
11
+
12
+ libs = " -r irb/completion"
13
+ libs << " -r #{File.dirname(__FILE__)}/../config/environment"
14
+ libs << " -r console_sandbox" if options[:sandbox]
15
+
16
+ if options[:sandbox]
17
+ puts "Loading #{ENV['RAILS_ENV']} environment in sandbox."
18
+ puts "Any modifications you make will be rolled back on exit."
19
+ else
20
+ puts "Loading #{ENV['RAILS_ENV']} environment."
21
+ end
22
+ exec "#{options[:irb]} #{libs} --prompt-mode simple"
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby18
2
+ require File.dirname(__FILE__)+"/../config/boot"
3
+ require 'rails_generator'
4
+ require 'rails_generator/scripts/generate'
5
+
6
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
7
+ Rails::Generator::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/ruby18
2
+
3
+ if PLATFORM =~ /darwin/
4
+ path = File.dirname(__FILE__) + "/../config/Mac/"
5
+ puts `(cd #{path}shattered.app; ./Contents/MacOS/shattered_mac)`
6
+ else
7
+ require File.dirname(__FILE__)+'/runner'
8
+ start_game
9
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ #This file is adapted from the rails command.
3
+
4
+ begin
5
+ require 'active_support'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require_gem 'activesupport'
9
+ end
10
+
11
+ min_release = "1.8.2 (2004-12-25)"
12
+ ruby_release = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
13
+
14
+ if ruby_release < min_release
15
+ abort <<-end_message
16
+
17
+ Rails requires Ruby version #{min_release} or later.
18
+ You're running #{ruby_release}; please upgrade to continue.
19
+
20
+ end_message
21
+ end
22
+
23
+ Signal.trap("INT") { puts; exit }
24
+
25
+
26
+ require File.dirname(__FILE__) + '/../lib/rails_generator'
27
+ require 'rails_generator/scripts/generate'
28
+ Rails::Generator::Base.use_application_sources!
29
+ Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'shattered_app')
@@ -0,0 +1,49 @@
1
+ require 'singleton'
2
+
3
+ begin
4
+ require 'shattered_controller'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'shattered_controller'
8
+ end
9
+ require 'shattered_view'
10
+ require 'shattered_model'
11
+
12
+ include ShatteredController
13
+ include ShatteredView
14
+ include ShatteredModel
15
+
16
+ module Shatter
17
+ #This class is loads the view, controller, and model. It then loads
18
+ #and starts the game based on these.
19
+ class GameLoader
20
+ include Singleton
21
+ attr_writer :environment
22
+ def require_all_files
23
+ Dir.foreach("#{SHATTERED_ROOT}/app/") do |type|
24
+ next if File.file?("#{SHATTERED_ROOT}/app/#{type}")
25
+ next if type =~ /view/ or type =~ /state/
26
+ Dir.foreach("#{SHATTERED_ROOT}/app/#{type}") do |file|
27
+ require "#{SHATTERED_ROOT}/app/#{type}/#{file}" if file =~ /\.rb$/
28
+ end
29
+ end
30
+ end
31
+ def run
32
+ view = ShatteredView::Runner.new @environment
33
+ view.add_to_environment @environment
34
+ control = ShatteredController::Runner.new @environment
35
+ ShatteredSupport::Configuration.environment=@environment
36
+
37
+ puts "require '#{SHATTERED_ROOT}/app/states/#{@environment[:start_state].to_s}_state'"
38
+ begin
39
+ eval "require '#{SHATTERED_ROOT}/app/states/#{@environment[:start_state].to_s}_state'"
40
+ rescue StandardError => output
41
+ puts output.message
42
+ end
43
+ puts "creating #{@environment[:start_state].to_s.camelize}State.new"
44
+
45
+ eval "#{@environment[:start_state].to_s.camelize}State.new"
46
+ control.start_game
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,43 @@
1
+ #--
2
+ # Copyright (c) 2004 Jeremy Kemper
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ $:.unshift(File.dirname(__FILE__))
25
+ $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
26
+
27
+ begin
28
+ require 'active_support'
29
+ rescue LoadError
30
+ require 'rubygems'
31
+ require_gem 'activesupport'
32
+ end
33
+
34
+ require 'rails_generator/base'
35
+ require 'rails_generator/lookup'
36
+ require 'rails_generator/commands'
37
+
38
+ Rails::Generator::Base.send(:include, Rails::Generator::Lookup)
39
+ Rails::Generator::Base.send(:include, Rails::Generator::Commands)
40
+
41
+ # Set up a default logger for convenience.
42
+ require 'rails_generator/simple_logger'
43
+ Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(STDOUT)
@@ -0,0 +1,203 @@
1
+ require File.dirname(__FILE__) + '/options'
2
+ require File.dirname(__FILE__) + '/manifest'
3
+ require File.dirname(__FILE__) + '/spec'
4
+
5
+ # Rails::Generator is a code generation platform tailored for the Rails
6
+ # web application framework. Generators are easily invoked within Rails
7
+ # applications to add and remove components such as models and controllers.
8
+ # New generators are easy to create and may be distributed as RubyGems or
9
+ # tarballs for inclusion system-wide, per-user, or per-application.
10
+ #
11
+ # Generators may subclass other generators to provide variations that
12
+ # require little or no new logic but replace the template files.
13
+ # The postback generator is an example: it subclasses the scaffold
14
+ # generator and just replaces the code templates with its own.
15
+ #
16
+ # Now go forth and multiply^Wgenerate.
17
+ module Rails
18
+ module Generator
19
+ class GeneratorError < StandardError; end
20
+ class UsageError < GeneratorError; end
21
+
22
+
23
+ # The base code generator is bare-bones. It sets up the source and
24
+ # destination paths and tells the logger whether to keep its trap shut.
25
+ # You're probably looking for NamedBase, a subclass meant for generating
26
+ # "named" components such as models, controllers, and mailers.
27
+ #
28
+ # Generators create a manifest of the actions they perform then hand
29
+ # the manifest to a command which replay the actions to do the heavy
30
+ # lifting. Create, destroy, and list commands are included. Since a
31
+ # single manifest may be used by any command, creating new generators is
32
+ # as simple as writing some code templates and declaring what you'd like
33
+ # to do with them.
34
+ #
35
+ # The manifest method must be implemented by subclasses, returning a
36
+ # Rails::Generator::Manifest. The record method is provided as a
37
+ # convenience for manifest creation. Example:
38
+ # class EliteGenerator < Rails::Generator::Base
39
+ # def manifest
40
+ # record do |m|
41
+ # m.do(some)
42
+ # m.things(in) { here }
43
+ # end
44
+ # end
45
+ # end
46
+ class Base
47
+ include Options
48
+
49
+ # Declare default options for the generator. These options
50
+ # are inherited to subclasses.
51
+ default_options :collision => :ask, :quiet => false
52
+
53
+ # A logger instance available everywhere in the generator.
54
+ cattr_accessor :logger
55
+
56
+ # Every generator that is dynamically looked up is tagged with a
57
+ # Spec describing where it was found.
58
+ class_inheritable_accessor :spec
59
+
60
+ attr_reader :source_root, :destination_root, :args
61
+
62
+ def initialize(runtime_args, runtime_options = {})
63
+ @args = runtime_args
64
+ parse!(@args, runtime_options)
65
+
66
+ # Derive source and destination paths.
67
+ @source_root = options[:source] || File.join(spec.path, 'templates')
68
+ if options[:destination]
69
+ @destination_root = options[:destination]
70
+ elsif defined? ::SHATTERED_ROOT
71
+ @destination_root = ::SHATTERED_ROOT
72
+ end
73
+
74
+ # Silence the logger if requested.
75
+ logger.quiet = options[:quiet]
76
+
77
+ # Raise usage error if help is requested.
78
+ usage if options[:help]
79
+ end
80
+
81
+ # Generators must provide a manifest. Use the record method to create
82
+ # a new manifest and record your generator's actions.
83
+ def manifest
84
+ raise NotImplementedError, "No manifest for '#{spec.name}' generator."
85
+ end
86
+
87
+ # Return the full path from the source root for the given path.
88
+ # Example for source_root = '/source':
89
+ # source_path('some/path.rb') == '/source/some/path.rb'
90
+ #
91
+ # The given path may include a colon ':' character to indicate that
92
+ # the file belongs to another generator. This notation allows any
93
+ # generator to borrow files from another. Example:
94
+ # source_path('model:fixture.yml') = '/model/source/path/fixture.yml'
95
+ def source_path(relative_source)
96
+ # Check whether we're referring to another generator's file.
97
+ name, path = relative_source.split(':', 2)
98
+
99
+ # If not, return the full path to our source file.
100
+ if path.nil?
101
+ File.join(source_root, name)
102
+
103
+ # Otherwise, ask our referral for the file.
104
+ else
105
+ # FIXME: this is broken, though almost always true. Others'
106
+ # source_root are not necessarily the templates dir.
107
+ File.join(self.class.lookup(name).path, 'templates', path)
108
+ end
109
+ end
110
+
111
+ # Return the full path from the destination root for the given path.
112
+ # Example for destination_root = '/dest':
113
+ # destination_path('some/path.rb') == '/dest/some/path.rb'
114
+ def destination_path(relative_destination)
115
+ File.join(destination_root, relative_destination)
116
+ end
117
+
118
+ protected
119
+ # Convenience method for generator subclasses to record a manifest.
120
+ def record
121
+ Rails::Generator::Manifest.new(self) { |m| yield m }
122
+ end
123
+
124
+ # Override with your own usage banner.
125
+ def banner
126
+ "Usage: #{$0} #{spec.name} [options]"
127
+ end
128
+
129
+ # Read USAGE from file in generator base path.
130
+ def usage_message
131
+ File.read(File.join(spec.path, 'USAGE')) rescue ''
132
+ end
133
+ end
134
+
135
+
136
+ # The base generator for named components: models, controllers, mailers,
137
+ # etc. The target name is taken as the first argument and inflected to
138
+ # singular, plural, class, file, and table forms for your convenience.
139
+ # The remaining arguments are aliased to actions for controller and
140
+ # mailer convenience.
141
+ #
142
+ # If no name is provided, the generator raises a usage error with content
143
+ # optionally read from the USAGE file in the generator's base path.
144
+ #
145
+ # See Rails::Generator::Base for a discussion of Manifests and Commands.
146
+ class NamedBase < Base
147
+ attr_reader :name, :class_name, :singular_name, :plural_name
148
+ attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth
149
+ alias_method :file_name, :singular_name
150
+ alias_method :table_name, :plural_name
151
+ alias_method :actions, :args
152
+
153
+ def initialize(runtime_args, runtime_options = {})
154
+ super
155
+
156
+ # Name argument is required.
157
+ usage if runtime_args.empty?
158
+
159
+ @args = runtime_args.dup
160
+ base_name = @args.shift
161
+ assign_names!(base_name)
162
+ end
163
+
164
+ protected
165
+ # Override with your own usage banner.
166
+ def banner
167
+ "Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]"
168
+ end
169
+
170
+ private
171
+ def assign_names!(name)
172
+ @name = name
173
+ base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name)
174
+ @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
175
+ if @class_nesting.empty?
176
+ @class_name = @class_name_without_nesting
177
+ else
178
+ @class_name = "#{@class_nesting}::#{@class_name_without_nesting}"
179
+ end
180
+ end
181
+
182
+ # Extract modules from filesystem-style or ruby-style path:
183
+ # good/fun/stuff
184
+ # Good::Fun::Stuff
185
+ # produce the same results.
186
+ def extract_modules(name)
187
+ modules = name.include?('/') ? name.split('/') : name.split('::')
188
+ name = modules.pop
189
+ path = modules.map { |m| m.underscore }
190
+ file_path = (path + [name.underscore]).join('/')
191
+ nesting = modules.map { |m| m.camelize }.join('::')
192
+ [name, path, file_path, nesting, modules.size]
193
+ end
194
+
195
+ def inflect_names(name)
196
+ camel = name.camelize
197
+ under = camel.underscore
198
+ plural = under.pluralize
199
+ [camel, under, plural]
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,445 @@
1
+ require 'delegate'
2
+ require 'optparse'
3
+ require 'fileutils'
4
+ require 'erb'
5
+
6
+ module Rails
7
+ module Generator
8
+ module Commands
9
+ # Here's a convenient way to get a handle on generator commands.
10
+ # Command.instance('destroy', my_generator) instantiates a Destroy
11
+ # delegate of my_generator ready to do your dirty work.
12
+ def self.instance(command, generator)
13
+ const_get(command.to_s.camelize).new(generator)
14
+ end
15
+
16
+ # Even more convenient access to commands. Include Commands in
17
+ # the generator Base class to get a nice #command instance method
18
+ # which returns a delegate for the requested command.
19
+ def self.append_features(base)
20
+ base.send(:define_method, :command) do |command|
21
+ Commands.instance(command, self)
22
+ end
23
+ end
24
+
25
+
26
+ # Generator commands delegate Rails::Generator::Base and implement
27
+ # a standard set of actions. Their behavior is defined by the way
28
+ # they respond to these actions: Create brings life; Destroy brings
29
+ # death; List passively observes.
30
+ #
31
+ # Commands are invoked by replaying (or rewinding) the generator's
32
+ # manifest of actions. See Rails::Generator::Manifest and
33
+ # Rails::Generator::Base#manifest method that generator subclasses
34
+ # are required to override.
35
+ #
36
+ # Commands allows generators to "plug in" invocation behavior, which
37
+ # corresponds to the GoF Strategy pattern.
38
+ class Base < DelegateClass(Rails::Generator::Base)
39
+ # Replay action manifest. RewindBase subclass rewinds manifest.
40
+ def invoke!
41
+ manifest.replay(self)
42
+ end
43
+
44
+ def dependency(generator_name, args, runtime_options = {})
45
+ logger.dependency(generator_name) do
46
+ self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke!
47
+ end
48
+ end
49
+
50
+ # Does nothing for all commands except Create.
51
+ def class_collisions(*class_names)
52
+ end
53
+
54
+ # Does nothing for all commands except Create.
55
+ def readme(*args)
56
+ end
57
+
58
+ private
59
+ # Ask the user interactively whether to force collision.
60
+ def force_file_collision?(destination)
61
+ $stdout.print "overwrite #{destination}? [Ynaq] "
62
+ case $stdin.gets
63
+ when /a/i
64
+ $stdout.puts "forcing #{spec.name}"
65
+ options[:collision] = :force
66
+ when /q/i
67
+ $stdout.puts "aborting #{spec.name}"
68
+ raise SystemExit
69
+ when /n/i then :skip
70
+ else :force
71
+ end
72
+ rescue
73
+ retry
74
+ end
75
+
76
+ def render_template_part(template_options)
77
+ # Getting Sandbox to evaluate part template in it
78
+ part_binding = template_options[:sandbox].call.sandbox_binding
79
+ part_rel_path = template_options[:insert]
80
+ part_path = source_path(part_rel_path)
81
+
82
+ # Render inner template within Sandbox binding
83
+ rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding)
84
+ begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id])
85
+ end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id])
86
+ begin_mark + rendered_part + end_mark
87
+ end
88
+
89
+ def template_part_mark(name, id)
90
+ "<!--[#{name}:#{id}]-->\n"
91
+ end
92
+ end
93
+
94
+ # Base class for commands which handle generator actions in reverse, such as Destroy.
95
+ class RewindBase < Base
96
+ # Rewind action manifest.
97
+ def invoke!
98
+ manifest.rewind(self)
99
+ end
100
+ end
101
+
102
+
103
+ # Create is the premier generator command. It copies files, creates
104
+ # directories, renders templates, and more.
105
+ class Create < Base
106
+
107
+ # Check whether the given class names are already taken by
108
+ # Ruby or Rails. In the future, expand to check other namespaces
109
+ # such as the rest of the user's app.
110
+ def class_collisions(*class_names)
111
+ class_names.flatten.each do |class_name|
112
+ # Convert to string to allow symbol arguments.
113
+ class_name = class_name.to_s
114
+
115
+ # Skip empty strings.
116
+ next if class_name.strip.empty?
117
+
118
+ # Split the class from its module nesting.
119
+ nesting = class_name.split('::')
120
+ name = nesting.pop
121
+
122
+ # Extract the last Module in the nesting.
123
+ last = nesting.inject(Object) { |last, nest|
124
+ break unless last.const_defined?(nest)
125
+ last.const_get(nest)
126
+ }
127
+
128
+ # If the last Module exists, check whether the given
129
+ # class exists and raise a collision if so.
130
+ if last and last.const_defined?(name.camelize)
131
+ raise_class_collision(class_name)
132
+ end
133
+ end
134
+ end
135
+
136
+ # Copy a file from source to destination with collision checking.
137
+ #
138
+ # The file_options hash accepts :chmod and :shebang options.
139
+ # :chmod sets the permissions of the destination file:
140
+ # file 'config/empty.log', 'log/test.log', :chmod => 0664
141
+ # :shebang sets the #!/usr/bin/ruby line for scripts
142
+ # file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby'
143
+ #
144
+ # Collisions are handled by checking whether the destination file
145
+ # exists and either skipping the file, forcing overwrite, or asking
146
+ # the user what to do.
147
+ def file(relative_source, relative_destination, file_options = {})
148
+ # Determine full paths for source and destination files.
149
+ source = source_path(relative_source)
150
+ destination = destination_path(relative_destination)
151
+
152
+ # Check for and resolve file collisions.
153
+ if File.exists?(destination)
154
+
155
+ # Make a choice whether to overwrite the file. :force and
156
+ # :skip already have their mind made up, but give :ask a shot.
157
+ choice = case options[:collision].to_sym #|| :ask
158
+ when :ask then force_file_collision?(relative_destination)
159
+ when :force then :force
160
+ when :skip then :skip
161
+ else raise "Invalid collision option: #{options[:collision].inspect}"
162
+ end
163
+
164
+ # Take action based on our choice. Bail out if we chose to
165
+ # skip the file; otherwise, log our transgression and continue.
166
+ case choice
167
+ when :force then logger.force(relative_destination)
168
+ when :skip then return(logger.skip(relative_destination))
169
+ else raise "Invalid collision choice: #{choice}.inspect"
170
+ end
171
+
172
+ # File doesn't exist so log its unbesmirched creation.
173
+ else
174
+ logger.create relative_destination
175
+ end
176
+
177
+ # If we're pretending, back off now.
178
+ return if options[:pretend]
179
+
180
+ # Write destination file with optional shebang. Yield for content
181
+ # if block given so templaters may render the source file. If a
182
+ # shebang is requested, replace the existing shebang or insert a
183
+ # new one.
184
+ File.open(destination, 'w') do |df|
185
+ File.open(source) do |sf|
186
+ if block_given?
187
+ df.write(yield(sf))
188
+ else
189
+ line = sf.gets
190
+ if file_options[:shebang]
191
+ df.puts("#!#{file_options[:shebang]}")
192
+ df.puts(line) if line !~ /^#!/
193
+ else
194
+ df.puts(line)
195
+ end
196
+ df.write(sf.read)
197
+ end
198
+ end
199
+ end
200
+
201
+ # Optionally change permissions.
202
+ if file_options[:chmod]
203
+ FileUtils.chmod(file_options[:chmod], destination)
204
+ end
205
+
206
+ # Optionally add file to subversion
207
+ system("svn add #{destination}") if options[:svn]
208
+ end
209
+
210
+ # Generate a file for a Rails application using an ERuby template.
211
+ # Looks up and evalutes a template by name and writes the result.
212
+ #
213
+ # The ERB template uses explicit trim mode to best control the
214
+ # proliferation of whitespace in generated code. <%- trims leading
215
+ # whitespace; -%> trims trailing whitespace including one newline.
216
+ #
217
+ # A hash of template options may be passed as the last argument.
218
+ # The options accepted by the file are accepted as well as :assigns,
219
+ # a hash of variable bindings. Example:
220
+ # template 'foo', 'bar', :assigns => { :action => 'view' }
221
+ #
222
+ # Template is implemented in terms of file. It calls file with a
223
+ # block which takes a file handle and returns its rendered contents.
224
+ def template(relative_source, relative_destination, template_options = {})
225
+ file(relative_source, relative_destination, template_options) do |file|
226
+ # Evaluate any assignments in a temporary, throwaway binding.
227
+ vars = template_options[:assigns] || {}
228
+ b = binding
229
+ vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
230
+
231
+ # Render the source file with the temporary binding.
232
+ ERB.new(file.read, nil, '-').result(b)
233
+ end
234
+ end
235
+
236
+ def complex_template(relative_source, relative_destination, template_options = {})
237
+ options = template_options.dup
238
+ options[:assigns] ||= {}
239
+ options[:assigns]['template_for_inclusion'] = render_template_part(template_options)
240
+ template(relative_source, relative_destination, options)
241
+ end
242
+
243
+ # Create a directory including any missing parent directories.
244
+ # Always directories which exist.
245
+ def directory(relative_path)
246
+ path = destination_path(relative_path)
247
+ if File.exists?(path)
248
+ logger.exists relative_path
249
+ else
250
+ logger.create relative_path
251
+ FileUtils.mkdir_p(path) unless options[:pretend]
252
+
253
+ # Optionally add file to subversion
254
+ system("svn add #{path}") if options[:svn]
255
+ end
256
+ end
257
+
258
+ # Display a README.
259
+ def readme(*relative_sources)
260
+ relative_sources.flatten.each do |relative_source|
261
+ logger.readme relative_source
262
+ puts File.read(source_path(relative_source)) unless options[:pretend]
263
+ end
264
+ end
265
+
266
+ private
267
+ # Raise a usage error with an informative WordNet suggestion.
268
+ # Thanks to Florian Gross (flgr).
269
+ def raise_class_collision(class_name)
270
+ message = <<end_message
271
+ The name '#{class_name}' is reserved by Ruby on Rails.
272
+ Please choose an alternative and run this generator again.
273
+ end_message
274
+ if suggest = find_synonyms(class_name)
275
+ message << "\n Suggestions: \n\n"
276
+ message << suggest.join("\n")
277
+ end
278
+ raise UsageError, message
279
+ end
280
+
281
+ SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/cgi-bin/webwn2.0?stage=2&word=%s&posnumber=1&searchtypenumber=2&senses=&showglosses=1"
282
+
283
+ # Look up synonyms on WordNet. Thanks to Florian Gross (flgr).
284
+ def find_synonyms(word)
285
+ require 'open-uri'
286
+ require 'timeout'
287
+ timeout(5) do
288
+ open(SYNONYM_LOOKUP_URI % word) do |stream|
289
+ data = stream.read.gsub("&nbsp;", " ").gsub("<BR>", "")
290
+ data.scan(/^Sense \d+\n.+?\n\n/m)
291
+ end
292
+ end
293
+ rescue Exception
294
+ return nil
295
+ end
296
+ end
297
+
298
+
299
+ # Undo the actions performed by a generator. Rewind the action
300
+ # manifest and attempt to completely erase the results of each action.
301
+ class Destroy < RewindBase
302
+ # Remove a file if it exists and is a file.
303
+ def file(relative_source, relative_destination, file_options = {})
304
+ destination = destination_path(relative_destination)
305
+ if File.exists?(destination)
306
+ logger.rm relative_destination
307
+ unless options[:pretend]
308
+ if options[:svn]
309
+ # If the file has been marked to be added
310
+ # but has not yet been checked in, revert and delete
311
+ if options[:svn][relative_destination]
312
+ system("svn revert #{destination}")
313
+ FileUtils.rm(destination)
314
+ else
315
+ # If the directory is not in the status list, it
316
+ # has no modifications so we can simply remove it
317
+ system("svn rm #{destination}")
318
+ end
319
+ else
320
+ FileUtils.rm(destination)
321
+ end
322
+ end
323
+ else
324
+ logger.missing relative_destination
325
+ return
326
+ end
327
+ end
328
+
329
+ # Templates are deleted just like files and the actions take the
330
+ # same parameters, so simply alias the file method.
331
+ alias_method :template, :file
332
+
333
+ # Remove each directory in the given path from right to left.
334
+ # Remove each subdirectory if it exists and is a directory.
335
+ def directory(relative_path)
336
+ parts = relative_path.split('/')
337
+ until parts.empty?
338
+ partial = File.join(parts)
339
+ path = destination_path(partial)
340
+ if File.exists?(path)
341
+ if Dir[File.join(path, '*')].empty?
342
+ logger.rmdir partial
343
+ unless options[:pretend]
344
+ if options[:svn]
345
+ # If the directory has been marked to be added
346
+ # but has not yet been checked in, revert and delete
347
+ if options[:svn][relative_path]
348
+ system("svn revert #{path}")
349
+ FileUtils.rmdir(path)
350
+ else
351
+ # If the directory is not in the status list, it
352
+ # has no modifications so we can simply remove it
353
+ system("svn rm #{path}")
354
+ end
355
+ else
356
+ FileUtils.rmdir(path)
357
+ end
358
+ end
359
+ else
360
+ logger.notempty partial
361
+ end
362
+ else
363
+ logger.missing partial
364
+ end
365
+ parts.pop
366
+ end
367
+ end
368
+
369
+ def complex_template(*args)
370
+ # nothing should be done here
371
+ end
372
+ end
373
+
374
+
375
+ # List a generator's action manifest.
376
+ class List < Base
377
+ def dependency(generator_name, args, options = {})
378
+ logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})"
379
+ end
380
+
381
+ def class_collisions(*class_names)
382
+ logger.class_collisions class_names.join(', ')
383
+ end
384
+
385
+ def file(relative_source, relative_destination, options = {})
386
+ logger.file relative_destination
387
+ end
388
+
389
+ def template(relative_source, relative_destination, options = {})
390
+ logger.template relative_destination
391
+ end
392
+
393
+ def complex_template(relative_source, relative_destination, options = {})
394
+ logger.template "#{options[:insert]} inside #{relative_destination}"
395
+ end
396
+
397
+ def directory(relative_path)
398
+ logger.directory "#{destination_path(relative_path)}/"
399
+ end
400
+
401
+ def readme(*args)
402
+ logger.readme args.join(', ')
403
+ end
404
+ end
405
+
406
+ # Update generator's action manifest.
407
+ class Update < Create
408
+ def file(relative_source, relative_destination, options = {})
409
+ # logger.file relative_destination
410
+ end
411
+
412
+ def template(relative_source, relative_destination, options = {})
413
+ # logger.template relative_destination
414
+ end
415
+
416
+ def complex_template(relative_source, relative_destination, template_options = {})
417
+
418
+ begin
419
+ dest_file = destination_path(relative_destination)
420
+ source_to_update = File.readlines(dest_file).join
421
+ rescue Errno::ENOENT
422
+ logger.missing relative_destination
423
+ return
424
+ end
425
+
426
+ logger.refreshing "#{template_options[:insert].gsub(/\.rhtml/,'')} inside #{relative_destination}"
427
+
428
+ begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id]))
429
+ end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id]))
430
+
431
+ # Refreshing inner part of the template with freshly rendered part.
432
+ rendered_part = render_template_part(template_options)
433
+ source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part)
434
+
435
+ File.open(dest_file, 'w') { |file| file.write(source_to_update) }
436
+ end
437
+
438
+ def directory(relative_path)
439
+ # logger.directory "#{destination_path(relative_path)}/"
440
+ end
441
+ end
442
+
443
+ end
444
+ end
445
+ end