brainstem 1.1.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +81 -4
  3. data/Gemfile.lock +9 -9
  4. data/README.md +134 -37
  5. data/brainstem.gemspec +1 -1
  6. data/lib/brainstem/api_docs/endpoint.rb +40 -18
  7. data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +27 -22
  8. data/lib/brainstem/api_docs/formatters/markdown/helper.rb +9 -0
  9. data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +14 -6
  10. data/lib/brainstem/api_docs/presenter.rb +3 -7
  11. data/lib/brainstem/concerns/controller_dsl.rb +138 -14
  12. data/lib/brainstem/concerns/presenter_dsl.rb +39 -6
  13. data/lib/brainstem/dsl/array_block_field.rb +25 -0
  14. data/lib/brainstem/dsl/block_field.rb +69 -0
  15. data/lib/brainstem/dsl/configuration.rb +13 -5
  16. data/lib/brainstem/dsl/field.rb +15 -1
  17. data/lib/brainstem/dsl/fields_block.rb +20 -2
  18. data/lib/brainstem/dsl/hash_block_field.rb +30 -0
  19. data/lib/brainstem/presenter.rb +10 -6
  20. data/lib/brainstem/presenter_validator.rb +20 -11
  21. data/lib/brainstem/version.rb +1 -1
  22. data/spec/brainstem/api_docs/endpoint_spec.rb +347 -14
  23. data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +106 -13
  24. data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +19 -0
  25. data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +150 -37
  26. data/spec/brainstem/api_docs/presenter_spec.rb +85 -18
  27. data/spec/brainstem/concerns/controller_dsl_spec.rb +615 -31
  28. data/spec/brainstem/concerns/inheritable_configuration_spec.rb +32 -9
  29. data/spec/brainstem/concerns/presenter_dsl_spec.rb +99 -25
  30. data/spec/brainstem/dsl/array_block_field_spec.rb +43 -0
  31. data/spec/brainstem/dsl/block_field_spec.rb +188 -0
  32. data/spec/brainstem/dsl/field_spec.rb +86 -20
  33. data/spec/brainstem/dsl/hash_block_field_spec.rb +166 -0
  34. data/spec/brainstem/presenter_collection_spec.rb +24 -24
  35. data/spec/brainstem/presenter_spec.rb +233 -9
  36. data/spec/brainstem/query_strategies/filter_and_search_spec.rb +1 -1
  37. data/spec/spec_helpers/presenters.rb +8 -0
  38. data/spec/spec_helpers/schema.rb +13 -0
  39. metadata +15 -6
@@ -129,19 +129,40 @@ module Brainstem
129
129
 
130
130
 
131
131
  describe "#format_params!" do
132
+ let(:const) do
133
+ Class.new do
134
+ def self.brainstem_model_name
135
+ :widget
136
+ end
137
+ end
138
+ end
139
+
132
140
  before do
141
+ stub(controller).const { const }
142
+
133
143
  subject.send(:format_params!)
134
144
  end
135
145
 
136
-
137
146
  context "with valid params" do
138
- let(:show_config) { {
139
- valid_params: {
140
- only: { info: "which ids to include", nodoc: nodoc },
141
- sprocket_id: { info: "the id of the sprocket", root: "widget", nodoc: nodoc },
142
- sprocket_child: { recursive: true, legacy: false, info: "it does the thing", root: "widget" },
147
+ let(:root_proc) { Proc.new { "widget" } }
148
+ let(:sprocket_id_proc) { Proc.new { "sprocket_id" } }
149
+ let(:sprocket_child_proc) { Proc.new { "sprocket_child" } }
150
+ let(:default_show_config) {
151
+ {
152
+ valid_params: {
153
+ only: {
154
+ info: "which ids to include", nodoc: nodoc, type: "array", item_type: "integer"
155
+ },
156
+ sprocket_id_proc => {
157
+ info: "the id of the sprocket", root: root_proc, ancestors: [root_proc], nodoc: nodoc, type: "integer"
158
+ },
159
+ sprocket_child_proc => {
160
+ recursive: true, legacy: false, info: "it does the thing", root: root_proc, ancestors: [root_proc], type: "string"
161
+ },
162
+ }
143
163
  }
144
- } }
164
+ }
165
+ let(:show_config) { default_show_config }
145
166
 
146
167
  context "when nodoc" do
147
168
  let(:nodoc) { true }
@@ -169,12 +190,18 @@ module Brainstem
169
190
 
170
191
  context "for non-root params" do
171
192
  it "outputs sub params under a list item" do
172
- expect(subject.output).to include "- `widget`\n - `sprocket_id` - the id of the sprocket\n - `sprocket_child`"
193
+ expect(subject.output).to include "- `widget` (`Hash`)\n - `sprocket_id` (`Integer`) - the id of the sprocket\n - `sprocket_child` (`String`)"
194
+ end
195
+ end
196
+
197
+ context "for params with type array" do
198
+ it "outputs item type along with the field type" do
199
+ expect(subject.output).to include "- `only` (`Array<Integer>`) - which ids to include\n"
173
200
  end
174
201
  end
175
202
 
176
203
  it "includes the info on a hash key" do
177
- expect(subject.output).to include "`sprocket_child` - it does the thing"
204
+ expect(subject.output).to include "`sprocket_child` (`String`) - it does the thing"
178
205
  end
179
206
 
180
207
  it "includes the recursivity if specified" do
@@ -184,14 +211,82 @@ module Brainstem
184
211
  it "includes the legacy status if specified" do
185
212
  expect(subject.output).to include "Legacy: false"
186
213
  end
214
+
215
+ context "when required option is specified" do
216
+ let(:show_config) {
217
+ default_show_config.tap do |config|
218
+ config[:valid_params][sprocket_id_proc][:required] = required
219
+ end
220
+ }
221
+
222
+ context "when required is true" do
223
+ let(:required) { true }
224
+
225
+ it "includes if required" do
226
+ expect(subject.output).to include "Required: true"
227
+ end
228
+ end
229
+
230
+ context "when required is false" do
231
+ let(:required) { false }
232
+
233
+ it "includes if required" do
234
+ expect(subject.output).to_not include "Required"
235
+ end
236
+ end
237
+ end
238
+
239
+ context "with multiple levels of nested params" do
240
+ let(:sprocket_template_proc) { Proc.new { "sprocket_template" } }
241
+ let(:sprocket_template_json_proc) { Proc.new { "sprocket_template_json" } }
242
+ let(:sprocket_template_title_proc) { Proc.new { "sprocket_template_title" } }
243
+ let(:multi_nested_params) {
244
+ {
245
+ sprocket_template_proc => {
246
+ info: "the template for the sprocket",
247
+ type: "hash",
248
+ root: root_proc,
249
+ ancestors: [root_proc]
250
+ },
251
+ sprocket_template_json_proc => {
252
+ info: "the json blob of the sprocket template",
253
+ type: "string",
254
+ ancestors: [root_proc, sprocket_template_proc]
255
+ },
256
+ sprocket_template_title_proc => {
257
+ info: "the title of the sprocket template",
258
+ type: "string",
259
+ ancestors: [root_proc, sprocket_template_proc]
260
+ }
261
+ }
262
+ }
263
+ let(:show_config) {
264
+ default_show_config.tap do |config|
265
+ config[:valid_params].merge!(multi_nested_params)
266
+ end
267
+ }
268
+
269
+ it "outputs sub params under a list item" do
270
+ output = subject.output
271
+ expect(output).to include("##### Valid Parameters\n\n")
272
+ expect(output).to include("- `only` (`Array<Integer>`) - which ids to include\n")
273
+ expect(output).to include("- `widget` (`Hash`)\n")
274
+ expect(output).to include(" - `sprocket_id` (`Integer`) - the id of the sprocket\n")
275
+ expect(output).to include(" - `sprocket_child` (`String`) - it does the thing\n")
276
+ expect(output).to include(" - Legacy: false\n")
277
+ expect(output).to include(" - Recursive: true\n")
278
+ expect(output).to include(" - `sprocket_template` (`Hash`) - the template for the sprocket\n")
279
+ expect(output).to include(" - `sprocket_template_json` (`String`) - the json blob of the sprocket template\n")
280
+ expect(output).to include(" - `sprocket_template_title` (`String`) - the title of the sprocket template\n\n\n")
281
+ end
282
+ end
187
283
  end
188
284
  end
189
285
 
190
-
191
286
  context "with only default params" do
192
287
  let(:default_config) { {
193
288
  valid_params: {
194
- sprocket_name: {
289
+ Proc.new { "sprocket_name" } => {
195
290
  info: "the name of the sprocket",
196
291
  nodoc: nodoc
197
292
  }
@@ -218,10 +313,8 @@ module Brainstem
218
313
  expect(subject.output).to eq ""
219
314
  end
220
315
  end
221
-
222
316
  end
223
317
 
224
-
225
318
  describe "#format_presents!" do
226
319
  let(:presenter) { Object.new }
227
320
 
@@ -93,6 +93,25 @@ module Brainstem
93
93
  expect(subject.md_a("text", "link.md")).to eq "[text](link.md)"
94
94
  end
95
95
  end
96
+
97
+
98
+ describe "#md_inline_type" do
99
+ it "renders the code between single backticks" do
100
+ expect(subject.md_inline_type("string")).to eq " (`String`)"
101
+ end
102
+
103
+ context "when type is blank" do
104
+ it "returns an empty string" do
105
+ expect(subject.md_inline_type("")).to eq ""
106
+ end
107
+ end
108
+
109
+ context "when item type is specified" do
110
+ it "renders the code between single backticks" do
111
+ expect(subject.md_inline_type("array", "integer")).to eq " (`Array<Integer>`)"
112
+ end
113
+ end
114
+ end
96
115
  end
97
116
  end
98
117
  end
@@ -54,7 +54,6 @@ module Brainstem
54
54
  end
55
55
  end
56
56
 
57
-
58
57
  describe "formatting" do
59
58
  let(:lorem) { "lorem ipsum dolor sit amet" }
60
59
 
@@ -110,39 +109,39 @@ module Brainstem
110
109
 
111
110
 
112
111
  describe "#format_fields!" do
113
- let(:valid_fields) { {} }
112
+ let(:presenter_class) do
113
+ Class.new(Brainstem::Presenter) do
114
+ presents Workspace
115
+ end
116
+ end
117
+ let(:presenter) { Presenter.new(Object.new, const: presenter_class, target_class: 'Workspace') }
114
118
  let(:conditionals) { {} }
115
- let(:optional) { false }
116
-
117
- let(:sprocket_name_long) { OpenStruct.new(
118
- name: :sprocket_name,
119
- description: lorem,
120
- options: { via: :name },
121
- type: :string
122
- ) }
123
-
124
- let(:sprocket_name_short) { OpenStruct.new(
125
- name: :sprocket_name,
126
- type: :string,
127
- options: { }
128
- ) }
129
119
 
130
120
  before do
131
- stub(sprocket_name_long).optional? { optional }
132
- stub(sprocket_name_short).optional? { optional }
133
- stub(presenter).conditionals { conditionals }
134
- stub(presenter).valid_fields { valid_fields }
135
- subject.send(:format_fields!)
121
+ stub(presenter).conditionals { conditionals }
136
122
  end
137
123
 
138
124
  it "outputs a header" do
125
+ presenter_class.fields do
126
+ field :name, :string
127
+ end
128
+ subject.send(:format_fields!)
129
+
139
130
  expect(subject.output).to include "Fields"
140
131
  end
141
132
 
142
133
  context "with fields present" do
143
134
  context "branch node" do
144
135
  context "with single branch" do
145
- let(:valid_fields) { { sprockets: { name: sprocket_name_long } } }
136
+ before do
137
+ presenter_class.fields do
138
+ fields :sprockets do
139
+ field :sprocket_name, :string, via: :name, info: "whatever"
140
+ end
141
+ end
142
+
143
+ subject.send(:format_fields!)
144
+ end
146
145
 
147
146
  it "outputs the name of the branch as a list item" do
148
147
  expect(subject.output.scan(/\n-/).count).to eq 1
@@ -151,13 +150,22 @@ module Brainstem
151
150
  end
152
151
 
153
152
  it "outputs the child nodes as sub-list items" do
154
- expect(subject.output).to \
155
- include("\n- `sprockets`\n - `sprocket_name`")
153
+ expect(subject.output).to include("\n- `sprockets` (`Hash`)\n - `sprocket_name`")
156
154
  end
157
155
  end
158
156
 
159
157
  context "with sub-branch" do
160
- let(:valid_fields) { { sprockets: { sub_sprocket: { name: sprocket_name_long } } } }
158
+ before do
159
+ presenter_class.fields do
160
+ fields :sprockets do
161
+ fields :sub_sprocket do
162
+ field :sprocket_name, :string, via: :name, info: "whatever"
163
+ end
164
+ end
165
+ end
166
+
167
+ subject.send(:format_fields!)
168
+ end
161
169
 
162
170
  it "outputs the name of sub-branches as a sub-list item" do
163
171
  expect(subject.output.scan(/\n-/).count).to eq 1
@@ -168,13 +176,60 @@ module Brainstem
168
176
 
169
177
  it "outputs the child nodes as sub-list items" do
170
178
  expect(subject.output).to \
171
- include("\n- `sprockets`\n - `sub_sprocket`\n - `sprocket_name`")
179
+ include("\n- `sprockets` (`Hash`)\n - `sub_sprocket` (`Hash`)\n - `sprocket_name`")
180
+ end
181
+ end
182
+
183
+ context "when branch has properties" do
184
+ let(:conditionals) do
185
+ {
186
+ :it_is_a_friday => OpenStruct.new(
187
+ description: "it is a friday",
188
+ name: :it_is_a_friday,
189
+ type: :request,
190
+ options: {}
191
+ )
192
+ }
193
+ end
194
+
195
+ before do
196
+ presenter_class.fields do
197
+ fields :sprockets, :array, info: "parent", if: :it_is_a_friday do
198
+ field :sprocket_name, :string, via: :name, info: "whatever"
199
+ end
200
+ end
201
+
202
+ subject.send(:format_fields!)
203
+ end
204
+
205
+ it "outputs the name of the branch as a list item" do
206
+ expect(subject.output.scan(/\n-/).count).to eq 1
207
+ expect(subject.output.scan(/\n -/).count).to eq 3
208
+ expect(subject.output.scan(/\n -/).count).to eq 2
209
+ end
210
+
211
+ it "outputs the parent node with its properties" do
212
+ result = subject.output
213
+
214
+ expect(result).to include("##### Fields\n\n")
215
+ expect(result).to include("- `sprockets` (`Array`)\n")
216
+ expect(result).to include(" - parent\n")
217
+ expect(result).to include(" - visible when it is a friday\n")
218
+ expect(result).to include(" - `sprocket_name` (`String`)\n")
219
+ expect(result).to include(" - whatever\n")
220
+ expect(result).to include(" - visible when it is a friday\n\n\n\n")
172
221
  end
173
222
  end
174
223
  end
175
224
 
176
225
  context "leaf node" do
177
- let(:valid_fields) { { sprocket_name: sprocket_name_long } }
226
+ before do
227
+ presenter_class.fields do
228
+ field :sprocket_name, :string, via: :name
229
+ end
230
+
231
+ subject.send(:format_fields!)
232
+ end
178
233
 
179
234
  context "if it is not conditional" do
180
235
  it "outputs each field as a list item" do
@@ -191,7 +246,16 @@ module Brainstem
191
246
 
192
247
  describe "optional" do
193
248
  context "when true" do
194
- let(:optional) { true }
249
+ before do
250
+ presenter_class.fields do
251
+ field :sprocket_name, :string,
252
+ info: "lorem ipsum dolor sit amet",
253
+ optional: true,
254
+ via: :name
255
+ end
256
+
257
+ subject.send(:format_fields!)
258
+ end
195
259
 
196
260
  it "says so" do
197
261
  expect(subject.output).to include "only returned when requested"
@@ -208,29 +272,58 @@ module Brainstem
208
272
 
209
273
  describe "description" do
210
274
  context "when present" do
275
+ before do
276
+ presenter_class.fields do
277
+ field :sprocket_name, :string, info: "lorem ipsum dolor sit amet"
278
+ end
279
+
280
+ subject.send(:format_fields!)
281
+ end
282
+
211
283
  it "outputs the description" do
212
284
  expect(subject.output).to include " - #{lorem}"
213
285
  end
214
286
  end
215
287
 
216
288
  context "when absent" do
217
- let(:valid_fields) { { sprocket_name: sprocket_name_short } }
289
+ before do
290
+ presenter_class.fields do
291
+ field :sprocket_name, :string
292
+ end
293
+
294
+ subject.send(:format_fields!)
295
+ end
218
296
 
219
297
  it "does not include the description" do
220
298
  expect(subject.output).not_to include " -"
221
299
  end
222
300
  end
223
301
  end
302
+
303
+ describe "when field has an item type" do
304
+ before do
305
+ presenter_class.fields do
306
+ field :sprocket_ids, :array, item_type: :integer
307
+ end
308
+
309
+ subject.send(:format_fields!)
310
+ end
311
+
312
+ it "outputs each field's type along with the sub item type" do
313
+ expect(subject.output).to include "`sprocket_ids` (`Array<Integer>`)"
314
+ end
315
+ end
224
316
  end
225
317
 
226
318
 
227
319
  context "if it is conditional" do
228
- let(:sprocket_name_long) { OpenStruct.new(
229
- name: :sprocket_name,
230
- description: lorem,
231
- options: { via: :name, if: [:it_is_a_friday] },
232
- type: :string
233
- ) }
320
+ before do
321
+ presenter_class.fields do
322
+ field :sprocket_name, :string, if: :it_is_a_friday
323
+ end
324
+
325
+ subject.send(:format_fields!)
326
+ end
234
327
 
235
328
  context "if nodoc" do
236
329
  let(:conditionals) { {
@@ -303,8 +396,9 @@ module Brainstem
303
396
  let(:valid_filters) {
304
397
  {
305
398
  "published" => {
399
+ type: "string",
306
400
  value: Proc.new { nil },
307
- info: "limits to published"
401
+ info: "limits to published"
308
402
  }
309
403
  }
310
404
  }
@@ -315,9 +409,28 @@ module Brainstem
315
409
 
316
410
  it "lists them" do
317
411
  expect(subject.output.scan(/\n-/).count).to eq 1
318
- expect(subject.output).to include "`published`"
412
+ expect(subject.output).to include "`published` (`String`)"
319
413
  expect(subject.output).to include " - limits to published"
320
414
  end
415
+
416
+ context "when items specified" do
417
+ let(:valid_filters) {
418
+ {
419
+ "published" => {
420
+ type: "string",
421
+ value: Proc.new { nil },
422
+ info: "limits to published",
423
+ items: ["fizz", "buzz"]
424
+ }
425
+ }
426
+ }
427
+
428
+ it "lists them with items" do
429
+ expect(subject.output.scan(/\n-/).count).to eq 1
430
+ expect(subject.output).to include "`published` (`String`)"
431
+ expect(subject.output).to include " - limits to published. Available values: fizz, buzz."
432
+ end
433
+ end
321
434
  end
322
435
 
323
436
  context "when no filters" do