brainstem 1.0.0.pre.1 → 1.0.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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +383 -32
  5. data/bin/brainstem +6 -0
  6. data/brainstem.gemspec +2 -0
  7. data/docs/api_doc_generator.markdown +175 -0
  8. data/docs/brainstem_executable.markdown +32 -0
  9. data/docs/docgen.png +0 -0
  10. data/docs/docgen_ascii.txt +63 -0
  11. data/docs/executable.png +0 -0
  12. data/docs/executable_ascii.txt +10 -0
  13. data/lib/brainstem/api_docs.rb +146 -0
  14. data/lib/brainstem/api_docs/abstract_collection.rb +116 -0
  15. data/lib/brainstem/api_docs/atlas.rb +158 -0
  16. data/lib/brainstem/api_docs/builder.rb +167 -0
  17. data/lib/brainstem/api_docs/controller.rb +122 -0
  18. data/lib/brainstem/api_docs/controller_collection.rb +40 -0
  19. data/lib/brainstem/api_docs/endpoint.rb +234 -0
  20. data/lib/brainstem/api_docs/endpoint_collection.rb +58 -0
  21. data/lib/brainstem/api_docs/exceptions.rb +8 -0
  22. data/lib/brainstem/api_docs/formatters/abstract_formatter.rb +64 -0
  23. data/lib/brainstem/api_docs/formatters/markdown/controller_formatter.rb +76 -0
  24. data/lib/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter.rb +73 -0
  25. data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +169 -0
  26. data/lib/brainstem/api_docs/formatters/markdown/helper.rb +76 -0
  27. data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +200 -0
  28. data/lib/brainstem/api_docs/introspectors/abstract_introspector.rb +100 -0
  29. data/lib/brainstem/api_docs/introspectors/rails_introspector.rb +232 -0
  30. data/lib/brainstem/api_docs/presenter.rb +225 -0
  31. data/lib/brainstem/api_docs/presenter_collection.rb +97 -0
  32. data/lib/brainstem/api_docs/resolver.rb +73 -0
  33. data/lib/brainstem/api_docs/sinks/abstract_sink.rb +37 -0
  34. data/lib/brainstem/api_docs/sinks/controller_presenter_multifile_sink.rb +93 -0
  35. data/lib/brainstem/api_docs/sinks/stdout_sink.rb +44 -0
  36. data/lib/brainstem/cli.rb +146 -0
  37. data/lib/brainstem/cli/abstract_command.rb +97 -0
  38. data/lib/brainstem/cli/generate_api_docs_command.rb +169 -0
  39. data/lib/brainstem/concerns/controller_dsl.rb +300 -0
  40. data/lib/brainstem/concerns/controller_param_management.rb +30 -9
  41. data/lib/brainstem/concerns/formattable.rb +38 -0
  42. data/lib/brainstem/concerns/inheritable_configuration.rb +3 -2
  43. data/lib/brainstem/concerns/optional.rb +43 -0
  44. data/lib/brainstem/concerns/presenter_dsl.rb +76 -15
  45. data/lib/brainstem/controller_methods.rb +6 -3
  46. data/lib/brainstem/dsl/association.rb +6 -3
  47. data/lib/brainstem/dsl/associations_block.rb +6 -3
  48. data/lib/brainstem/dsl/base_block.rb +2 -4
  49. data/lib/brainstem/dsl/conditional.rb +7 -3
  50. data/lib/brainstem/dsl/conditionals_block.rb +4 -4
  51. data/lib/brainstem/dsl/configuration.rb +184 -8
  52. data/lib/brainstem/dsl/field.rb +6 -3
  53. data/lib/brainstem/dsl/fields_block.rb +2 -3
  54. data/lib/brainstem/help_text.txt +8 -0
  55. data/lib/brainstem/presenter.rb +27 -6
  56. data/lib/brainstem/presenter_validator.rb +5 -2
  57. data/lib/brainstem/time_classes.rb +1 -1
  58. data/lib/brainstem/version.rb +1 -1
  59. data/spec/brainstem/api_docs/abstract_collection_spec.rb +156 -0
  60. data/spec/brainstem/api_docs/atlas_spec.rb +353 -0
  61. data/spec/brainstem/api_docs/builder_spec.rb +100 -0
  62. data/spec/brainstem/api_docs/controller_collection_spec.rb +92 -0
  63. data/spec/brainstem/api_docs/controller_spec.rb +225 -0
  64. data/spec/brainstem/api_docs/endpoint_collection_spec.rb +144 -0
  65. data/spec/brainstem/api_docs/endpoint_spec.rb +346 -0
  66. data/spec/brainstem/api_docs/formatters/abstract_formatter_spec.rb +30 -0
  67. data/spec/brainstem/api_docs/formatters/markdown/controller_formatter_spec.rb +126 -0
  68. data/spec/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter_spec.rb +85 -0
  69. data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +261 -0
  70. data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +100 -0
  71. data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +485 -0
  72. data/spec/brainstem/api_docs/introspectors/abstract_introspector_spec.rb +192 -0
  73. data/spec/brainstem/api_docs/introspectors/rails_introspector_spec.rb +170 -0
  74. data/spec/brainstem/api_docs/presenter_collection_spec.rb +84 -0
  75. data/spec/brainstem/api_docs/presenter_spec.rb +519 -0
  76. data/spec/brainstem/api_docs/resolver_spec.rb +72 -0
  77. data/spec/brainstem/api_docs/sinks/abstract_sink_spec.rb +16 -0
  78. data/spec/brainstem/api_docs/sinks/controller_presenter_multifile_sink_spec.rb +56 -0
  79. data/spec/brainstem/api_docs/sinks/stdout_sink_spec.rb +22 -0
  80. data/spec/brainstem/api_docs_spec.rb +58 -0
  81. data/spec/brainstem/cli/abstract_command_spec.rb +91 -0
  82. data/spec/brainstem/cli/generate_api_docs_command_spec.rb +125 -0
  83. data/spec/brainstem/cli_spec.rb +67 -0
  84. data/spec/brainstem/concerns/controller_dsl_spec.rb +471 -0
  85. data/spec/brainstem/concerns/controller_param_management_spec.rb +36 -16
  86. data/spec/brainstem/concerns/formattable_spec.rb +30 -0
  87. data/spec/brainstem/concerns/inheritable_configuration_spec.rb +104 -4
  88. data/spec/brainstem/concerns/optional_spec.rb +48 -0
  89. data/spec/brainstem/concerns/presenter_dsl_spec.rb +202 -31
  90. data/spec/brainstem/dsl/association_spec.rb +18 -2
  91. data/spec/brainstem/dsl/conditional_spec.rb +25 -2
  92. data/spec/brainstem/dsl/configuration_spec.rb +1 -1
  93. data/spec/brainstem/dsl/field_spec.rb +18 -2
  94. data/spec/brainstem/presenter_collection_spec.rb +10 -2
  95. data/spec/brainstem/presenter_spec.rb +32 -0
  96. data/spec/brainstem/presenter_validator_spec.rb +12 -7
  97. data/spec/dummy/rails.rb +49 -0
  98. data/spec/shared/atlas_taker.rb +18 -0
  99. data/spec/shared/formattable.rb +14 -0
  100. data/spec/spec_helper.rb +2 -0
  101. data/spec/spec_helpers/db.rb +1 -1
  102. data/spec/spec_helpers/presenters.rb +20 -14
  103. 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