muding 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +17 -0
- data/MANIFEST +10 -1
- data/README +0 -9
- data/configs/boot.rb +45 -3
- data/configs/environment.rb +49 -0
- data/fresh_rakefile +5 -2
- data/helpers/test_helper.rb +2 -2
- data/lib/acts/container.rb +3 -2
- data/lib/code_statistics.rb +107 -0
- data/lib/commands/destroy.rb +3 -1
- data/lib/commands/generate.rb +1 -1
- data/lib/commands/server.rb +24 -2
- data/lib/commands/update.rb +1 -1
- data/lib/controller.rb +81 -26
- data/lib/handle.rb +8 -2
- data/lib/initializer.rb +612 -0
- data/lib/model.rb +0 -1
- data/lib/muding.rb +1 -1
- data/lib/muding_generator/base.rb +3 -3
- data/lib/muding_generator/commands.rb +7 -6
- data/lib/muding_generator/generators/applications/app/app_generator.rb +1 -0
- data/lib/muding_generator/generators/components/controller/controller_generator.rb +1 -1
- data/lib/muding_generator/generators/components/migration/migration_generator.rb +1 -1
- data/lib/muding_generator/generators/components/model/model_generator.rb +1 -1
- data/lib/muding_generator/lookup.rb +10 -10
- data/lib/muding_generator/scripts.rb +5 -5
- data/lib/tasks/databases.rake +169 -0
- data/lib/tasks/documentation.rake +55 -0
- data/lib/tasks/muding.rb +7 -0
- data/lib/tasks/statistics.rake +18 -0
- data/lib/tasks/testing.rake +102 -0
- data/lib/test_help.rb +19 -0
- data/lib/version.rb +11 -0
- metadata +12 -3
- data/lib/tasks/migrate.rake +0 -33
data/CHANGELOG
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
*** 0.2.0 / 2006-08-02
|
2
|
+
|
3
|
+
+ 1 major enhancement
|
4
|
+
+Unit Testing
|
5
|
+
|
6
|
+
+ 2 minor enhancements
|
7
|
+
+ redirect_to
|
8
|
+
+ abbreviations
|
9
|
+
+ generators now work
|
10
|
+
|
11
|
+
+ 1 change
|
12
|
+
+ .rhtml is now .eruby, the standard ERB file suffix
|
13
|
+
|
14
|
+
+ 2 bug fixes
|
15
|
+
+fixed some issues with Controller::Base
|
16
|
+
+removed redirect_to method due to it's non-functioning
|
17
|
+
|
1
18
|
*** 0.1.1 / 2006-07-25
|
2
19
|
|
3
20
|
+ 2 bug fixes
|
data/MANIFEST
CHANGED
@@ -11,6 +11,7 @@ bin/console
|
|
11
11
|
bin/destroy
|
12
12
|
configs/
|
13
13
|
configs/boot.rb
|
14
|
+
configs/environment.rb
|
14
15
|
configs/databases/
|
15
16
|
configs/databases/mysql.yml
|
16
17
|
configs/databases/oracle.yml
|
@@ -24,13 +25,21 @@ helpers/mud.rb
|
|
24
25
|
helpers/mud_helper.rb
|
25
26
|
helpers/test_helper.rb
|
26
27
|
lib/
|
28
|
+
lib/initializer.rb
|
29
|
+
lib/code_statistics.rb
|
30
|
+
lib/version.rb
|
31
|
+
lib/test_help.rb
|
27
32
|
lib/controller.rb
|
28
33
|
lib/handle.rb
|
29
34
|
lib/model.rb
|
30
35
|
lib/muding.rb
|
31
36
|
lib/muding_generator.rb
|
32
37
|
lib/ruby_version_check.rb
|
33
|
-
lib/tasks/
|
38
|
+
lib/tasks/muding.rb
|
39
|
+
lib/tasks/databases.rake
|
40
|
+
lib/tasks/documentation.rake
|
41
|
+
lib/tasks/statistics.rake
|
42
|
+
lib/tasks/testing.rake
|
34
43
|
lib/acts/expireable.rb
|
35
44
|
lib/acts/container.rb
|
36
45
|
lib/commands/
|
data/README
CHANGED
@@ -10,12 +10,3 @@ common to all multi-user environments into a separate framework that can
|
|
10
10
|
be developed through open-source. Developers using the framework can then
|
11
11
|
build their multi-user environment on top of the framework, and update the
|
12
12
|
framework code independantly of their own code.
|
13
|
-
|
14
|
-
** SETUP:
|
15
|
-
muding mudname
|
16
|
-
- creates a new directory at your current path for the mud you are
|
17
|
-
developing
|
18
|
-
|
19
|
-
** INSTALL:
|
20
|
-
|
21
|
-
sudo gem install muding
|
data/configs/boot.rb
CHANGED
@@ -1,4 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
|
2
|
+
|
3
|
+
unless defined?(MUDING_ROOT)
|
4
|
+
root_path = File.join(File.dirname(__FILE__), '..')
|
5
|
+
|
6
|
+
unless RUBY_PLATFORM =~ /mswin32/
|
7
|
+
require 'pathname'
|
8
|
+
root_path = Pathname.new(root_path).cleanpath(true).to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
MUDING_ROOT = root_path
|
12
|
+
end
|
13
|
+
|
14
|
+
unless defined?(Muding::Initializer)
|
15
|
+
if File.directory?("#{MUDING_ROOT}/vendor/rails")
|
16
|
+
require "#{MUDING_ROOT}/vendor/muding/railties/lib/initializer"
|
17
|
+
else
|
18
|
+
require 'rubygems'
|
19
|
+
|
20
|
+
environment_without_comments = IO.readlines(File.dirname(__FILE__) + '/environment.rb').reject { |l| l =~ /^#/ }.join
|
21
|
+
environment_without_comments =~ /[^#]MUDING_GEM_VERSION = '([\d.]+)'/
|
22
|
+
muding_gem_version = $1
|
23
|
+
|
24
|
+
if version = defined?(MUDING_GEM_VERSION) ? MUDING_GEM_VERSION : muding_gem_version
|
25
|
+
muding_gem = Gem.cache.search('muding', "=#{version}").first
|
26
|
+
|
27
|
+
if muding_gem
|
28
|
+
require_gem "muding", "=#{version}"
|
29
|
+
require muding_gem.full_gem_path + '/lib/initializer'
|
30
|
+
else
|
31
|
+
STDERR.puts %(Cannot find gem for muding =#{version}:
|
32
|
+
Install the missing gem with 'gem install -v=#{version} muding', or
|
33
|
+
change environment.rb to define MUDING_GEM_VERSION with your desired version.
|
34
|
+
)
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
else
|
38
|
+
require_gem "muding"
|
39
|
+
require 'initializer'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Muding::Initializer.run(:set_load_path)
|
44
|
+
end
|
45
|
+
|
4
46
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# Use this if you want to move your muding server into production mode.
|
4
|
+
# ENV['MUDING_ENV'] ||= 'production'
|
5
|
+
|
6
|
+
# Specifies gem version of Rails to use when vendor/rails is not present
|
7
|
+
<%= '# ' if freeze %>MUDING_GEM_VERSION = '<%= Muding::VERSION::STRING %>'
|
8
|
+
|
9
|
+
# Bootstrap the Rails environment, frameworks, and default configuration
|
10
|
+
require File.join(File.dirname(__FILE__), 'boot')
|
11
|
+
|
12
|
+
Muding::Initializer.run do |config|
|
13
|
+
# Settings in config/environments/* take precedence those specified here
|
14
|
+
|
15
|
+
# Add additional load paths for your own custom dirs
|
16
|
+
# config.load_paths += %W( #{MUDING_ROOT}/extras )
|
17
|
+
|
18
|
+
# Force all environments to use the same logger level
|
19
|
+
# (by default production uses :info, the others :debug)
|
20
|
+
# config.log_level = :debug
|
21
|
+
|
22
|
+
# Use the database for sessions instead of the file system
|
23
|
+
# (create the session table with 'rake db:sessions:create')
|
24
|
+
# config.action_controller.session_store = :active_record_store
|
25
|
+
|
26
|
+
# Use SQL instead of Active Record's schema dumper when creating the test database.
|
27
|
+
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
28
|
+
# like if you have constraints or database-specific column types
|
29
|
+
# config.active_record.schema_format = :sql
|
30
|
+
|
31
|
+
# Activate observers that should always be running
|
32
|
+
# config.active_record.observers = :cacher, :garbage_collector
|
33
|
+
|
34
|
+
# Make Active Record use UTC-base instead of local time
|
35
|
+
# config.active_record.default_timezone = :utc
|
36
|
+
|
37
|
+
# See Muding::Configuration for more options
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add new inflection rules using the following format
|
41
|
+
# (all these examples are active by default):
|
42
|
+
# Inflector.inflections do |inflect|
|
43
|
+
# inflect.plural /^(ox)$/i, '\1en'
|
44
|
+
# inflect.singular /^(ox)en/i, '\1'
|
45
|
+
# inflect.irregular 'person', 'people'
|
46
|
+
# inflect.uncountable %w( fish sheep )
|
47
|
+
# end
|
48
|
+
|
49
|
+
# Include your application configuration below
|
data/fresh_rakefile
CHANGED
data/helpers/test_helper.rb
CHANGED
data/lib/acts/container.rb
CHANGED
@@ -44,9 +44,10 @@ module Acts #:nodoc:
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def <<(content)
|
47
|
+
content.container = nil
|
47
48
|
self.containables.each do |containable|
|
48
|
-
begin
|
49
|
-
|
49
|
+
begin
|
50
|
+
self.send(containable).<< content
|
50
51
|
return # if it successfully adds we are done with this method.
|
51
52
|
|
52
53
|
# there is probably a better way to do this. right now if it can't be added to the container
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class CodeStatistics #:nodoc:
|
2
|
+
|
3
|
+
TEST_TYPES = %w(Units Functionals Unit\ tests Functional\ tests Integration\ tests)
|
4
|
+
|
5
|
+
def initialize(*pairs)
|
6
|
+
@pairs = pairs
|
7
|
+
@statistics = calculate_statistics
|
8
|
+
@total = calculate_total if pairs.length > 1
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
print_header
|
13
|
+
@pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
|
14
|
+
print_splitter
|
15
|
+
|
16
|
+
if @total
|
17
|
+
print_line("Total", @total)
|
18
|
+
print_splitter
|
19
|
+
end
|
20
|
+
|
21
|
+
print_code_test_stats
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def calculate_statistics
|
26
|
+
@pairs.inject({}) { |stats, pair| stats[pair.first] = calculate_directory_statistics(pair.last); stats }
|
27
|
+
end
|
28
|
+
|
29
|
+
def calculate_directory_statistics(directory, pattern = /.*\.rb$/)
|
30
|
+
stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
|
31
|
+
|
32
|
+
Dir.foreach(directory) do |file_name|
|
33
|
+
if File.stat(directory + "/" + file_name).directory? and (/^\./ !~ file_name)
|
34
|
+
newstats = calculate_directory_statistics(directory + "/" + file_name, pattern)
|
35
|
+
stats.each { |k, v| stats[k] += newstats[k] }
|
36
|
+
end
|
37
|
+
|
38
|
+
next unless file_name =~ pattern
|
39
|
+
|
40
|
+
f = File.open(directory + "/" + file_name)
|
41
|
+
|
42
|
+
while line = f.gets
|
43
|
+
stats["lines"] += 1
|
44
|
+
stats["classes"] += 1 if line =~ /class [A-Z]/
|
45
|
+
stats["methods"] += 1 if line =~ /def [a-z]/
|
46
|
+
stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
stats
|
51
|
+
end
|
52
|
+
|
53
|
+
def calculate_total
|
54
|
+
total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
|
55
|
+
@statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
|
56
|
+
total
|
57
|
+
end
|
58
|
+
|
59
|
+
def calculate_code
|
60
|
+
code_loc = 0
|
61
|
+
@statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k }
|
62
|
+
code_loc
|
63
|
+
end
|
64
|
+
|
65
|
+
def calculate_tests
|
66
|
+
test_loc = 0
|
67
|
+
@statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k }
|
68
|
+
test_loc
|
69
|
+
end
|
70
|
+
|
71
|
+
def print_header
|
72
|
+
print_splitter
|
73
|
+
puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
|
74
|
+
print_splitter
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_splitter
|
78
|
+
puts "+----------------------+-------+-------+---------+---------+-----+-------+"
|
79
|
+
end
|
80
|
+
|
81
|
+
def print_line(name, statistics)
|
82
|
+
m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0
|
83
|
+
loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0
|
84
|
+
|
85
|
+
start = if TEST_TYPES.include? name
|
86
|
+
"| #{name.ljust(18)} "
|
87
|
+
else
|
88
|
+
"| #{name.ljust(20)} "
|
89
|
+
end
|
90
|
+
|
91
|
+
puts start +
|
92
|
+
"| #{statistics["lines"].to_s.rjust(5)} " +
|
93
|
+
"| #{statistics["codelines"].to_s.rjust(5)} " +
|
94
|
+
"| #{statistics["classes"].to_s.rjust(7)} " +
|
95
|
+
"| #{statistics["methods"].to_s.rjust(7)} " +
|
96
|
+
"| #{m_over_c.to_s.rjust(3)} " +
|
97
|
+
"| #{loc_over_m.to_s.rjust(5)} |"
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_code_test_stats
|
101
|
+
code = calculate_code
|
102
|
+
tests = calculate_tests
|
103
|
+
|
104
|
+
puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
|
105
|
+
puts ""
|
106
|
+
end
|
107
|
+
end
|
data/lib/commands/destroy.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
require "#{MUDING_ROOT}/config/environment"
|
1
|
+
#require "#{MUDING_ROOT}/config/environment"
|
2
2
|
require 'muding_generator'
|
3
3
|
require 'muding_generator/scripts/destroy'
|
4
4
|
|
5
|
+
|
5
6
|
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
7
|
+
|
6
8
|
Muding::Generator::Scripts::Destroy.new.run(ARGV)
|
7
9
|
|
data/lib/commands/generate.rb
CHANGED
data/lib/commands/server.rb
CHANGED
@@ -1,9 +1,31 @@
|
|
1
1
|
%w[socket handle].each { |lib| require lib }
|
2
|
+
require "optparse"
|
3
|
+
|
4
|
+
# Specified for sake of cleanliness, as it is repeated.
|
5
|
+
DEFAULT_PORT = 4000
|
6
|
+
|
7
|
+
options = {
|
8
|
+
:port => DEFAULT_PORT
|
9
|
+
}
|
10
|
+
|
11
|
+
optparse = OptionParser.new do |opt|
|
12
|
+
opt.banner = "Usage: #{$0} [OPTIONS]"
|
13
|
+
opt.separator ""
|
14
|
+
opt.on "-p", "--port [PORT]", "Select the server port, defaults to #{DEFAULT_PORT}" do |port|
|
15
|
+
options[:port] = port
|
16
|
+
end
|
17
|
+
opt.on_tail "-h", "--help", "Display this help message" do
|
18
|
+
puts optparse
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
optparse.parse! ARGV
|
2
24
|
|
3
25
|
log = Log4r::Logger.new 'main'
|
4
26
|
log.outputters = Log4r::Outputter.stdout
|
5
27
|
begin
|
6
|
-
server = TCPServer.new('localhost',
|
28
|
+
server = TCPServer.new('localhost', options[:port])
|
7
29
|
threads = []
|
8
30
|
|
9
31
|
#The procedures for shuting down the server
|
@@ -25,7 +47,7 @@ begin
|
|
25
47
|
Signal.trap sig, handle_signalled_shutdown
|
26
48
|
end
|
27
49
|
|
28
|
-
log.info "Accepting Connections..."
|
50
|
+
log.info "Accepting Connections on port #{options[:port]}..."
|
29
51
|
while session = server.accept
|
30
52
|
threads << Thread.new(session) do |s|
|
31
53
|
begin
|
data/lib/commands/update.rb
CHANGED
data/lib/controller.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
require 'abbrev'
|
2
|
+
|
1
3
|
module Controller
|
2
4
|
Log = Log4r::Logger.new 'controller'
|
3
5
|
Log.outputters = Log4r::Outputter.stdout
|
4
6
|
|
5
7
|
class NoCommandException < Exception;end
|
6
|
-
|
7
|
-
|
8
|
+
class NoViewException < Exception;end
|
8
9
|
|
9
10
|
class Base
|
11
|
+
|
10
12
|
alias :route :class
|
11
13
|
attr_accessor :input, :body, :rendered
|
12
|
-
attr_accessor :new_route, :default_command, :prompt
|
14
|
+
attr_accessor :new_route, :default_command, :prompt, :redirect, :args
|
13
15
|
|
14
16
|
@@before_view = {}
|
15
17
|
|
@@ -41,10 +43,15 @@ module Controller
|
|
41
43
|
|
42
44
|
#TODO: Implement this.
|
43
45
|
#Should jump to next controller method right after this one finishes
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
def redirect_to(options={})
|
47
|
+
config = {:route => self.class, :command => nil, :prompt => "", :view => :default, :args => ""}
|
48
|
+
config.update options if options.is_a?(Hash)
|
49
|
+
raise Exception.new("Could not redirect to nil. Please supply a command to redirect to.") if config[:command] == nil
|
50
|
+
|
51
|
+
self.redirect = true
|
52
|
+
self.args = config[:args]
|
53
|
+
route_to(config)
|
54
|
+
end
|
48
55
|
|
49
56
|
#render sets the text that will be sent back as a response to this command
|
50
57
|
#you can only render once per controller. This is in place to avoid magical
|
@@ -52,50 +59,80 @@ module Controller
|
|
52
59
|
#
|
53
60
|
#render called with no options yields the view file rendered if one exists.
|
54
61
|
#
|
55
|
-
# render #=> renders /mud/views/controller/view.
|
62
|
+
# render #=> renders /mud/views/controller/view.eruby
|
56
63
|
#
|
57
64
|
# render :text => "send this to them" #=> send this to them
|
58
65
|
def render(options = {})
|
59
66
|
raise "Double Render Error" if self.rendered
|
60
67
|
self.rendered = true
|
61
|
-
|
62
|
-
if options
|
63
|
-
|
68
|
+
config = {:view => @method}
|
69
|
+
config.update options if options.is_a?(Hash)
|
70
|
+
|
71
|
+
return if config[:nothing]
|
72
|
+
|
73
|
+
if config[:text]
|
74
|
+
@body = config[:text]
|
64
75
|
return
|
65
76
|
end
|
66
|
-
|
77
|
+
|
78
|
+
@body = render_template(view_file(config[:view]))
|
67
79
|
end
|
68
80
|
|
81
|
+
def rendered?
|
82
|
+
@rendered
|
83
|
+
end
|
84
|
+
|
69
85
|
def initialize(command, s = {}) #:nodoc:
|
70
|
-
@
|
86
|
+
@method_string = command.downcase
|
71
87
|
@session = s
|
88
|
+
self.redirect = false
|
89
|
+
end
|
90
|
+
|
91
|
+
#simple begins with abbreviation with alphabetical order
|
92
|
+
def find_method(method_string)
|
93
|
+
methods = self.command_methods.sort
|
94
|
+
methods.abbrev[method_string]
|
72
95
|
end
|
96
|
+
|
97
|
+
|
73
98
|
|
74
99
|
#service is the main method that controlls the flow through the controller.
|
75
100
|
#first it runs the method,
|
76
101
|
#then it renders the view if something hasn't already been rendered
|
77
102
|
#then it tacks the prompt onto the end of the command.
|
78
103
|
def service(*a)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
104
|
+
|
105
|
+
@method = find_method(@method_string)
|
106
|
+
if @method == nil
|
107
|
+
render :view=>@method_string
|
108
|
+
else
|
109
|
+
if respond_to? @method
|
110
|
+
if self.method(@method).arity >= 1
|
111
|
+
send(@method, *a)
|
112
|
+
else
|
113
|
+
send(@method)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
if !self.rendered
|
117
|
+
begin
|
118
|
+
render
|
119
|
+
rescue NoViewException
|
120
|
+
if self.redirect
|
121
|
+
return self
|
122
|
+
end
|
123
|
+
raise
|
124
|
+
end
|
84
125
|
end
|
85
126
|
end
|
86
|
-
|
87
|
-
render
|
88
|
-
end
|
89
|
-
|
127
|
+
|
90
128
|
@body += self.prompt if self.prompt
|
91
|
-
|
92
|
-
session[:handle].puts(@body)
|
93
129
|
|
130
|
+
session[:handle].puts @body
|
94
131
|
self
|
95
132
|
end
|
96
133
|
|
97
134
|
def view_file(method)
|
98
|
-
command_file = method + ".
|
135
|
+
command_file = method.to_s + ".eruby"
|
99
136
|
load_and_read_file(command_file)
|
100
137
|
end
|
101
138
|
|
@@ -109,7 +146,8 @@ module Controller
|
|
109
146
|
file.read
|
110
147
|
rescue Errno::ENOENT
|
111
148
|
#if we haven't at least rendered something, we are going to through a nocommand exception here..
|
112
|
-
raise Controller::NoCommandException
|
149
|
+
raise Controller::NoCommandException if !@method
|
150
|
+
raise Controller::NoViewException
|
113
151
|
end
|
114
152
|
|
115
153
|
def view_dir
|
@@ -131,6 +169,23 @@ module Controller
|
|
131
169
|
def Base.before_view
|
132
170
|
return @@before_view
|
133
171
|
end
|
172
|
+
|
173
|
+
class << self
|
174
|
+
def hidden_commands
|
175
|
+
write_inheritable_attribute(:hidden_commands, Controller::Base.public_instance_methods) unless read_inheritable_attribute(:hidden_commands)
|
176
|
+
read_inheritable_attribute(:hidden_commands)
|
177
|
+
end
|
178
|
+
def hide_action(*names)
|
179
|
+
write_inheritable_attribute(:hidden_actions,
|
180
|
+
hidden_actions | names.collect { |n| n.to_s })
|
181
|
+
end
|
182
|
+
end
|
183
|
+
def command_methods
|
184
|
+
self.class.command_methods
|
185
|
+
end
|
186
|
+
def self.command_methods
|
187
|
+
@command_methods ||= Set.new(public_instance_methods - hidden_commands)
|
188
|
+
end
|
134
189
|
end
|
135
190
|
|
136
191
|
class NotFound < Base
|