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,72 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/resolver'
3
+
4
+ class ActiveRecord::Base
5
+ end
6
+
7
+ module Brainstem
8
+ module ApiDocs
9
+ describe Resolver do
10
+ let(:atlas) { Object.new }
11
+ let(:options) { { } }
12
+
13
+ subject { described_class.new(atlas, options) }
14
+
15
+ describe "#initialize" do
16
+ it "requires an atlas" do
17
+ expect { described_class.new }.to raise_error ArgumentError
18
+ expect { described_class.new(atlas) }.not_to raise_error
19
+ end
20
+ end
21
+
22
+ describe "#find_by_class" do
23
+ let(:klass) { Class.new }
24
+ let(:result) { Object.new }
25
+
26
+ context "when activerecord" do
27
+ let(:klass) { Class.new(ActiveRecord::Base) }
28
+
29
+ it "finds the presenter from the target class" do
30
+ mock(subject).find_presenter_from_target_class(klass) { result }
31
+ expect(subject.find_by_class(klass)).to eq result
32
+ end
33
+ end
34
+
35
+ context "when symbol" do
36
+ context "when polymorphic" do
37
+ let(:klass) { :polymorphic }
38
+
39
+ it "returns nil" do
40
+ expect(subject.find_by_class(klass)).to be_nil
41
+ end
42
+ end
43
+ end
44
+
45
+ context "when not found" do
46
+ it "returns nil" do
47
+ expect(subject.find_by_class(klass)).to be_nil
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ describe "#find_presenter_from_target_class" do
54
+ let(:klass) { Class.new }
55
+ let(:presenter_const) { Class.new }
56
+ let(:pclm) { Object.new }
57
+ let(:presenter_wrapper) { OpenStruct.new(const: presenter_const) }
58
+ let(:options) { { presenter_constant_lookup_method: pclm } }
59
+
60
+ before do
61
+ stub(klass).to_s { "Klass" }
62
+ mock(pclm).call("Klass") { presenter_const }
63
+ stub(atlas).presenters { [ presenter_wrapper ] }
64
+ end
65
+
66
+ it "returns the presenter" do
67
+ expect(subject.send(:find_presenter_from_target_class, klass)).to eq presenter_wrapper
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/sinks/abstract_sink'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ module Sinks
7
+ describe AbstractSink do
8
+ describe "#<<" do
9
+ it "is not implemented" do
10
+ expect { subject << Object.new }.to raise_error NotImplementedError
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/sinks/controller_presenter_multifile_sink'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ module Sinks
7
+ describe ControllerPresenterMultifileSink do
8
+ let(:write_method) { Object.new }
9
+ let(:atlas) { Object.new }
10
+ let(:options) {
11
+ {
12
+ format: :markdown,
13
+ write_method: write_method,
14
+ write_path: './'
15
+ }
16
+ }
17
+
18
+ subject { described_class.new(options) }
19
+
20
+ context "controllers" do
21
+ before do
22
+ stub(subject).write_presenter_files
23
+ end
24
+
25
+ it "writes each controller to its own file" do
26
+ stub(atlas).controllers
27
+ .stub!
28
+ .each_formatted_with_filename(:markdown, include_actions: true)
29
+ .yields('it has info', 'test.markdown')
30
+
31
+ mock(write_method).call("./test.markdown", "it has info")
32
+ subject << atlas
33
+ end
34
+ end
35
+
36
+
37
+ context "presenters" do
38
+ before do
39
+ stub(subject).write_controller_files
40
+ end
41
+
42
+
43
+ it "writes each presenter to its own file" do
44
+ stub(atlas).presenters
45
+ .stub!
46
+ .each_formatted_with_filename(:markdown)
47
+ .yields('it has info', 'test.markdown')
48
+
49
+ mock(write_method).call("./test.markdown", "it has info")
50
+ subject << atlas
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/sinks/stdout_sink'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ module Sinks
7
+ describe StdoutSink do
8
+ let(:output) { "Zadok the Priest and Nathan the Prophet" }
9
+ let(:dummy_puts) { Object.new }
10
+
11
+ subject { described_class.new(puts_method: dummy_puts) }
12
+
13
+ describe "#<<" do
14
+ it "calls the putting method" do
15
+ mock(dummy_puts).call(output)
16
+ subject << output
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs'
3
+
4
+ module Brainstem
5
+ describe ApiDocs do
6
+ let(:lorem) { "lorem ipsum dolor sit amet" }
7
+
8
+
9
+ describe "configuration" do
10
+ describe "formatters" do
11
+ it "has a formatters constant" do
12
+ expect(Brainstem::ApiDocs::FORMATTERS).to be_a(Hash)
13
+ end
14
+ end
15
+
16
+
17
+ %w(
18
+ controller_filename_pattern
19
+ presenter_filename_pattern
20
+ controller_filename_link_pattern
21
+ presenter_filename_link_pattern
22
+ write_path
23
+ output_extension
24
+ base_presenter_class
25
+ base_controller_class
26
+ document_empty_presenter_associations
27
+ document_empty_presenter_filters
28
+ ).each do |meth|
29
+ describe meth do
30
+ before do
31
+ @original = Brainstem::ApiDocs.public_send(meth)
32
+ end
33
+
34
+ after do
35
+ Brainstem::ApiDocs.public_send("#{meth}=", @original)
36
+ end
37
+
38
+ it "can be set and read" do
39
+ Brainstem::ApiDocs.public_send("#{meth}=", lorem)
40
+ expect(Brainstem::ApiDocs.public_send(meth)).to eq lorem
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "filename link patterns" do
47
+ it 'defaults to the filename pattern' do
48
+ expect(Brainstem::ApiDocs.public_send("controller_filename_link_pattern")).
49
+ to eq(Brainstem::ApiDocs.public_send("controller_filename_pattern"))
50
+
51
+ expect(Brainstem::ApiDocs.public_send("presenter_filename_link_pattern")).
52
+ to eq(Brainstem::ApiDocs.public_send("presenter_filename_pattern"))
53
+ end
54
+ end
55
+
56
+
57
+ end
58
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/cli/abstract_command'
3
+
4
+ module Brainstem
5
+ module CLI
6
+ describe AbstractCommand do
7
+ let(:options) { { } }
8
+ subject { AbstractCommand.new(options) }
9
+
10
+
11
+ describe ".call" do
12
+ before do
13
+ any_instance_of(AbstractCommand) do |instance|
14
+ stub(instance) do |obj|
15
+ obj.call
16
+ obj.extract_options!
17
+ end
18
+ end
19
+ end
20
+
21
+ it "creates a new instance" do
22
+ mock.proxy(AbstractCommand).new([])
23
+ AbstractCommand.call([])
24
+ end
25
+
26
+ it "passes its args to the instance" do
27
+ mock.proxy(AbstractCommand).new(%w(--silent))
28
+ AbstractCommand.call(%w(--silent))
29
+ end
30
+
31
+ it "calls the new instance" do
32
+ any_instance_of(AbstractCommand) do |instance|
33
+ mock(instance).call
34
+ end
35
+
36
+ AbstractCommand.call([])
37
+ end
38
+
39
+ it "it returns the new instance" do
40
+ expect(AbstractCommand.call).to be_an AbstractCommand
41
+ end
42
+ end
43
+
44
+
45
+ describe "#extract_options!" do
46
+ let(:option_parser) { Object.new }
47
+ let(:args) { %w(--silent --pretend) }
48
+
49
+ it "feeds the args to the class's option parser" do
50
+ mock(option_parser).order!(args)
51
+
52
+ any_instance_of(AbstractCommand) do |instance|
53
+ mock(instance).option_parser { option_parser }
54
+ end
55
+
56
+ AbstractCommand.new(args)
57
+ end
58
+ end
59
+
60
+
61
+ describe "#option_parser" do
62
+ it "is not implemented" do
63
+ expect { AbstractCommand.new }.to raise_error NotImplementedError
64
+ end
65
+ end
66
+
67
+
68
+ describe "#initialize" do
69
+ it "extracts options from args" do
70
+ any_instance_of(AbstractCommand) do |instance|
71
+ mock(instance).extract_options!
72
+ end
73
+
74
+ AbstractCommand.new
75
+ end
76
+ end
77
+
78
+ describe "#call" do
79
+ before do
80
+ any_instance_of(AbstractCommand) do |instance|
81
+ stub(instance).extract_options!
82
+ end
83
+ end
84
+
85
+ it "is not implemented" do
86
+ expect { subject.call }.to raise_error NotImplementedError
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/cli/generate_api_docs_command'
3
+
4
+ module Brainstem
5
+ module CLI
6
+ describe GenerateApiDocsCommand do
7
+ let(:args) { [ ] }
8
+
9
+ subject { GenerateApiDocsCommand.new(args) }
10
+
11
+ describe "options" do
12
+ xcontext "when --markdown" do
13
+ let(:args) { %w(--markdown) }
14
+
15
+ it "sets sink options method to the MarkdownFormatter instantiation" do
16
+ expect(subject.options[:builder]).to have_key :formatter_method
17
+ expect(subject.options[:builder][:formatter_method]).to be_a Method
18
+
19
+ # TODO: If we need to go further here, we can implement .call on
20
+ # the formatter:
21
+ # expect(subject.options[:builder][:formatter_method].owner).to \
22
+ # eq Brainstem::ApiDocs::Formatters::AbstractFormatter
23
+ end
24
+ end
25
+
26
+ context "when --multifile-presenters-and-controllers" do
27
+ let(:args) { %w(--multifile-presenters-and-controllers) }
28
+
29
+ it "sets sink to a ControllerPresenterMultifileSink" do
30
+ expect(subject.options).to have_key :sink
31
+ expect(subject.options[:sink][:method].call).to be_a \
32
+ Brainstem::ApiDocs::Sinks::ControllerPresenterMultifileSink
33
+ end
34
+ end
35
+
36
+ context "when --output-dir" do
37
+ let(:args) { %w(--output-dir=./blah) }
38
+
39
+ it "sets the write_path option of the sink" do
40
+ expect(subject.options[:sink][:options][:write_path]).to eq "./blah"
41
+ end
42
+ end
43
+
44
+ context "when --controller-matches" do
45
+ let(:matches) { subject.options[:builder][:args_for_atlas][:controller_matches] }
46
+
47
+ context "when just one match specified" do
48
+ let(:args) { %w(--controller-matches=/workspaces/) }
49
+ it "creates a case-insensitive regexp from the in-between-slash info" do
50
+ mock.proxy(Regexp).new('workspaces', 'i')
51
+ subject
52
+ end
53
+
54
+ it "appends to the match terms" do
55
+ expect(matches).to include Regexp.new('workspaces', 'i')
56
+ end
57
+ end
58
+
59
+ context "when multiple matches specified" do
60
+ let(:args) { %w(--controller-matches=/workspaces/ --controller-matches=/extra/) }
61
+
62
+ it "allows additional specification and merges the arguments" do
63
+ expect(matches).to include Regexp.new('workspaces', 'i')
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ describe "execution" do
71
+ context "when no sink provided" do
72
+ before do
73
+ any_instance_of(described_class) do |instance|
74
+ stub(instance).default_sink_method { nil }
75
+ end
76
+ end
77
+
78
+ it "raises an error" do
79
+ expect { subject.call }.to raise_error \
80
+ Brainstem::ApiDocs::NoSinkSpecifiedException
81
+ end
82
+ end
83
+
84
+ context "when sink specified" do
85
+ before do
86
+ stub(subject).ensure_sink_specified!
87
+ end
88
+
89
+ describe "builder" do
90
+ let(:builder_options) { { builder: { test: 123 } } }
91
+
92
+ before do
93
+ stub(subject).present_atlas!
94
+ stub(subject).builder_options { builder_options }
95
+ end
96
+
97
+ it "creates a new builder with builder options passed" do
98
+ mock(Brainstem::ApiDocs::Builder).new(builder_options)
99
+ subject.call
100
+ end
101
+ end
102
+
103
+ describe "presentation" do
104
+ it "sends the atlas to the sink" do
105
+ atlas = Object.new
106
+ builder = mock!.atlas.returns(atlas).subject
107
+ sink = mock!.<<(atlas).subject
108
+ sink_method = mock!.call({ test: 123 }).returns(sink).subject
109
+
110
+ stub(subject) do |sub|
111
+ sub.construct_builder!
112
+ sub.builder { builder }
113
+ sub.sink_options { { test: 123 } }
114
+ sub.sink_method { sink_method }
115
+ end
116
+
117
+ subject.call
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end