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.
- data/CHANGELOG +11 -0
- data/MANIFEST +77 -0
- data/MIT-LICENSE +22 -0
- data/README +21 -0
- data/bin/console +4 -0
- data/bin/destroy +4 -0
- data/bin/generate +4 -0
- data/bin/muding +16 -0
- data/bin/server +5 -0
- data/configs/boot.rb +4 -0
- data/configs/databases/mysql.yml +47 -0
- data/configs/databases/oracle.yml +30 -0
- data/configs/databases/postgresql.yml +44 -0
- data/configs/databases/sqlite2.yml +16 -0
- data/configs/databases/sqlite3.yml +16 -0
- data/doc/README_FOR_MUD +3 -0
- data/fresh_rakefile +4 -0
- data/helpers/mud.rb +8 -0
- data/helpers/mud_helper.rb +4 -0
- data/helpers/test_helper.rb +29 -0
- data/lib/acts/container.rb +70 -0
- data/lib/acts/expireable.rb +82 -0
- data/lib/commands/destroy.rb +7 -0
- data/lib/commands/generate.rb +7 -0
- data/lib/commands/server.rb +45 -0
- data/lib/commands/update.rb +5 -0
- data/lib/controller.rb +132 -0
- data/lib/handle.rb +42 -0
- data/lib/model.rb +11 -0
- data/lib/muding.rb +46 -0
- data/lib/muding_generator.rb +22 -0
- data/lib/muding_generator/base.rb +162 -0
- data/lib/muding_generator/commands.rb +519 -0
- data/lib/muding_generator/generators/applications/app/USAGE +14 -0
- data/lib/muding_generator/generators/applications/app/app_generator.rb +132 -0
- data/lib/muding_generator/generators/components/controller/USAGE +30 -0
- data/lib/muding_generator/generators/components/controller/controller_generator.rb +38 -0
- data/lib/muding_generator/generators/components/controller/templates/controller.rb +7 -0
- data/lib/muding_generator/generators/components/controller/templates/functional_test.rb +18 -0
- data/lib/muding_generator/generators/components/controller/templates/helper.rb +2 -0
- data/lib/muding_generator/generators/components/controller/templates/view.rhtml +2 -0
- data/lib/muding_generator/generators/components/migration/USAGE +14 -0
- data/lib/muding_generator/generators/components/migration/migration_generator.rb +7 -0
- data/lib/muding_generator/generators/components/migration/templates/migration.rb +7 -0
- data/lib/muding_generator/generators/components/model/USAGE +19 -0
- data/lib/muding_generator/generators/components/model/model_generator.rb +34 -0
- data/lib/muding_generator/generators/components/model/templates/fixtures.yml +5 -0
- data/lib/muding_generator/generators/components/model/templates/migration.rb +11 -0
- data/lib/muding_generator/generators/components/model/templates/model.rb +2 -0
- data/lib/muding_generator/generators/components/model/templates/unit_test.rb +10 -0
- data/lib/muding_generator/lookup.rb +210 -0
- data/lib/muding_generator/manifest.rb +53 -0
- data/lib/muding_generator/options.rb +140 -0
- data/lib/muding_generator/scripts.rb +83 -0
- data/lib/muding_generator/scripts/destroy.rb +7 -0
- data/lib/muding_generator/scripts/generate.rb +7 -0
- data/lib/muding_generator/scripts/update.rb +12 -0
- data/lib/muding_generator/simple_logger.rb +46 -0
- data/lib/muding_generator/spec.rb +44 -0
- data/lib/ruby_version_check.rb +12 -0
- data/lib/tasks/migrate.rake +33 -0
- metadata +149 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
#TODO: rewrite this class to allow individual objects to have their own configurations..
|
2
|
+
module Acts #:nodoc:
|
3
|
+
module Expireable
|
4
|
+
def self.included(base) # :nodoc:
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def acts_as_expireable(options = {})
|
10
|
+
config = {:respawn => false, :lifespan => 10.seconds, :delay=> 0.seconds}
|
11
|
+
config.update options if options.is_a?(Hash)
|
12
|
+
|
13
|
+
unless expireable? # don't let AR call this twice
|
14
|
+
cattr_accessor :respawn, :lifespan, :delay
|
15
|
+
self.respawn = config[:respawn]
|
16
|
+
self.lifespan = config[:lifespan]
|
17
|
+
self.delay = config[:delay]
|
18
|
+
Thread.new do
|
19
|
+
while true
|
20
|
+
|
21
|
+
#puts "Checking Expireables."
|
22
|
+
#self.find(:all).each do |s|
|
23
|
+
# puts "Expires at:" + s.expires_at.to_s
|
24
|
+
# s.destroy
|
25
|
+
#end
|
26
|
+
|
27
|
+
puts Time.now.to_s
|
28
|
+
|
29
|
+
self.find(:all, :conditions => ["spawn_at <= ? and expires_at >= ?", Time.now, Time.now]).each do |expireable|
|
30
|
+
expireable.spawn
|
31
|
+
end
|
32
|
+
self.find(:all, :conditions => ["expires_at <= ?", Time.now]).each do |expireable|
|
33
|
+
expireable.expire
|
34
|
+
end
|
35
|
+
sleep 0.5
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
before_create :setup_expireable
|
40
|
+
include InstanceMethods
|
41
|
+
rescue
|
42
|
+
puts $!
|
43
|
+
end
|
44
|
+
|
45
|
+
def expireable?
|
46
|
+
self.included_modules.include?(InstanceMethods)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module InstanceMethods #:nodoc:
|
51
|
+
def self.included(base) # :nodoc:
|
52
|
+
base.extend ClassMethods
|
53
|
+
end
|
54
|
+
|
55
|
+
def expire
|
56
|
+
self.on_expire if respond_to? :on_expire
|
57
|
+
self.destroy
|
58
|
+
if self.class.respawn
|
59
|
+
self.on_respawn if respond_to? :on_respawn
|
60
|
+
self.class.new.save
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def spawn
|
65
|
+
if !spawned?
|
66
|
+
self.on_spawn if respond_to? :on_spawn
|
67
|
+
self.spawn_at = nil
|
68
|
+
save
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup_expireable
|
73
|
+
self.expires_at = Time.now + self.class.lifespan
|
74
|
+
self.spawn_at = Time.now + self.class.delay
|
75
|
+
end
|
76
|
+
|
77
|
+
def spawned?
|
78
|
+
self.spawn_at == nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
%w[socket handle].each { |lib| require lib }
|
2
|
+
|
3
|
+
log = Log4r::Logger.new 'main'
|
4
|
+
log.outputters = Log4r::Outputter.stdout
|
5
|
+
|
6
|
+
begin
|
7
|
+
server = TCPServer.new('localhost', 4000)
|
8
|
+
threads = []
|
9
|
+
|
10
|
+
#The procedures for shuting down the server
|
11
|
+
graceful_shutdown = Proc.new do
|
12
|
+
#shuting down the game right now is as simple as exiting.
|
13
|
+
#later we probably want to backup save the active records
|
14
|
+
#or something
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
#what to do if our server receives a OS signal to shut down
|
19
|
+
handle_signalled_shutdown = Proc.new do |sig|
|
20
|
+
log.warn "Caught " + Signal.list.invert[sig] + " Signal... Shutting Down"
|
21
|
+
graceful_shutdown.call
|
22
|
+
end
|
23
|
+
|
24
|
+
#trap the signals we want to handle a shutdown for.
|
25
|
+
[:INT,:TERM,:KILL].each do |sig|
|
26
|
+
Signal.trap sig, handle_signalled_shutdown
|
27
|
+
end
|
28
|
+
|
29
|
+
log.info "Accepting Connections..."
|
30
|
+
while session = server.accept
|
31
|
+
threads << Thread.new(session) do |s|
|
32
|
+
begin
|
33
|
+
Handle.this session
|
34
|
+
rescue
|
35
|
+
log.error "Top Level Thread Error. You should never see this."
|
36
|
+
log.error $!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
graceful_shutdown.call
|
42
|
+
rescue
|
43
|
+
log.fatal "Top Level Server Error. You should never see this."
|
44
|
+
log.fatal $!
|
45
|
+
end
|
data/lib/controller.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module Controller
|
2
|
+
Log = Log4r::Logger.new 'controller'
|
3
|
+
Log.outputters = Log4r::Outputter.stdout
|
4
|
+
|
5
|
+
class Base
|
6
|
+
attr_accessor :input, :body, :rendered
|
7
|
+
attr_accessor :new_route, :default_command, :prompt
|
8
|
+
|
9
|
+
@@before_view = {}
|
10
|
+
|
11
|
+
#route_to is the command that routes a connection's input to the
|
12
|
+
#next command controller, or default command.
|
13
|
+
#
|
14
|
+
# route_to :route =>StartController, :command=>:begin, :prompt=>">"
|
15
|
+
# This will make the user's next input goto the StartController
|
16
|
+
# with the command "begin". After rendering the command
|
17
|
+
# it will output ">" to the user.
|
18
|
+
#
|
19
|
+
# route_to :view=>"password"
|
20
|
+
#
|
21
|
+
# This will keep the user's route the same. After rendering the command
|
22
|
+
# it will output the content of the password view rendered in the context
|
23
|
+
# of the command's binding. The directory for the password view will
|
24
|
+
# be the same as the Controller.
|
25
|
+
def route_to(options={})
|
26
|
+
config = {:controller => self.class, :command => nil, :prompt => "", :view => :default}
|
27
|
+
config.update options if options.is_a?(Hash)
|
28
|
+
|
29
|
+
self.new_route = config[:controller]
|
30
|
+
self.default_command = config[:command]
|
31
|
+
self.prompt = config[:prompt] if config[:prompt] != ""
|
32
|
+
self.prompt = render_template(before_view_file) if config[:view] == :default && @@before_view[config[:command]]
|
33
|
+
end
|
34
|
+
|
35
|
+
#TODO: Implement this.
|
36
|
+
#Should jump to next controller method right away, instead of waiting for new input.
|
37
|
+
#r(302,'','Location'=>URL(*a))
|
38
|
+
#by right away I mean after the command finishes running.
|
39
|
+
def redirect_to(options={})
|
40
|
+
config = {:route => self.class, :command => nil, :prompt => "", :view => :default}
|
41
|
+
config.update options if options.is_a?(Hash)
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def render(options = {})
|
46
|
+
raise "Double Render Error" if self.rendered
|
47
|
+
|
48
|
+
self.rendered = true
|
49
|
+
return if options[:nothing]
|
50
|
+
if options[:text]
|
51
|
+
@body = options[:text]
|
52
|
+
return
|
53
|
+
end
|
54
|
+
@body = render_template(view_file)
|
55
|
+
|
56
|
+
@body += self.prompt if self.prompt
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(command, s = {}) #:nodoc:
|
60
|
+
@method = command.downcase
|
61
|
+
@session = s
|
62
|
+
end
|
63
|
+
|
64
|
+
def route
|
65
|
+
return self.class
|
66
|
+
end
|
67
|
+
|
68
|
+
def view_file
|
69
|
+
command_file = @method + ".rhtml"
|
70
|
+
load_and_read_file(command_file)
|
71
|
+
end
|
72
|
+
|
73
|
+
def before_view_file
|
74
|
+
command_file = @@before_view[default_command].to_s + ".rhtml"
|
75
|
+
load_and_read_file(command_file)
|
76
|
+
end
|
77
|
+
|
78
|
+
def load_and_read_file(file_name)
|
79
|
+
file = File.open(File.join(view_dir, file_name))
|
80
|
+
file.read
|
81
|
+
rescue Errno::ENOENT
|
82
|
+
puts $!
|
83
|
+
return "Could not find the proper view for this command"
|
84
|
+
end
|
85
|
+
|
86
|
+
def view_dir
|
87
|
+
File.join("mud","views",route.to_s.downcase.chomp("controller"))
|
88
|
+
end
|
89
|
+
|
90
|
+
def render_template(vf)
|
91
|
+
ERB.new(vf, nil, '-').result(binding)
|
92
|
+
end
|
93
|
+
|
94
|
+
def service(*a)
|
95
|
+
send(@method, *a) if respond_to? @method
|
96
|
+
if !self.rendered
|
97
|
+
render
|
98
|
+
end
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def session()
|
103
|
+
return @session
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_s
|
107
|
+
"#{@body}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def Base.before_view
|
111
|
+
return @@before_view
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class NotFound < Base
|
116
|
+
def get(p)
|
117
|
+
render :text=>"#{p} Could not be found."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class ServerError < Base
|
122
|
+
def get
|
123
|
+
render :text=>"Command problem, check the logs."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#Load all the controllers.
|
128
|
+
load "./mud/controllers/mud.rb"
|
129
|
+
Dir["./mud/controllers/*_controller.rb"].sort.each { |ext| load ext.to_s }
|
130
|
+
end
|
131
|
+
|
132
|
+
|
data/lib/handle.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'muding'
|
2
|
+
|
3
|
+
#This class handles connections. It stores session information
|
4
|
+
#about the connection. It keeps track of where the connection is currently sending it's input to.
|
5
|
+
class Handle
|
6
|
+
Log = Log4r::Logger.new 'handle'
|
7
|
+
Log.outputters = Log4r::Outputter.stdout
|
8
|
+
|
9
|
+
@@handles = [] #All of the connections.
|
10
|
+
|
11
|
+
attr_accessor :route, :session, :default_command, :peer
|
12
|
+
|
13
|
+
def Handle.this s
|
14
|
+
@@handles << h = Handle.new(s)
|
15
|
+
h.start_handling
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize peer
|
19
|
+
self.session = {:handle=>self}
|
20
|
+
self.peer = peer
|
21
|
+
self.route = MudController
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_handling
|
25
|
+
response = Muding.run("begin", self)
|
26
|
+
|
27
|
+
while true
|
28
|
+
peer.puts response.body
|
29
|
+
self.route = response.new_route if response.new_route
|
30
|
+
|
31
|
+
self.default_command = nil
|
32
|
+
self.default_command = response.default_command if response.default_command
|
33
|
+
response = Muding.run(peer.gets, self)
|
34
|
+
end
|
35
|
+
rescue Errno::EPIPE
|
36
|
+
Log.info "Connection " + self.object_id.to_s + " Severed"
|
37
|
+
end
|
38
|
+
|
39
|
+
def message(message_string)
|
40
|
+
peer.puts message_string
|
41
|
+
end
|
42
|
+
end
|
data/lib/model.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Dir["./lib/acts/*.rb"].sort.each { |ext| load ext.to_s }
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(YAML.load_file("config/database.yml")["development"])
|
4
|
+
|
5
|
+
ActiveRecord::Base.send :include, Acts::Expireable
|
6
|
+
ActiveRecord::Base.send :include, Acts::Container
|
7
|
+
|
8
|
+
Base = ActiveRecord::Base
|
9
|
+
Base.logger = Log4r::Logger.new 'ActiveRecord::Base' #not outputing anywhere?
|
10
|
+
|
11
|
+
Dir["./mud/models/*.rb"].sort.each { |ext| load ext.to_s }
|
data/lib/muding.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
%w[active_record socket erb log4r].each { |lib| require lib }
|
2
|
+
%w[model controller].each { |lib| require(lib) }
|
3
|
+
|
4
|
+
module Muding
|
5
|
+
Log = Log4r::Logger.new 'muding'
|
6
|
+
Log.outputters = Log4r::Outputter.stdout
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def run(input, handle)
|
10
|
+
unless input
|
11
|
+
raise Errno::EPIPE.new("Nil String to Pipe")
|
12
|
+
end
|
13
|
+
default_command = handle.default_command
|
14
|
+
session = handle.session
|
15
|
+
route = handle.route
|
16
|
+
|
17
|
+
reload_controller_code
|
18
|
+
|
19
|
+
|
20
|
+
# if we don't have a default command we need to figure
|
21
|
+
# out what the command is.
|
22
|
+
#
|
23
|
+
# format: "command [args]"
|
24
|
+
unless default_command
|
25
|
+
string_array = input.split(" ")
|
26
|
+
command = string_array.shift
|
27
|
+
args = string_array.to_s
|
28
|
+
else
|
29
|
+
command = default_command.to_s
|
30
|
+
args = input
|
31
|
+
end
|
32
|
+
|
33
|
+
return route.new(command, session).service(*args)
|
34
|
+
|
35
|
+
rescue Errno::EPIPE
|
36
|
+
raise
|
37
|
+
rescue Exception => x
|
38
|
+
Log.warn x
|
39
|
+
return Controllers::ServerError.new("get",session).service()
|
40
|
+
end
|
41
|
+
def reload_controller_code
|
42
|
+
load "./mud/controllers/mud.rb"
|
43
|
+
Dir["./mud/controllers/*_controller.rb"].sort.each { |ext| load ext }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'active_support'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require_gem 'activesupport'
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'muding_generator/base'
|
12
|
+
require 'muding_generator/lookup'
|
13
|
+
require 'muding_generator/commands'
|
14
|
+
|
15
|
+
Muding::Generator::Base.send(:include, Muding::Generator::Lookup)
|
16
|
+
Muding::Generator::Base.send(:include, Muding::Generator::Commands)
|
17
|
+
|
18
|
+
# Set up a default logger for convenience.
|
19
|
+
require 'muding_generator/simple_logger'
|
20
|
+
Muding::Generator::Base.logger = Muding::Generator::SimpleLogger.new(STDOUT)
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/options'
|
2
|
+
require File.dirname(__FILE__) + '/manifest'
|
3
|
+
require File.dirname(__FILE__) + '/spec'
|
4
|
+
|
5
|
+
module Muding
|
6
|
+
module Generator
|
7
|
+
class GeneratorError < StandardError; end
|
8
|
+
class UsageError < GeneratorError; end
|
9
|
+
|
10
|
+
class Base
|
11
|
+
include Options
|
12
|
+
|
13
|
+
default_options :collision => :ask, :quiet => false
|
14
|
+
|
15
|
+
cattr_accessor :logger
|
16
|
+
|
17
|
+
class_inheritable_accessor :spec
|
18
|
+
|
19
|
+
attr_reader :source_root, :destination_root, :args
|
20
|
+
|
21
|
+
def initialize(runtime_args, runtime_options = {})
|
22
|
+
@args = runtime_args
|
23
|
+
parse!(@args, runtime_options)
|
24
|
+
|
25
|
+
# Derive source and destination paths.
|
26
|
+
@source_root = options[:source] || File.join(spec.path, 'templates')
|
27
|
+
if options[:destination]
|
28
|
+
@destination_root = options[:destination]
|
29
|
+
elsif defined? ::RAILS_ROOT
|
30
|
+
@destination_root = ::RAILS_ROOT
|
31
|
+
end
|
32
|
+
|
33
|
+
# Silence the logger if requested.
|
34
|
+
logger.quiet = options[:quiet]
|
35
|
+
|
36
|
+
# Raise usage error if help is requested.
|
37
|
+
usage if options[:help]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generators must provide a manifest. Use the record method to create
|
41
|
+
# a new manifest and record your generator's actions.
|
42
|
+
def manifest
|
43
|
+
raise NotImplementedError, "No manifest for '#{spec.name}' generator."
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return the full path from the source root for the given path.
|
47
|
+
# Example for source_root = '/source':
|
48
|
+
# source_path('some/path.rb') == '/source/some/path.rb'
|
49
|
+
#
|
50
|
+
# The given path may include a colon ':' character to indicate that
|
51
|
+
# the file belongs to another generator. This notation allows any
|
52
|
+
# generator to borrow files from another. Example:
|
53
|
+
# source_path('model:fixture.yml') = '/model/source/path/fixture.yml'
|
54
|
+
def source_path(relative_source)
|
55
|
+
# Check whether we're referring to another generator's file.
|
56
|
+
name, path = relative_source.split(':', 2)
|
57
|
+
|
58
|
+
# If not, return the full path to our source file.
|
59
|
+
if path.nil?
|
60
|
+
File.join(source_root, name)
|
61
|
+
|
62
|
+
# Otherwise, ask our referral for the file.
|
63
|
+
else
|
64
|
+
# FIXME: this is broken, though almost always true. Others'
|
65
|
+
# source_root are not necessarily the templates dir.
|
66
|
+
File.join(self.class.lookup(name).path, 'templates', path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the full path from the destination root for the given path.
|
71
|
+
# Example for destination_root = '/dest':
|
72
|
+
# destination_path('some/path.rb') == '/dest/some/path.rb'
|
73
|
+
def destination_path(relative_destination)
|
74
|
+
File.join(destination_root, relative_destination)
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
# Convenience method for generator subclasses to record a manifest.
|
79
|
+
def record
|
80
|
+
Muding::Generator::Manifest.new(self) { |m| yield m }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Override with your own usage banner.
|
84
|
+
def banner
|
85
|
+
"Usage: #{$0} #{spec.name} [options]"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Read USAGE from file in generator base path.
|
89
|
+
def usage_message
|
90
|
+
File.read(File.join(spec.path, 'USAGE')) rescue ''
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# The base generator for named components: models, controllers, mailers,
|
96
|
+
# etc. The target name is taken as the first argument and inflected to
|
97
|
+
# singular, plural, class, file, and table forms for your convenience.
|
98
|
+
# The remaining arguments are aliased to actions for controller and
|
99
|
+
# mailer convenience.
|
100
|
+
#
|
101
|
+
# If no name is provided, the generator raises a usage error with content
|
102
|
+
# optionally read from the USAGE file in the generator's base path.
|
103
|
+
#
|
104
|
+
# See Rails::Generator::Base for a discussion of Manifests and Commands.
|
105
|
+
class NamedBase < Base
|
106
|
+
attr_reader :name, :class_name, :singular_name, :plural_name, :table_name
|
107
|
+
attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth
|
108
|
+
alias_method :file_name, :singular_name
|
109
|
+
alias_method :actions, :args
|
110
|
+
|
111
|
+
def initialize(runtime_args, runtime_options = {})
|
112
|
+
super
|
113
|
+
|
114
|
+
# Name argument is required.
|
115
|
+
usage if runtime_args.empty?
|
116
|
+
|
117
|
+
@args = runtime_args.dup
|
118
|
+
base_name = @args.shift
|
119
|
+
assign_names!(base_name)
|
120
|
+
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
# Override with your own usage banner.
|
124
|
+
def banner
|
125
|
+
"Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]"
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
def assign_names!(name)
|
130
|
+
@name = name
|
131
|
+
base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name)
|
132
|
+
@class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name)
|
133
|
+
@table_name = ActiveRecord::Base.pluralize_table_names ? plural_name : singular_name
|
134
|
+
if @class_nesting.empty?
|
135
|
+
@class_name = @class_name_without_nesting
|
136
|
+
else
|
137
|
+
@class_name = "#{@class_nesting}::#{@class_name_without_nesting}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Extract modules from filesystem-style or ruby-style path:
|
142
|
+
# good/fun/stuff
|
143
|
+
# Good::Fun::Stuff
|
144
|
+
# produce the same results.
|
145
|
+
def extract_modules(name)
|
146
|
+
modules = name.include?('/') ? name.split('/') : name.split('::')
|
147
|
+
name = modules.pop
|
148
|
+
path = modules.map { |m| m.underscore }
|
149
|
+
file_path = (path + [name.underscore]).join('/')
|
150
|
+
nesting = modules.map { |m| m.camelize }.join('::')
|
151
|
+
[name, path, file_path, nesting, modules.size]
|
152
|
+
end
|
153
|
+
|
154
|
+
def inflect_names(name)
|
155
|
+
camel = name.camelize
|
156
|
+
under = camel.underscore
|
157
|
+
plural = under.pluralize
|
158
|
+
[camel, under, plural]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|