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