brainstem 1.1.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +81 -4
- data/Gemfile.lock +9 -9
- data/README.md +134 -37
- data/brainstem.gemspec +1 -1
- data/lib/brainstem/api_docs/endpoint.rb +40 -18
- data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +27 -22
- data/lib/brainstem/api_docs/formatters/markdown/helper.rb +9 -0
- data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +14 -6
- data/lib/brainstem/api_docs/presenter.rb +3 -7
- data/lib/brainstem/concerns/controller_dsl.rb +138 -14
- data/lib/brainstem/concerns/presenter_dsl.rb +39 -6
- data/lib/brainstem/dsl/array_block_field.rb +25 -0
- data/lib/brainstem/dsl/block_field.rb +69 -0
- data/lib/brainstem/dsl/configuration.rb +13 -5
- data/lib/brainstem/dsl/field.rb +15 -1
- data/lib/brainstem/dsl/fields_block.rb +20 -2
- data/lib/brainstem/dsl/hash_block_field.rb +30 -0
- data/lib/brainstem/presenter.rb +10 -6
- data/lib/brainstem/presenter_validator.rb +20 -11
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/endpoint_spec.rb +347 -14
- data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +106 -13
- data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +19 -0
- data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +150 -37
- data/spec/brainstem/api_docs/presenter_spec.rb +85 -18
- data/spec/brainstem/concerns/controller_dsl_spec.rb +615 -31
- data/spec/brainstem/concerns/inheritable_configuration_spec.rb +32 -9
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +99 -25
- data/spec/brainstem/dsl/array_block_field_spec.rb +43 -0
- data/spec/brainstem/dsl/block_field_spec.rb +188 -0
- data/spec/brainstem/dsl/field_spec.rb +86 -20
- data/spec/brainstem/dsl/hash_block_field_spec.rb +166 -0
- data/spec/brainstem/presenter_collection_spec.rb +24 -24
- data/spec/brainstem/presenter_spec.rb +233 -9
- data/spec/brainstem/query_strategies/filter_and_search_spec.rb +1 -1
- data/spec/spec_helpers/presenters.rb +8 -0
- data/spec/spec_helpers/schema.rb +13 -0
- metadata +15 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5785540a81ca6119478cd1c8567f3c96fa73fa6
|
4
|
+
data.tar.gz: c8ea012472c8d23767b446dfb941d0ebff5ec067
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71a2fd84129676cd27b0d669c94080f92b23417c4d7eb0ccb1366b02e2baa388fd53e0a54d0bbb0f84ea47d139bd54d13bd1e725bafa2bfc613ecb343f7cd119
|
7
|
+
data.tar.gz: e5a35cc09a2a048fa30a4633b1c7b66f2d60f56f52e1889e46466983f4b9dcddbd4828859836680b012a68cbca8fbd74ad66b2cacb6ed2e53a810ea160ee7be8
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,89 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
+ **1.
|
4
|
-
- Add
|
3
|
+
+ **1.3.0** - _04/12/2018_
|
4
|
+
- Add the capability to nest fields under evaluable parent blocks where the nested fields are evaluated
|
5
|
+
with the resulting value of the parent field.
|
6
|
+
```ruby
|
7
|
+
fields :tags, :array,
|
8
|
+
info: "The tags for the given category",
|
9
|
+
dynamic: -> (widget) { widget.tags } do |tag|
|
5
10
|
|
6
|
-
|
11
|
+
tag.field :name, :string,
|
12
|
+
info: "Name of the assigned tag"
|
13
|
+
end
|
14
|
+
```
|
15
|
+
- Add support for nested parameters on an endpoint.
|
16
|
+
```ruby
|
17
|
+
model_params :post do |param|
|
18
|
+
...
|
19
|
+
|
20
|
+
param.valid :message, :string, ...
|
21
|
+
|
22
|
+
param.model_params :rating do |rating_param|
|
23
|
+
...
|
24
|
+
|
25
|
+
rating_param.valid :stars, :integer, ...
|
26
|
+
end
|
27
|
+
|
28
|
+
params.valid :replies, :array,
|
29
|
+
item_type: :hash, ... do |reply_params|
|
30
|
+
|
31
|
+
reply_params.valid :message, :string,
|
32
|
+
info: "the message of the post"
|
33
|
+
|
34
|
+
...
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
- Fix issue with nested fields being unable to have the same name as top level fields.
|
39
|
+
- Support generation of markdown documentation for the nested block fields and nested parameters.
|
40
|
+
|
41
|
+
+ **1.2.0** - _03/29/2018_
|
42
|
+
- Add the capability to indicate an endpoint param is required with the `required` key in the options hash.
|
43
|
+
```ruby
|
44
|
+
params.valid :message, :text,
|
45
|
+
required: true,
|
46
|
+
info: "the message of the post"
|
47
|
+
```
|
48
|
+
- Add support for specifying the type of an endpoint param. For an endpoint param that has type `Array`,
|
49
|
+
type of list items can be specified using `item_type` key in the options hash.
|
50
|
+
```ruby
|
51
|
+
params.valid :viewable_by, :array,
|
52
|
+
item_type: :integer,
|
53
|
+
info: "an array of user ids that can access the post"
|
54
|
+
```
|
55
|
+
- Add support for specifying the data type of an item for a presenter field using `item_type` key in the
|
56
|
+
options hash when the field is of type `Array`.
|
57
|
+
```ruby
|
58
|
+
field :aliases, :array,
|
59
|
+
item_type: :string,
|
60
|
+
info: "an array of user ids that can access the post"
|
61
|
+
```
|
62
|
+
- Include the type and item type when generating markdown documentation for endpoint params.
|
63
|
+
- Specify the data type of a filter and available values with `items` key in the options hash. If filter is an array,
|
64
|
+
data type of items can be specified with the `item_type` property in options.
|
65
|
+
```ruby
|
66
|
+
filter :status, :string,
|
67
|
+
items: ['Started', 'Completed'],
|
68
|
+
info: "only returns elements with the given status"
|
69
|
+
|
70
|
+
filter :sprocket_ids, :array,
|
71
|
+
item_type: :integer,
|
72
|
+
info: "returns objects associated with given sprocket Ids"
|
73
|
+
```
|
74
|
+
- Add support for generating markdown documentation for the following:
|
75
|
+
- when the `required` option is specified on an endpoint param
|
76
|
+
- when the `type` and `item_type` params are specified on the endpoint param
|
77
|
+
- when the `type` and `item_type` params are specified on a presenter field
|
78
|
+
- when the `type` and `items` params are specified on a presenter filter
|
79
|
+
|
80
|
+
+ **1.1.1** - _01/15/2017_
|
81
|
+
- Add `Brainstem.mysql_use_calc_found_rows` boolean config option to utilize MySQL's [FOUND_ROWS()](https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_found-rows) functionality to avoid issuing a new query to calculate the record count, which has the potential to up to double the response time of the endpoint.
|
82
|
+
|
83
|
+
+ **1.1.0** - _12/18/2017_
|
7
84
|
- Add `meta` key to API responses which includes `page_number`, `page_count`, and `page_size` keys.
|
8
85
|
|
9
|
-
+ **1.0.0 - _07/20/2017_
|
86
|
+
+ **1.0.0** - _07/20/2017_
|
10
87
|
- Add the capability to generate the documentation extracted from your properly annotated
|
11
88
|
presenters and controllers using `bundle exec brainstem generate [ARGS]`.
|
12
89
|
- Update Brainstem to use Ruby version 2.3.3.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
brainstem (1.
|
4
|
+
brainstem (1.3.0)
|
5
5
|
activerecord (>= 4.1)
|
6
6
|
activesupport (>= 4.1)
|
7
7
|
|
@@ -22,30 +22,30 @@ GEM
|
|
22
22
|
arel (8.0.0)
|
23
23
|
coderay (1.1.2)
|
24
24
|
concurrent-ruby (1.0.5)
|
25
|
-
database_cleaner (1.6.
|
25
|
+
database_cleaner (1.6.1)
|
26
26
|
db-query-matchers (0.9.0)
|
27
27
|
activesupport (>= 4.0, <= 6.0)
|
28
28
|
rspec (~> 3.0)
|
29
29
|
diff-lcs (1.3)
|
30
|
-
i18n (0.9.
|
30
|
+
i18n (0.9.0)
|
31
31
|
concurrent-ruby (~> 1.0)
|
32
32
|
method_source (0.9.0)
|
33
|
-
minitest (5.
|
34
|
-
mysql2 (0.
|
33
|
+
minitest (5.10.3)
|
34
|
+
mysql2 (0.4.10)
|
35
35
|
pry (0.9.12.6)
|
36
36
|
coderay (~> 1.0)
|
37
37
|
method_source (~> 0.8)
|
38
38
|
slop (~> 3.4)
|
39
39
|
pry-nav (0.2.4)
|
40
40
|
pry (>= 0.9.10, < 0.11.0)
|
41
|
-
rake (12.
|
41
|
+
rake (12.2.1)
|
42
42
|
redcarpet (3.4.0)
|
43
43
|
rr (1.2.1)
|
44
44
|
rspec (3.7.0)
|
45
45
|
rspec-core (~> 3.7.0)
|
46
46
|
rspec-expectations (~> 3.7.0)
|
47
47
|
rspec-mocks (~> 3.7.0)
|
48
|
-
rspec-core (3.7.
|
48
|
+
rspec-core (3.7.0)
|
49
49
|
rspec-support (~> 3.7.0)
|
50
50
|
rspec-expectations (3.7.0)
|
51
51
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -59,7 +59,7 @@ GEM
|
|
59
59
|
thread_safe (0.3.6)
|
60
60
|
tzinfo (1.2.4)
|
61
61
|
thread_safe (~> 0.1)
|
62
|
-
yard (0.9.
|
62
|
+
yard (0.9.9)
|
63
63
|
|
64
64
|
PLATFORMS
|
65
65
|
ruby
|
@@ -68,7 +68,7 @@ DEPENDENCIES
|
|
68
68
|
brainstem!
|
69
69
|
database_cleaner
|
70
70
|
db-query-matchers
|
71
|
-
mysql2
|
71
|
+
mysql2 (= 0.4.10)
|
72
72
|
pry
|
73
73
|
pry-nav
|
74
74
|
rake
|
data/README.md
CHANGED
@@ -50,13 +50,13 @@ module Api
|
|
50
50
|
default_sort_order "updated_at:desc"
|
51
51
|
|
52
52
|
# Optional filter that applies a lambda.
|
53
|
-
filter :location_name do |scope, location_name|
|
53
|
+
filter :location_name, :string, items: [:sf, :la] do |scope, location_name|
|
54
54
|
scope.joins(:locations).where("locations.name = ?", location_name)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Filter with an overridable default. This will run on every request,
|
58
58
|
# passing in `bool` as `false` unless a user has specified otherwise.
|
59
|
-
filter :include_legacy_widgets, default: false do |scope, bool|
|
59
|
+
filter :include_legacy_widgets, :boolean, default: false do |scope, bool|
|
60
60
|
bool ? scope : scope.without_legacy_widgets
|
61
61
|
end
|
62
62
|
|
@@ -66,11 +66,54 @@ module Api
|
|
66
66
|
|
67
67
|
# Specify the fields to be present in the returned JSON.
|
68
68
|
fields do
|
69
|
-
field :name, :string,
|
70
|
-
|
71
|
-
field :
|
72
|
-
|
73
|
-
|
69
|
+
field :name, :string,
|
70
|
+
info: "the Widget's name"
|
71
|
+
field :legacy, :boolean,
|
72
|
+
info: "true for legacy Widgets, false otherwise",
|
73
|
+
via: :legacy?
|
74
|
+
field :longform_description, :string,
|
75
|
+
info: "feature-length description of this Widget",
|
76
|
+
optional: true
|
77
|
+
field :aliases, :array,
|
78
|
+
item_type: :string,
|
79
|
+
info: "the differnt aliases for the widget"
|
80
|
+
field :updated_at, :datetime,
|
81
|
+
info: "the time of this Widget's last update"
|
82
|
+
field :created_at, :datetime,
|
83
|
+
info: "the time at which this Widget was created"
|
84
|
+
|
85
|
+
# Fields can be nested under non-evaluable parent fields where the nested fields
|
86
|
+
# are evaluated with the presented model.
|
87
|
+
fields :permissions, :hash do |permissions_field|
|
88
|
+
|
89
|
+
# Since the permissions parent field is not evaluable, the can_edit? method is
|
90
|
+
# evaluated with the presented Widget model.
|
91
|
+
permissions_field.field :can_edit, :boolean,
|
92
|
+
via: :can_edit?,
|
93
|
+
info: "Indicates if the user can edit the widget"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Specify nested fields within an evaluable parent block field. A parent block field
|
97
|
+
# is evaluable only if one of the following options :via, :dynamic or :lookup is specified.
|
98
|
+
# The nested fields are evaluated with the value of the parent.
|
99
|
+
fields :tags, :array,
|
100
|
+
item_type: :hash,
|
101
|
+
info: "The tags for the given category",
|
102
|
+
dynamic: -> (widget) { widget.tags } do |tag|
|
103
|
+
|
104
|
+
# The name method will be evaluated with each tag model returned by the the parent block.
|
105
|
+
tag.field :name, :string,
|
106
|
+
info: "Name of the assigned tag"
|
107
|
+
end
|
108
|
+
|
109
|
+
fields :primary_category, :hash,
|
110
|
+
via: :primary_category,
|
111
|
+
info: "The primary category of the widget" do |category|
|
112
|
+
|
113
|
+
# The title method will be evaluated with each category model returned by the parent block.
|
114
|
+
category.field :title, :string,
|
115
|
+
info: "The title of the category"
|
116
|
+
end
|
74
117
|
end
|
75
118
|
|
76
119
|
# Associations can be included by providing include=association_name in the URL.
|
@@ -78,8 +121,10 @@ module Api
|
|
78
121
|
# columns on the model, otherwise the user must explicitly request associations
|
79
122
|
# to avoid unnecessary loads.
|
80
123
|
associations do
|
81
|
-
association :features, Feature,
|
82
|
-
|
124
|
+
association :features, Feature,
|
125
|
+
info: "features associated with this Widget"
|
126
|
+
association :location, Location,
|
127
|
+
info: "the location of this Widget"
|
83
128
|
end
|
84
129
|
end
|
85
130
|
end
|
@@ -447,11 +492,14 @@ class PostsPresenter < Brainstem::Presenter
|
|
447
492
|
MARKDOWN
|
448
493
|
|
449
494
|
associations do
|
450
|
-
association :author, User,
|
495
|
+
association :author, User,
|
496
|
+
info: "the author of the post"
|
451
497
|
|
452
498
|
# Temporarily disable documenting this relationship as we revamp the
|
453
499
|
# editorial system:
|
454
|
-
association :editor, User,
|
500
|
+
association :editor, User,
|
501
|
+
info: "the editor of the post",
|
502
|
+
nodoc: true
|
455
503
|
end
|
456
504
|
end
|
457
505
|
```
|
@@ -489,17 +537,19 @@ context, and it will keep the documentation isolated to that specific action:
|
|
489
537
|
|
490
538
|
```ruby
|
491
539
|
brainstem_params do
|
492
|
-
valid :global_controller_param,
|
493
|
-
|
540
|
+
valid :global_controller_param, :string,
|
541
|
+
info: "A trivial example of a param that applies to all actions."
|
494
542
|
|
495
543
|
actions :index do
|
496
544
|
# This adds a `blog_id` param to just the `index` action.
|
497
|
-
valid :blog_id,
|
545
|
+
valid :blog_id, :integer,
|
546
|
+
info: "The id of the blog to which this post belongs"
|
498
547
|
end
|
499
548
|
|
500
549
|
actions :create, :update do
|
501
550
|
# This will add an `id` param to both `create` and `update` actions.
|
502
|
-
valid :id,
|
551
|
+
valid :id, :integer,
|
552
|
+
info: "The id of the blog post"
|
503
553
|
end
|
504
554
|
end
|
505
555
|
```
|
@@ -575,21 +625,55 @@ class BlogPostsController < ApiController
|
|
575
625
|
brainstem_params do
|
576
626
|
|
577
627
|
# Add an `:category_id` param to all actions in this controller / children:
|
578
|
-
valid :category_id,
|
628
|
+
valid :category_id, :integer,
|
629
|
+
info: "(required) the category's ID"
|
579
630
|
|
580
631
|
# Do not document this additional field.
|
581
|
-
valid :lang,
|
582
|
-
|
583
|
-
|
632
|
+
valid :lang, :string,
|
633
|
+
info: "(optional) the language of the requested post",
|
634
|
+
nodoc: true
|
635
|
+
|
584
636
|
|
585
637
|
actions :show do
|
586
638
|
# Declare a nested param under the `brainstem_model_name` root key,
|
587
639
|
# i.e. `params[:blog_post][:id]`):
|
588
640
|
model_params do |post|
|
589
|
-
post.valid :id,
|
641
|
+
post.valid :id, :integer,
|
642
|
+
info: "the id of the post", required: true
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
|
647
|
+
actions :create do
|
648
|
+
model_params :post do |params|
|
649
|
+
params.valid :message, :string,
|
650
|
+
info: "the message of the post",
|
651
|
+
required: true
|
652
|
+
|
653
|
+
params.valid :viewable_by, :array,
|
654
|
+
item_type: :integer,
|
655
|
+
info: "an array of user ids that can access the post"
|
656
|
+
|
657
|
+
# Declare a nested param with an explicit root key:, i.e. `params[:rating][...]`
|
658
|
+
model_params :rating do |rating_param|
|
659
|
+
rating_param.valid :stars, :integer,
|
660
|
+
info: "the rating of the post"
|
661
|
+
end
|
662
|
+
|
663
|
+
# Declare nested array params with an explicit key:, i.e. `params[:replies][0][...]`
|
664
|
+
params.valid :replies, :array,
|
665
|
+
item_type: :hash,
|
666
|
+
info: "an array of reply params that can be created along with the post" do |reply_params|
|
667
|
+
reply_params.valid :message, :string,
|
668
|
+
info: "the message of the post"
|
669
|
+
reply_params.valid :replier_id, :integer,
|
670
|
+
info: "the ID of the user"
|
671
|
+
...
|
672
|
+
end
|
590
673
|
end
|
591
674
|
end
|
592
675
|
|
676
|
+
|
593
677
|
actions :share do
|
594
678
|
# Declare a nested param with an explicit root key:, i.e. `params[:share][...]`
|
595
679
|
model_param :share do
|
@@ -826,13 +910,13 @@ Brainstem provides a rich DSL for building presenters. This section details the
|
|
826
910
|
|
827
911
|
```ruby
|
828
912
|
# Optional filter that applies a lambda.
|
829
|
-
filter :location_name do |scope, location_name|
|
913
|
+
filter :location_name, :string do |scope, location_name|
|
830
914
|
scope.joins(:locations).where("locations.name = ?", location_name)
|
831
915
|
end
|
832
916
|
|
833
917
|
# Filter with an overridable default. This will run on every request,
|
834
918
|
# passing in `bool` as `false` unless a user has specified otherwise.
|
835
|
-
filter :include_legacy_widgets, default: false do |scope, bool|
|
919
|
+
filter :include_legacy_widgets, :boolean, default: false do |scope, bool|
|
836
920
|
bool ? scope : scope.without_legacy_widgets
|
837
921
|
end
|
838
922
|
```
|
@@ -892,6 +976,9 @@ Brainstem provides a rich DSL for building presenters. This section details the
|
|
892
976
|
field :dynamic_name, :string,
|
893
977
|
info: "a formatted name for this Widget",
|
894
978
|
dynamic: lambda { |widget| "This Widget's name is #{widget.name}" }
|
979
|
+
field :aliases, :array,
|
980
|
+
item_type: :string,
|
981
|
+
info: "the differnt aliases for the widget"
|
895
982
|
field :longform_description, :string,
|
896
983
|
info: "feature-length description of this Widget",
|
897
984
|
optional: true
|
@@ -900,6 +987,16 @@ Brainstem provides a rich DSL for building presenters. This section details the
|
|
900
987
|
fields :permissions do
|
901
988
|
field :access_level, :integer
|
902
989
|
end
|
990
|
+
|
991
|
+
# Fields can be nested under executable parent blocks.
|
992
|
+
# Sub fields are evaluated with the value of the parent block.
|
993
|
+
fields :tags, :array,
|
994
|
+
info: "The tags for the given category",
|
995
|
+
dynamic: -> (widget) { widget.tags } do |tag|
|
996
|
+
|
997
|
+
tag.field :name, :string,
|
998
|
+
info: "Name of the assigned tag"
|
999
|
+
end
|
903
1000
|
end
|
904
1001
|
```
|
905
1002
|
|
@@ -946,12 +1043,12 @@ the `lookup` will be used.
|
|
946
1043
|
```ruby
|
947
1044
|
associations do
|
948
1045
|
association :current_user_groups, Group,
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
1046
|
+
info: "the Groups for the current user",
|
1047
|
+
lookup: lambda { |models|
|
1048
|
+
Group.where(subject_id: models.map(&:id)
|
1049
|
+
.where(user_id: current_user.id)
|
1050
|
+
.group_by { |group| group.subject_id }
|
1051
|
+
}
|
955
1052
|
end
|
956
1053
|
```
|
957
1054
|
|
@@ -963,15 +1060,15 @@ the `lookup` will be used.
|
|
963
1060
|
```ruby
|
964
1061
|
fields do
|
965
1062
|
field :current_user_post_count, Post,
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
1063
|
+
info: "count of Posts the current_user has for this model",
|
1064
|
+
lookup: lambda { |models|
|
1065
|
+
lookup = Post.where(subject_id: models.map(&:id)
|
1066
|
+
.where(user_id: current_user.id)
|
1067
|
+
.group_by { |post| post.subject_id }
|
1068
|
+
|
1069
|
+
lookup
|
1070
|
+
},
|
1071
|
+
lookup_fetch: lambda { |lookup, model| lookup[model.id] }
|
975
1072
|
end
|
976
1073
|
```
|
977
1074
|
|
data/brainstem.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |gem|
|
|
27
27
|
gem.add_development_dependency "rr"
|
28
28
|
gem.add_development_dependency "rspec", "~> 3.5"
|
29
29
|
gem.add_development_dependency "sqlite3"
|
30
|
-
gem.add_development_dependency "mysql2"
|
30
|
+
gem.add_development_dependency "mysql2", "0.4.10"
|
31
31
|
gem.add_development_dependency "database_cleaner"
|
32
32
|
gem.add_development_dependency "yard"
|
33
33
|
gem.add_development_dependency "pry"
|