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,100 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/builder'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ describe Builder do
7
+ let(:dummy_environment_file) do
8
+ File.expand_path('../../../../spec/dummy/rails.rb', __FILE__)
9
+ end
10
+
11
+ let(:default_options) do
12
+ { args_for_introspector: { rails_environment_file: dummy_environment_file } }
13
+ end
14
+
15
+ let(:options) { {} }
16
+
17
+ subject { Builder.new(default_options.merge(options)) }
18
+
19
+
20
+ describe "#initialize" do
21
+ let(:introspector_method) { Object.new }
22
+ let(:options) { { introspector_method: introspector_method } }
23
+
24
+ before do
25
+ any_instance_of(Builder) do |instance|
26
+ stub(instance) do |k|
27
+ k.build_introspector!
28
+ k.build_atlas!
29
+ end
30
+ end
31
+ end
32
+
33
+ it "sets all valid values given to it as options" do
34
+ expect(subject.introspector_method).to eq introspector_method
35
+ end
36
+ end
37
+
38
+
39
+ describe "introspection" do
40
+ let(:introspector_method) { Object.new }
41
+ let(:args_for_introspector) { { blah: true } }
42
+ let(:introspector) { Object.new }
43
+ let(:options) do
44
+ {
45
+ args_for_introspector: args_for_introspector,
46
+ introspector_method: introspector_method
47
+ }
48
+ end
49
+
50
+ before do
51
+ any_instance_of(Builder) do |instance|
52
+ stub(instance) do |k|
53
+ k.build_atlas!
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ it "passes the introspector method the introspector options" do
60
+ mock(introspector_method).call(args_for_introspector)
61
+ subject
62
+ end
63
+
64
+ it "creates an introspector" do
65
+ stub(introspector_method).call(args_for_introspector) { introspector }
66
+ expect(subject.introspector).to eq introspector
67
+ end
68
+ end
69
+
70
+
71
+ describe "modeling" do
72
+ let(:introspector) { Object.new }
73
+ let(:atlas_method) { Object.new }
74
+ let(:args_for_atlas) { { blah: true } }
75
+ let(:atlas) { Object.new }
76
+ let(:options) { { atlas_method: atlas_method, args_for_atlas: args_for_atlas } }
77
+
78
+ before do
79
+ any_instance_of(Builder) do |instance|
80
+ stub(instance) do |inst|
81
+ inst.introspector { introspector }
82
+ inst.build_introspector!
83
+ inst.build_formatter!
84
+ end
85
+ end
86
+ end
87
+
88
+ it "passes the atlas method the introspector and the atlas options" do
89
+ mock(atlas_method).call(introspector, args_for_atlas)
90
+ subject
91
+ end
92
+
93
+ it "creates an atlas" do
94
+ mock(atlas_method).call(introspector, args_for_atlas) { atlas }
95
+ expect(subject.atlas).to eq atlas
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/controller_collection'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ describe ControllerCollection do
7
+ let(:controller) { Object.new }
8
+ let(:atlas) { Object.new }
9
+ let(:options) { {} }
10
+
11
+ subject { described_class.new(atlas, options) }
12
+
13
+ describe "#find_by_route" do
14
+ before do
15
+ stub(controller) do |c|
16
+ c.path { "/posts" }
17
+ c.const { Object }
18
+ c.action { "index" }
19
+ end
20
+
21
+ subject << controller
22
+ end
23
+
24
+ context "when matches route" do
25
+ it "returns the matching controller" do
26
+ route = { controller: Object }
27
+ expect(subject.find_by_route(route)).to eq controller
28
+ end
29
+ end
30
+
31
+ context "when does not match route" do
32
+ it "returns nil" do
33
+ route = { controller: TrueClass }
34
+ expect(subject.find_by_route(route)).to eq nil
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ describe "#create_from_route" do
41
+ it "creates a new controller, adding it to the members" do
42
+ controller = subject.create_from_route(
43
+ path: "/posts",
44
+ controller: Object,
45
+ action: "index",
46
+ controller_name: "object"
47
+ )
48
+
49
+ expect(subject.first).to eq controller
50
+ expect(controller.const).to eq Object
51
+ expect(controller.name).to eq "object"
52
+ expect(controller.endpoints).to \
53
+ be_a Brainstem::ApiDocs::EndpointCollection
54
+ expect(controller.endpoints.count).to eq 0
55
+ end
56
+ end
57
+
58
+
59
+ describe "#find_or_create_from_route" do
60
+ let!(:existing_controller) { subject.create_from_route(
61
+ path: "/posts",
62
+ controller: Object,
63
+ action: "index",
64
+ controller_name: "object"
65
+ ) }
66
+
67
+ context "when has matching controller" do
68
+ let(:route) { { controller: Object, controller_name: "object" } }
69
+
70
+ it "returns that controller" do
71
+ subject.find_or_create_from_route(route)
72
+ expect(subject.count).to eq 1
73
+ expect(subject.last).to eq existing_controller
74
+ end
75
+ end
76
+
77
+ context "when no matching controller" do
78
+ let(:route) { { controller: TrueClass, controller_name: "true_class" } }
79
+
80
+ it "returns a new controller" do
81
+ new_controller = subject.find_or_create_from_route(route)
82
+ expect(subject.count).to eq 2
83
+ expect(subject.last).to eq new_controller
84
+ end
85
+ end
86
+ end
87
+
88
+ it_behaves_like "formattable"
89
+ it_behaves_like "atlas taker"
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/controller'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ describe Controller do
7
+ subject { described_class.new(atlas, options) }
8
+ let(:atlas) { Object.new }
9
+ let(:options) { {} }
10
+
11
+ describe "#initialize" do
12
+ it "yields self if given a block" do
13
+ block = Proc.new { |s| s.name = "bork bork" }
14
+ expect(described_class.new(atlas, &block).name).to eq "bork bork"
15
+ end
16
+ end
17
+
18
+
19
+ describe "#add_endpoint" do
20
+ let(:endpoint) { Object.new }
21
+
22
+ it "adds the endpoint to its list" do
23
+ expect(subject.endpoints.count).to eq 0
24
+ subject.add_endpoint(endpoint)
25
+ expect(subject.endpoints.count).to eq 1
26
+ end
27
+ end
28
+
29
+
30
+ describe "derived fields" do
31
+ let(:lorem) { "lorem ipsum dolor sit amet" }
32
+ let(:const) { Object.new }
33
+ let(:default_config) { {} }
34
+ let(:show_config) { {} }
35
+ let(:nodoc) { false }
36
+ let(:options) { { const: const } }
37
+
38
+ before do
39
+ stub(const) do |constant|
40
+ constant.configuration { {
41
+ :_default => default_config,
42
+ :show => show_config
43
+ } }
44
+
45
+ constant.to_s { "Api::V1::ClassName" }
46
+ end
47
+ end
48
+
49
+
50
+ describe "configuration helpers" do
51
+ describe "#contextual_documentation" do
52
+ let(:default_config) { { title: { info: info, nodoc: nodoc } } }
53
+ let(:info) { lorem }
54
+
55
+ context "when has the key" do
56
+ let(:key) { :title }
57
+
58
+ context "when not nodoc" do
59
+ context "when has info" do
60
+ it "is truthy" do
61
+ expect(subject.contextual_documentation(key)).to be_truthy
62
+ end
63
+
64
+ it "is the info" do
65
+ expect(subject.contextual_documentation(key)).to eq lorem
66
+ end
67
+ end
68
+
69
+ context "when has no info" do
70
+ let(:info) { nil }
71
+
72
+ it "is falsey" do
73
+ expect(subject.contextual_documentation(key)).to be_falsey
74
+ end
75
+ end
76
+ end
77
+
78
+ context "when nodoc" do
79
+ let(:nodoc) { true }
80
+
81
+ it "is falsey" do
82
+ expect(subject.contextual_documentation(key)).to be_falsey
83
+ end
84
+ end
85
+ end
86
+
87
+ context "when doesn't have the key" do
88
+ let(:key) { :herp }
89
+
90
+ it "is falsey" do
91
+ expect(subject.contextual_documentation(key)).to be_falsey
92
+ end
93
+ end
94
+ end
95
+
96
+
97
+ describe "#default_configuration" do
98
+ let(:default_config) { { title: nil } }
99
+
100
+ it "returns the default key of the configuration" do
101
+ expect(subject.default_configuration).to eq default_config
102
+ end
103
+ end
104
+ end
105
+
106
+
107
+ describe "#nodoc?" do
108
+ let(:default_config) { { nodoc: nodoc } }
109
+
110
+ context "when nodoc in default" do
111
+ let(:nodoc) { true }
112
+
113
+ it "is true" do
114
+ expect(subject.nodoc?).to eq true
115
+ end
116
+ end
117
+
118
+ context "when not nodoc in default" do
119
+ it "is false" do
120
+ expect(subject.nodoc?).to eq false
121
+ end
122
+ end
123
+ end
124
+
125
+
126
+ describe "#title" do
127
+ context "when present" do
128
+ let(:default_config) { { title: { info: lorem, nodoc: nodoc } } }
129
+
130
+ context "when nodoc" do
131
+ let(:nodoc) { true }
132
+
133
+ it "falls back to the controller class" do
134
+ expect(subject.title).to eq "ClassName"
135
+ end
136
+ end
137
+
138
+ context "when documentable" do
139
+ it "shows the title" do
140
+ expect(subject.title).to eq lorem
141
+ end
142
+ end
143
+ end
144
+
145
+ context "when absent" do
146
+ it "falls back to the controller class" do
147
+ expect(subject.title).to eq "ClassName"
148
+ end
149
+ end
150
+ end
151
+
152
+
153
+ describe "#description" do
154
+ context "when present" do
155
+ let(:default_config) { { description: { info: lorem, nodoc: nodoc } } }
156
+
157
+ context "when nodoc" do
158
+ let(:nodoc) { true }
159
+
160
+ it "shows nothing" do
161
+ expect(subject.description).to eq ""
162
+ end
163
+ end
164
+
165
+ context "when documentable" do
166
+ it "shows the description" do
167
+ expect(subject.description).to eq lorem
168
+ end
169
+ end
170
+ end
171
+
172
+ context "when absent" do
173
+ it "shows nothing" do
174
+ expect(subject.description).to eq ""
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+
181
+ describe "#suggested_filename" do
182
+ let(:const) { Object.new }
183
+
184
+ before do
185
+ stub(const) do |constant|
186
+ constant.to_s { "Api::V1::ClassName" }
187
+ end
188
+ end
189
+
190
+
191
+ it "gsubs namespace, filename and extension" do
192
+ instance = described_class.new(atlas,
193
+ filename_pattern: "controllers/{{namespace}}/{{name}}_controller.{{extension}}",
194
+ name: 'api/v1/abc',
195
+ const: const,
196
+ )
197
+
198
+ stub(instance).extension { "xyz" }
199
+
200
+ expect(instance.suggested_filename(:xyz)).to \
201
+ eq "controllers/api/v1/abc_controller.xyz"
202
+ end
203
+ end
204
+
205
+
206
+ describe "#suggested_filename_link" do
207
+ it "gsubs filename and extension" do
208
+
209
+ instance = described_class.new(atlas,
210
+ filename_link_pattern: "controllers/{{name}}_controller.{{extension}}.foo",
211
+ name: 'abc'
212
+ )
213
+
214
+ stub(instance).extension { "xyz" }
215
+
216
+ expect(instance.suggested_filename_link(:xyz)).to eq "controllers/abc_controller.xyz.foo"
217
+ end
218
+ end
219
+
220
+
221
+ it_behaves_like "formattable"
222
+ it_behaves_like "atlas taker"
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/endpoint_collection'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ describe EndpointCollection do
7
+ let(:atlas) { Object.new }
8
+ let(:options) { {} }
9
+ let(:endpoint) { Object.new }
10
+
11
+ subject { described_class.new(atlas, options) }
12
+
13
+
14
+ describe "#find_from_route" do
15
+ let(:controller) { stub!.const { Object }.subject }
16
+
17
+ before do
18
+ stub(endpoint) do |c|
19
+ c.path { "/posts" }
20
+ c.http_methods { ["GET", "POST"] }
21
+ c.controller { controller }
22
+ c.controller_name { "object" }
23
+ c.action { "index" }
24
+ end
25
+
26
+ subject << endpoint
27
+ end
28
+
29
+ context "when matches route" do
30
+ it "returns the matching controller" do
31
+ route = { controller: Object, path: "/posts", action: "index" }
32
+ expect(subject.find_by_route(route)).to eq endpoint
33
+ end
34
+ end
35
+
36
+ context "when does not match route" do
37
+ it "returns nil" do
38
+ routes = [
39
+ { controller: TrueClass, path: "/posts", action: "index" },
40
+ { controller: Object, path: "/wrong", action: "index" },
41
+ { controller: Object, path: "/posts", action: "wrong" },
42
+ ]
43
+
44
+ routes.each do |route|
45
+ expect(subject.find_by_route(route)).to eq nil
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ describe "#create_from_route" do
53
+ it "creates a new endpoint, adding it to the members" do
54
+ controller = Object.new
55
+
56
+ endpoint = subject.create_from_route({
57
+ path: "/posts",
58
+ http_methods: ["GET"],
59
+ controller_name: "object",
60
+ action: "index",
61
+ }, controller)
62
+
63
+ expect(subject.first).to eq endpoint
64
+ expect(endpoint.controller).to eq controller
65
+ expect(endpoint.controller_name).to eq "object"
66
+ expect(endpoint.action).to eq "index"
67
+ expect(endpoint.path).to eq "/posts"
68
+ expect(endpoint.http_methods).to eq ["GET"]
69
+ end
70
+ end
71
+
72
+
73
+ describe "#only_documentable" do
74
+ let(:endpoint_2) { Object.new }
75
+
76
+ before do
77
+ stub(endpoint).nodoc? { false }
78
+ stub(endpoint_2).nodoc? { true }
79
+
80
+ subject << endpoint
81
+ subject << endpoint_2
82
+ end
83
+
84
+ it "returns a new collection with members that are not nodoc" do
85
+ new_clxn = subject.only_documentable
86
+
87
+ expect(new_clxn).to be_a described_class
88
+ expect(new_clxn).to include endpoint
89
+ expect(new_clxn).not_to include endpoint_2
90
+ end
91
+ end
92
+
93
+
94
+ describe "#with_declared_presented_class" do
95
+ let(:endpoint_2) { Object.new }
96
+
97
+ before do
98
+ stub(endpoint).declared_presented_class { Class.new }
99
+ stub(endpoint_2).declared_presented_class { nil }
100
+ subject << endpoint
101
+ subject << endpoint_2
102
+ end
103
+
104
+ it "returns a new collection with members that have declared presents" do
105
+ new_clxn = subject.with_declared_presented_class
106
+
107
+ expect(new_clxn).to be_a described_class
108
+ expect(new_clxn).to include endpoint
109
+ expect(new_clxn).not_to include endpoint_2
110
+ end
111
+ end
112
+
113
+
114
+ describe "#with_actions_in_controller" do
115
+ let(:endpoint_2) { Object.new }
116
+ let(:const) do
117
+ Class.new do
118
+ def show
119
+ end
120
+ end
121
+ end
122
+
123
+ before do
124
+ stub(endpoint).action { :show }
125
+ stub(endpoint_2).action { :index }
126
+ subject << endpoint
127
+ subject << endpoint_2
128
+ end
129
+
130
+ it "returns a new collection with members that the const responds to" do
131
+ new_clxn = subject.with_actions_in_controller(const)
132
+
133
+ expect(new_clxn).to be_a described_class
134
+ expect(new_clxn).to include endpoint
135
+ expect(new_clxn).not_to include endpoint_2
136
+ end
137
+ end
138
+
139
+
140
+ it_behaves_like "formattable"
141
+ it_behaves_like "atlas taker"
142
+ end
143
+ end
144
+ end