brainstem 1.1.1 → 1.3.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 (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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6284bb9b32ad35fb5e88a28ada0cb128a533dbae
4
- data.tar.gz: 3f1624b6bde3c0703b3540b70a77566c6312bd7b
3
+ metadata.gz: a5785540a81ca6119478cd1c8567f3c96fa73fa6
4
+ data.tar.gz: c8ea012472c8d23767b446dfb941d0ebff5ec067
5
5
  SHA512:
6
- metadata.gz: '06469a9fd4016afafc4c169208eecf714bfc0159ff11a1f3442eefdcfe7406a5017288f146e25f5d4bdc203705fe3b26986ac8adac1d6112e05775898c7b33d4'
7
- data.tar.gz: 2fccd29843c5b613e288520a9897f3c0dac7a02bcaf5953bf5dfa4bdeacf876711bf3d07cc65c4f1f14dc725b389c4541a1e002dc865ebc435507521686e474a
6
+ metadata.gz: 71a2fd84129676cd27b0d669c94080f92b23417c4d7eb0ccb1366b02e2baa388fd53e0a54d0bbb0f84ea47d139bd54d13bd1e725bafa2bfc613ecb343f7cd119
7
+ data.tar.gz: e5a35cc09a2a048fa30a4633b1c7b66f2d60f56f52e1889e46466983f4b9dcddbd4828859836680b012a68cbca8fbd74ad66b2cacb6ed2e53a810ea160ee7be8
data/CHANGELOG.md CHANGED
@@ -1,12 +1,89 @@
1
1
  # Changelog
2
2
 
3
- + **1.1.1 - _01/15/2017_
4
- - 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.
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
- + **1.1.0 - _12/18/2017_
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.1.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.2)
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.1)
30
+ i18n (0.9.0)
31
31
  concurrent-ruby (~> 1.0)
32
32
  method_source (0.9.0)
33
- minitest (5.11.1)
34
- mysql2 (0.3.18)
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.3.0)
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.1)
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.12)
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, info: "the Widget's name"
70
- field :legacy, :boolean, info: "true for legacy Widgets, false otherwise", via: :legacy?
71
- field :longform_description, :string, info: "feature-length description of this Widget", optional: true
72
- field :updated_at, :datetime, info: "the time of this Widget's last update"
73
- field :created_at, :datetime, info: "the time at which this Widget was created"
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, info: "features associated with this Widget"
82
- association :location, Location, info: "the location of this Widget"
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, info: "the author of the post"
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, info: "the editor of the post", nodoc: true
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
- info: "A trivial example of a param that applies to all actions."
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, info: "The id of the blog to which this post belongs"
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, info: "The id of the blog post"
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, info: "(required) the category's 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
- info: "(optional) the language of the requested post",
583
- nodoc: true
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, info: "(required) the id of the post"
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
- info: "the Groups for the current user",
950
- lookup: lambda { |models|
951
- Group.where(subject_id: models.map(&:id)
952
- .where(user_id: current_user.id)
953
- .group_by { |group| group.subject_id }
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
- info: "count of Posts the current_user has for this model",
967
- lookup: lambda { |models|
968
- lookup = Post.where(subject_id: models.map(&:id)
969
- .where(user_id: current_user.id)
970
- .group_by { |post| post.subject_id }
971
-
972
- lookup
973
- },
974
- lookup_fetch: lambda { |lookup, model| lookup[model.id] }
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"