brainstem 1.0.0.pre.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +383 -32
- data/bin/brainstem +6 -0
- data/brainstem.gemspec +2 -0
- data/docs/api_doc_generator.markdown +175 -0
- data/docs/brainstem_executable.markdown +32 -0
- data/docs/docgen.png +0 -0
- data/docs/docgen_ascii.txt +63 -0
- data/docs/executable.png +0 -0
- data/docs/executable_ascii.txt +10 -0
- data/lib/brainstem/api_docs.rb +146 -0
- data/lib/brainstem/api_docs/abstract_collection.rb +116 -0
- data/lib/brainstem/api_docs/atlas.rb +158 -0
- data/lib/brainstem/api_docs/builder.rb +167 -0
- data/lib/brainstem/api_docs/controller.rb +122 -0
- data/lib/brainstem/api_docs/controller_collection.rb +40 -0
- data/lib/brainstem/api_docs/endpoint.rb +234 -0
- data/lib/brainstem/api_docs/endpoint_collection.rb +58 -0
- data/lib/brainstem/api_docs/exceptions.rb +8 -0
- data/lib/brainstem/api_docs/formatters/abstract_formatter.rb +64 -0
- data/lib/brainstem/api_docs/formatters/markdown/controller_formatter.rb +76 -0
- data/lib/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter.rb +73 -0
- data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +169 -0
- data/lib/brainstem/api_docs/formatters/markdown/helper.rb +76 -0
- data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +200 -0
- data/lib/brainstem/api_docs/introspectors/abstract_introspector.rb +100 -0
- data/lib/brainstem/api_docs/introspectors/rails_introspector.rb +232 -0
- data/lib/brainstem/api_docs/presenter.rb +225 -0
- data/lib/brainstem/api_docs/presenter_collection.rb +97 -0
- data/lib/brainstem/api_docs/resolver.rb +73 -0
- data/lib/brainstem/api_docs/sinks/abstract_sink.rb +37 -0
- data/lib/brainstem/api_docs/sinks/controller_presenter_multifile_sink.rb +93 -0
- data/lib/brainstem/api_docs/sinks/stdout_sink.rb +44 -0
- data/lib/brainstem/cli.rb +146 -0
- data/lib/brainstem/cli/abstract_command.rb +97 -0
- data/lib/brainstem/cli/generate_api_docs_command.rb +169 -0
- data/lib/brainstem/concerns/controller_dsl.rb +300 -0
- data/lib/brainstem/concerns/controller_param_management.rb +30 -9
- data/lib/brainstem/concerns/formattable.rb +38 -0
- data/lib/brainstem/concerns/inheritable_configuration.rb +3 -2
- data/lib/brainstem/concerns/optional.rb +43 -0
- data/lib/brainstem/concerns/presenter_dsl.rb +76 -15
- data/lib/brainstem/controller_methods.rb +6 -3
- data/lib/brainstem/dsl/association.rb +6 -3
- data/lib/brainstem/dsl/associations_block.rb +6 -3
- data/lib/brainstem/dsl/base_block.rb +2 -4
- data/lib/brainstem/dsl/conditional.rb +7 -3
- data/lib/brainstem/dsl/conditionals_block.rb +4 -4
- data/lib/brainstem/dsl/configuration.rb +184 -8
- data/lib/brainstem/dsl/field.rb +6 -3
- data/lib/brainstem/dsl/fields_block.rb +2 -3
- data/lib/brainstem/help_text.txt +8 -0
- data/lib/brainstem/presenter.rb +27 -6
- data/lib/brainstem/presenter_validator.rb +5 -2
- data/lib/brainstem/time_classes.rb +1 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/abstract_collection_spec.rb +156 -0
- data/spec/brainstem/api_docs/atlas_spec.rb +353 -0
- data/spec/brainstem/api_docs/builder_spec.rb +100 -0
- data/spec/brainstem/api_docs/controller_collection_spec.rb +92 -0
- data/spec/brainstem/api_docs/controller_spec.rb +225 -0
- data/spec/brainstem/api_docs/endpoint_collection_spec.rb +144 -0
- data/spec/brainstem/api_docs/endpoint_spec.rb +346 -0
- data/spec/brainstem/api_docs/formatters/abstract_formatter_spec.rb +30 -0
- data/spec/brainstem/api_docs/formatters/markdown/controller_formatter_spec.rb +126 -0
- data/spec/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter_spec.rb +85 -0
- data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +261 -0
- data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +100 -0
- data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +485 -0
- data/spec/brainstem/api_docs/introspectors/abstract_introspector_spec.rb +192 -0
- data/spec/brainstem/api_docs/introspectors/rails_introspector_spec.rb +170 -0
- data/spec/brainstem/api_docs/presenter_collection_spec.rb +84 -0
- data/spec/brainstem/api_docs/presenter_spec.rb +519 -0
- data/spec/brainstem/api_docs/resolver_spec.rb +72 -0
- data/spec/brainstem/api_docs/sinks/abstract_sink_spec.rb +16 -0
- data/spec/brainstem/api_docs/sinks/controller_presenter_multifile_sink_spec.rb +56 -0
- data/spec/brainstem/api_docs/sinks/stdout_sink_spec.rb +22 -0
- data/spec/brainstem/api_docs_spec.rb +58 -0
- data/spec/brainstem/cli/abstract_command_spec.rb +91 -0
- data/spec/brainstem/cli/generate_api_docs_command_spec.rb +125 -0
- data/spec/brainstem/cli_spec.rb +67 -0
- data/spec/brainstem/concerns/controller_dsl_spec.rb +471 -0
- data/spec/brainstem/concerns/controller_param_management_spec.rb +36 -16
- data/spec/brainstem/concerns/formattable_spec.rb +30 -0
- data/spec/brainstem/concerns/inheritable_configuration_spec.rb +104 -4
- data/spec/brainstem/concerns/optional_spec.rb +48 -0
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +202 -31
- data/spec/brainstem/dsl/association_spec.rb +18 -2
- data/spec/brainstem/dsl/conditional_spec.rb +25 -2
- data/spec/brainstem/dsl/configuration_spec.rb +1 -1
- data/spec/brainstem/dsl/field_spec.rb +18 -2
- data/spec/brainstem/presenter_collection_spec.rb +10 -2
- data/spec/brainstem/presenter_spec.rb +32 -0
- data/spec/brainstem/presenter_validator_spec.rb +12 -7
- data/spec/dummy/rails.rb +49 -0
- data/spec/shared/atlas_taker.rb +18 -0
- data/spec/shared/formattable.rb +14 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helpers/db.rb +1 -1
- data/spec/spec_helpers/presenters.rb +20 -14
- metadata +106 -6
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'active_support/inflector/inflections'
|
2
|
+
require 'brainstem/api_docs/abstract_collection'
|
3
|
+
require 'brainstem/api_docs/presenter'
|
4
|
+
require 'brainstem/concerns/formattable'
|
5
|
+
|
6
|
+
module Brainstem
|
7
|
+
module ApiDocs
|
8
|
+
class PresenterCollection < AbstractCollection
|
9
|
+
include Concerns::Formattable
|
10
|
+
|
11
|
+
|
12
|
+
def valid_options
|
13
|
+
super | [ :presenter_constant_lookup_method ]
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_writer :presenter_constant_lookup_method
|
17
|
+
|
18
|
+
|
19
|
+
#
|
20
|
+
# Finds or creates a presenter with the given target class and appends it to the
|
21
|
+
# members list if it is new.
|
22
|
+
#
|
23
|
+
def find_or_create_from_target_class(target_class)
|
24
|
+
find_by_target_class(target_class) ||
|
25
|
+
create_from_target_class(target_class)
|
26
|
+
end
|
27
|
+
alias_method :find_or_create_by_target_class,
|
28
|
+
:find_or_create_from_target_class
|
29
|
+
|
30
|
+
|
31
|
+
#
|
32
|
+
# Finds a presenter for the given class
|
33
|
+
#
|
34
|
+
def find_by_target_class(target_class)
|
35
|
+
find { |p| p.target_class == target_class }
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# Creates a new +Presenter+ wrapper and appends it to the collection. If
|
41
|
+
# the constant lookup for the actual presenter class fails, returns nil.
|
42
|
+
#
|
43
|
+
def create_from_target_class(target_class)
|
44
|
+
::Brainstem::ApiDocs::Presenter.new(atlas,
|
45
|
+
target_class: target_class,
|
46
|
+
const: target_class_to_const(target_class)
|
47
|
+
).tap { |p| self.<< p }
|
48
|
+
rescue KeyError
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def find_or_create_from_presenter_collection(target_class, const)
|
54
|
+
find_by_target_class(target_class) ||
|
55
|
+
create_from_presenter_collection(target_class, const)
|
56
|
+
end
|
57
|
+
alias_method :find_or_create_by_presenter_collection,
|
58
|
+
:find_or_create_from_presenter_collection
|
59
|
+
|
60
|
+
|
61
|
+
def create_from_presenter_collection(target_class, const)
|
62
|
+
::Brainstem::ApiDocs::Presenter.new(atlas,
|
63
|
+
target_class: target_class,
|
64
|
+
const: const
|
65
|
+
).tap { |p| self.<< p }
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
#########################################################################
|
70
|
+
private
|
71
|
+
#########################################################################
|
72
|
+
|
73
|
+
#
|
74
|
+
# Converts a target class into a presenter constant. Raises an error
|
75
|
+
# if not found.
|
76
|
+
#
|
77
|
+
def target_class_to_const(target_class)
|
78
|
+
presenter_constant_lookup_method.call(target_class.to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
#
|
83
|
+
# A callable method by which presenter constants can be looked up from
|
84
|
+
# their human name.
|
85
|
+
#
|
86
|
+
# In future, it might be worth unifying this method with `find_by_class`
|
87
|
+
# to reduce total surface.
|
88
|
+
#
|
89
|
+
def presenter_constant_lookup_method
|
90
|
+
@presenter_constant_lookup_method ||= \
|
91
|
+
Brainstem.presenter_collection.presenters.method(:fetch)
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'brainstem/concerns/optional'
|
2
|
+
|
3
|
+
#
|
4
|
+
# The Resolver is responsible for taking a non-ApiDocs class and turning it
|
5
|
+
# into an ApiDocs wrapper class (such as a Presenter, Controller, or Endpoint).
|
6
|
+
#
|
7
|
+
# It's useful for doing object lookups for associated objects where we only
|
8
|
+
# have the original class or constant to look up the relationship.
|
9
|
+
#
|
10
|
+
module Brainstem
|
11
|
+
module ApiDocs
|
12
|
+
class Resolver
|
13
|
+
include Brainstem::Concerns::Optional
|
14
|
+
|
15
|
+
|
16
|
+
def valid_options
|
17
|
+
[
|
18
|
+
:presenter_constant_lookup_method,
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(atlas, options = {})
|
23
|
+
self.atlas = atlas
|
24
|
+
super options
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
attr_accessor :atlas,
|
29
|
+
:presenter_constant_lookup_method
|
30
|
+
|
31
|
+
|
32
|
+
def find_by_class(klass)
|
33
|
+
if klass == :polymorphic
|
34
|
+
nil
|
35
|
+
elsif klass < ActiveRecord::Base
|
36
|
+
find_presenter_from_target_class(klass)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
#########################################################################
|
42
|
+
private
|
43
|
+
#########################################################################
|
44
|
+
|
45
|
+
def find_presenter_from_target_class(klass)
|
46
|
+
const = presenter_target_class_to_const(klass)
|
47
|
+
atlas.presenters.find {|p| p.const == const }
|
48
|
+
rescue
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
#
|
54
|
+
# Converts a class into a presenter constant. Raises an error
|
55
|
+
# if not found.
|
56
|
+
#
|
57
|
+
def presenter_target_class_to_const(target_class)
|
58
|
+
presenter_constant_lookup_method.call(target_class.to_s)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
#
|
63
|
+
# A callable method by which presenter constants can be looked up from
|
64
|
+
# their human name.
|
65
|
+
#
|
66
|
+
def presenter_constant_lookup_method
|
67
|
+
@presenter_constant_lookup_method ||= \
|
68
|
+
Brainstem.presenter_collection.presenters.method(:fetch)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'brainstem/concerns/optional'
|
2
|
+
|
3
|
+
module Brainstem
|
4
|
+
module ApiDocs
|
5
|
+
module Sinks
|
6
|
+
class AbstractSink
|
7
|
+
include Concerns::Optional
|
8
|
+
|
9
|
+
|
10
|
+
#
|
11
|
+
# Primary method for putting the atlas into the sink.
|
12
|
+
#
|
13
|
+
# @param [Brainstem::ApiDocs::Atlas] the atlas
|
14
|
+
#
|
15
|
+
def <<(atlas)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
#######################################################################$
|
21
|
+
private
|
22
|
+
########################################################################
|
23
|
+
|
24
|
+
|
25
|
+
#
|
26
|
+
# Whitelist of options which can be set on an instance.
|
27
|
+
#
|
28
|
+
# @return [Array<Symbol>] valid options
|
29
|
+
#
|
30
|
+
def valid_options
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'brainstem/api_docs/sinks/abstract_sink'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Brainstem
|
6
|
+
module ApiDocs
|
7
|
+
module Sinks
|
8
|
+
class ControllerPresenterMultifileSink < AbstractSink
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
delegate [:controllers, :presenters] => :atlas
|
12
|
+
|
13
|
+
|
14
|
+
def <<(atlas)
|
15
|
+
self.atlas = atlas
|
16
|
+
|
17
|
+
write_controller_files
|
18
|
+
write_presenter_files
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def valid_options
|
23
|
+
super | [ :write_method, :format, :write_path ]
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
attr_writer :write_method,
|
28
|
+
:write_path
|
29
|
+
|
30
|
+
attr_accessor :atlas,
|
31
|
+
:format
|
32
|
+
|
33
|
+
|
34
|
+
#######################################################################
|
35
|
+
private
|
36
|
+
#######################################################################
|
37
|
+
|
38
|
+
#
|
39
|
+
# Dumps each formatted controller to a file.
|
40
|
+
#
|
41
|
+
def write_controller_files
|
42
|
+
controllers.each_formatted_with_filename(
|
43
|
+
format,
|
44
|
+
include_actions: true,
|
45
|
+
&method(:write_buffer_to_file)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
#
|
51
|
+
# Dumps each formatted presenter to a file.
|
52
|
+
#
|
53
|
+
def write_presenter_files
|
54
|
+
presenters.each_formatted_with_filename(format, &method(:write_buffer_to_file))
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# Writes a given bufer to a filename within the base path.
|
60
|
+
#
|
61
|
+
def write_buffer_to_file(buffer, filename)
|
62
|
+
abs_path = File.join(write_path, filename)
|
63
|
+
assert_directory_exists!(abs_path)
|
64
|
+
write_method.call(abs_path, buffer)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
#
|
69
|
+
# Asserts that a directory exists, creating it if it does not.
|
70
|
+
#
|
71
|
+
def assert_directory_exists!(path)
|
72
|
+
dir = File.dirname(path)
|
73
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
#
|
78
|
+
# Defines how we write out the files.
|
79
|
+
#
|
80
|
+
def write_method
|
81
|
+
@write_method ||= Proc.new do |name, buff|
|
82
|
+
File.write(name, buff, mode: 'w')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def write_path
|
88
|
+
@write_path ||= ::Brainstem::ApiDocs.write_path
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'brainstem/api_docs/sinks/abstract_sink'
|
2
|
+
|
3
|
+
module Brainstem
|
4
|
+
module ApiDocs
|
5
|
+
module Sinks
|
6
|
+
class StdoutSink < AbstractSink
|
7
|
+
|
8
|
+
#
|
9
|
+
# Writes the output using stdout.puts.
|
10
|
+
#
|
11
|
+
def <<(output)
|
12
|
+
puts_method.call(output)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
########################################################################
|
17
|
+
private
|
18
|
+
########################################################################
|
19
|
+
|
20
|
+
|
21
|
+
def valid_options
|
22
|
+
super | [ :puts_method ]
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
#
|
27
|
+
# Storage for holding the writing method.
|
28
|
+
#
|
29
|
+
attr_writer :puts_method
|
30
|
+
|
31
|
+
|
32
|
+
#
|
33
|
+
# Callable method for writing data to a buffer (by default stdout).
|
34
|
+
#
|
35
|
+
# @return [Proc] a method which writes data to a buffer when called.
|
36
|
+
#
|
37
|
+
def puts_method
|
38
|
+
@puts_method ||= $stdout.method(:puts)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# Require all CLI commands.
|
2
|
+
Dir.glob(File.expand_path('../cli/**/*.rb', __FILE__)).each { |f| require f }
|
3
|
+
require 'brainstem/concerns/optional'
|
4
|
+
|
5
|
+
|
6
|
+
#
|
7
|
+
# General manager for CLI requests. Takes incoming user input and routes to a
|
8
|
+
# subcommand.
|
9
|
+
module Brainstem
|
10
|
+
class Cli
|
11
|
+
include Concerns::Optional
|
12
|
+
|
13
|
+
EXECUTABLE_NAME = 'brainstem'
|
14
|
+
|
15
|
+
#
|
16
|
+
# Convenience for instantiating and calling the Cli object.
|
17
|
+
#
|
18
|
+
# @return [Brainstem::Cli] the created instance
|
19
|
+
#
|
20
|
+
def self.call(args, options = {})
|
21
|
+
new(args, options).call
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
#
|
26
|
+
# Creates a new instance of the Cli to respond to user input.
|
27
|
+
#
|
28
|
+
# Input is expected to be the name of the subcommand, followed by any
|
29
|
+
# additional arguments.
|
30
|
+
#
|
31
|
+
def initialize(args, options = {})
|
32
|
+
super options
|
33
|
+
|
34
|
+
self._args = args.dup.freeze
|
35
|
+
self.requested_command = args.shift
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# Routes to an application endpoint depending on given options.
|
41
|
+
#
|
42
|
+
# @return [Brainstem::Cli] the instance
|
43
|
+
#
|
44
|
+
def call
|
45
|
+
if requested_command && commands.has_key?(requested_command)
|
46
|
+
self.command_method = commands[requested_command].method(:call)
|
47
|
+
end
|
48
|
+
|
49
|
+
command_method.call(_args.drop(1))
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
#
|
56
|
+
# Holds a copy of the initial given args for debugging purposes.
|
57
|
+
#
|
58
|
+
attr_accessor :_args
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# Storage for the extracted command.
|
63
|
+
#
|
64
|
+
attr_accessor :requested_command
|
65
|
+
|
66
|
+
|
67
|
+
################################################################################
|
68
|
+
private
|
69
|
+
################################################################################
|
70
|
+
|
71
|
+
|
72
|
+
#
|
73
|
+
# A whitelist of valid options that can be applied to the instance.
|
74
|
+
#
|
75
|
+
# @returns [Array<String>]
|
76
|
+
#
|
77
|
+
def valid_options
|
78
|
+
super | [
|
79
|
+
:log_method
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
#
|
85
|
+
# A basic routing table where the keys are the command to invoke, and where
|
86
|
+
# the value is a callable object or class that will be called with the
|
87
|
+
# +command_options+.
|
88
|
+
#
|
89
|
+
# @return [Hash] A hash of +'command' => Callable+
|
90
|
+
#
|
91
|
+
def commands
|
92
|
+
{ 'generate' => Brainstem::CLI::GenerateApiDocsCommand }
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
# Retrieves the help text and subs any placeholder values.
|
98
|
+
#
|
99
|
+
def help_text
|
100
|
+
@help_text ||= File.read(File.expand_path('../help_text.txt', __FILE__))
|
101
|
+
.gsub('EXECUTABLE_NAME', EXECUTABLE_NAME)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
#
|
106
|
+
# Stores the method we should call to run the user command.
|
107
|
+
#
|
108
|
+
attr_writer :command_method
|
109
|
+
|
110
|
+
|
111
|
+
#
|
112
|
+
# Reader for the method to invoke. By default, will output the help text
|
113
|
+
# when called.
|
114
|
+
#
|
115
|
+
# @return [Proc] An object responding to +#call+ that is used as the main
|
116
|
+
# point execution.
|
117
|
+
#
|
118
|
+
def command_method
|
119
|
+
@command_method ||= Proc.new do
|
120
|
+
# By default, serve help.
|
121
|
+
log_method.call(help_text)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
#
|
127
|
+
# Stores the method we should use to log messages.
|
128
|
+
#
|
129
|
+
attr_writer :log_method
|
130
|
+
|
131
|
+
|
132
|
+
#
|
133
|
+
# Reader for the method to log. By default, will print to stdout when
|
134
|
+
# called.
|
135
|
+
#
|
136
|
+
# We return a proc here because it's much tricker to make assertions
|
137
|
+
# against stdout than it is to allow ourselves to inject a proc.
|
138
|
+
#
|
139
|
+
# @return [Proc] An object responding to +#call+ that is used to print
|
140
|
+
# information.
|
141
|
+
#
|
142
|
+
def log_method
|
143
|
+
@log_method ||= $stdout.method(:puts)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|