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.
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