praxis 2.0.pre.15 → 2.0.pre.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/.simplecov +3 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +19 -0
  6. data/CONTRIBUTING.md +2 -79
  7. data/Gemfile +5 -1
  8. data/Guardfile +6 -4
  9. data/LICENSE +0 -2
  10. data/MAINTAINERS.md +1 -0
  11. data/README.md +15 -22
  12. data/Rakefile +4 -2
  13. data/bin/praxis +55 -58
  14. data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
  15. data/lib/praxis/action_definition.rb +65 -95
  16. data/lib/praxis/api_definition.rb +21 -29
  17. data/lib/praxis/api_general_info.rb +55 -66
  18. data/lib/praxis/application.rb +15 -32
  19. data/lib/praxis/blueprint.rb +79 -73
  20. data/lib/praxis/bootloader.rb +24 -33
  21. data/lib/praxis/bootloader_stages/environment.rb +5 -10
  22. data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
  23. data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
  24. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
  25. data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
  26. data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
  27. data/lib/praxis/bootloader_stages/routing.rb +5 -8
  28. data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
  29. data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
  30. data/lib/praxis/callbacks.rb +12 -11
  31. data/lib/praxis/collection.rb +10 -13
  32. data/lib/praxis/config.rb +17 -28
  33. data/lib/praxis/config_hash.rb +2 -1
  34. data/lib/praxis/controller.rb +7 -6
  35. data/lib/praxis/dispatcher.rb +34 -42
  36. data/lib/praxis/docs/open_api/info_object.rb +11 -8
  37. data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
  38. data/lib/praxis/docs/open_api/operation_object.rb +6 -3
  39. data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
  40. data/lib/praxis/docs/open_api/paths_object.rb +11 -9
  41. data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
  42. data/lib/praxis/docs/open_api/response_object.rb +24 -18
  43. data/lib/praxis/docs/open_api/responses_object.rb +3 -1
  44. data/lib/praxis/docs/open_api/schema_object.rb +16 -16
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +6 -3
  47. data/lib/praxis/docs/open_api_generator.rb +92 -95
  48. data/lib/praxis/endpoint_definition.rb +60 -77
  49. data/lib/praxis/error_handler.rb +2 -2
  50. data/lib/praxis/exception.rb +2 -0
  51. data/lib/praxis/exceptions/config.rb +3 -1
  52. data/lib/praxis/exceptions/config_load.rb +2 -0
  53. data/lib/praxis/exceptions/config_validation.rb +3 -1
  54. data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
  55. data/lib/praxis/exceptions/invalid_response.rb +3 -1
  56. data/lib/praxis/exceptions/invalid_trait.rb +3 -1
  57. data/lib/praxis/exceptions/stage_not_found.rb +3 -1
  58. data/lib/praxis/exceptions/validation.rb +4 -3
  59. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +187 -131
  60. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
  61. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
  62. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
  63. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
  64. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
  65. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -66
  66. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
  67. data/lib/praxis/extensions/attribute_filtering.rb +3 -1
  68. data/lib/praxis/extensions/field_expansion.rb +6 -4
  69. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
  70. data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
  71. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
  72. data/lib/praxis/extensions/field_selection.rb +3 -1
  73. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
  74. data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
  75. data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
  76. data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
  77. data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
  78. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
  79. data/lib/praxis/extensions/pagination.rb +10 -15
  80. data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
  81. data/lib/praxis/extensions/rails_compat.rb +2 -0
  82. data/lib/praxis/extensions/rendering.rb +12 -12
  83. data/lib/praxis/field_expander.rb +8 -9
  84. data/lib/praxis/file_group.rb +8 -12
  85. data/lib/praxis/finalizable.rb +1 -0
  86. data/lib/praxis/handlers/json.rb +5 -2
  87. data/lib/praxis/handlers/plain.rb +2 -1
  88. data/lib/praxis/handlers/www_form.rb +6 -3
  89. data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
  90. data/lib/praxis/mapper/active_model_compat.rb +13 -10
  91. data/lib/praxis/mapper/resource.rb +171 -180
  92. data/lib/praxis/mapper/selector_generator.rb +106 -112
  93. data/lib/praxis/mapper/sequel_compat.rb +70 -67
  94. data/lib/praxis/media_type.rb +2 -2
  95. data/lib/praxis/media_type_identifier.rb +26 -22
  96. data/lib/praxis/middleware_app.rb +18 -15
  97. data/lib/praxis/multipart/parser.rb +46 -51
  98. data/lib/praxis/multipart/part.rb +78 -110
  99. data/lib/praxis/notifications.rb +2 -4
  100. data/lib/praxis/plugin.rb +11 -18
  101. data/lib/praxis/plugin_concern.rb +12 -15
  102. data/lib/praxis/plugins/mapper_plugin.rb +15 -13
  103. data/lib/praxis/plugins/pagination_plugin.rb +8 -6
  104. data/lib/praxis/plugins/rails_plugin.rb +33 -28
  105. data/lib/praxis/renderer.rb +11 -15
  106. data/lib/praxis/request.rb +48 -44
  107. data/lib/praxis/request_stages/action.rb +4 -6
  108. data/lib/praxis/request_stages/load_request.rb +2 -4
  109. data/lib/praxis/request_stages/request_stage.rb +19 -23
  110. data/lib/praxis/request_stages/response.rb +4 -6
  111. data/lib/praxis/request_stages/validate.rb +3 -5
  112. data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -28
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +13 -25
  117. data/lib/praxis/response_definition.rb +77 -122
  118. data/lib/praxis/response_template.rb +11 -15
  119. data/lib/praxis/responses/http.rb +23 -44
  120. data/lib/praxis/responses/internal_server_error.rb +18 -21
  121. data/lib/praxis/responses/multipart_ok.rb +4 -9
  122. data/lib/praxis/responses/validation_error.rb +8 -15
  123. data/lib/praxis/route.rb +8 -10
  124. data/lib/praxis/router/rack.rb +13 -7
  125. data/lib/praxis/router/simple.rb +10 -5
  126. data/lib/praxis/router.rb +27 -34
  127. data/lib/praxis/routing_config.rb +52 -29
  128. data/lib/praxis/simple_media_type.rb +5 -8
  129. data/lib/praxis/stage.rb +17 -25
  130. data/lib/praxis/tasks/api_docs.rb +15 -15
  131. data/lib/praxis/tasks/console.rb +3 -1
  132. data/lib/praxis/tasks/environment.rb +2 -0
  133. data/lib/praxis/tasks/routes.rb +26 -24
  134. data/lib/praxis/tasks.rb +3 -1
  135. data/lib/praxis/trait.rb +37 -46
  136. data/lib/praxis/types/fuzzy_hash.rb +13 -14
  137. data/lib/praxis/types/media_type_common.rb +11 -10
  138. data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
  139. data/lib/praxis/types/multipart_array.rb +100 -115
  140. data/lib/praxis/validation_handler.rb +5 -3
  141. data/lib/praxis/version.rb +3 -1
  142. data/lib/praxis.rb +4 -5
  143. data/praxis.gemspec +22 -21
  144. data/spec/functional_spec.rb +40 -52
  145. data/spec/praxis/action_definition_spec.rb +39 -48
  146. data/spec/praxis/api_definition_spec.rb +45 -47
  147. data/spec/praxis/api_general_info_spec.rb +28 -29
  148. data/spec/praxis/application_spec.rb +18 -14
  149. data/spec/praxis/blueprint_spec.rb +33 -34
  150. data/spec/praxis/bootloader_spec.rb +32 -30
  151. data/spec/praxis/callbacks_spec.rb +37 -37
  152. data/spec/praxis/collection_spec.rb +18 -25
  153. data/spec/praxis/config_hash_spec.rb +5 -4
  154. data/spec/praxis/config_spec.rb +27 -26
  155. data/spec/praxis/controller_spec.rb +8 -9
  156. data/spec/praxis/endpoint_definition_spec.rb +25 -32
  157. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +221 -106
  158. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
  159. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +127 -60
  160. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
  161. data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
  162. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
  163. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
  164. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
  165. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
  166. data/spec/praxis/extensions/rendering_spec.rb +9 -9
  167. data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -47
  168. data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
  169. data/spec/praxis/field_expander_spec.rb +6 -5
  170. data/spec/praxis/file_group_spec.rb +3 -1
  171. data/spec/praxis/handlers/json_spec.rb +6 -5
  172. data/spec/praxis/mapper/resource_spec.rb +30 -33
  173. data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
  174. data/spec/praxis/media_type_identifier_spec.rb +13 -10
  175. data/spec/praxis/media_type_spec.rb +12 -12
  176. data/spec/praxis/middleware_app_spec.rb +23 -22
  177. data/spec/praxis/multipart/parser_spec.rb +7 -9
  178. data/spec/praxis/notifications_spec.rb +4 -4
  179. data/spec/praxis/plugin_concern_spec.rb +5 -6
  180. data/spec/praxis/renderer_spec.rb +10 -9
  181. data/spec/praxis/request_spec.rb +38 -41
  182. data/spec/praxis/request_stages/action_spec.rb +14 -15
  183. data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
  184. data/spec/praxis/request_stages/validate_spec.rb +3 -1
  185. data/spec/praxis/response_definition_spec.rb +79 -92
  186. data/spec/praxis/response_spec.rb +28 -39
  187. data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
  188. data/spec/praxis/responses/validation_error_spec.rb +17 -18
  189. data/spec/praxis/route_spec.rb +4 -7
  190. data/spec/praxis/router_spec.rb +69 -79
  191. data/spec/praxis/routing_config_spec.rb +15 -14
  192. data/spec/praxis/stage_spec.rb +56 -53
  193. data/spec/praxis/trait_spec.rb +17 -17
  194. data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
  195. data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
  196. data/spec/praxis/types/multipart_array_spec.rb +33 -48
  197. data/spec/spec_app/app/concerns/authenticated.rb +5 -5
  198. data/spec/spec_app/app/concerns/basic_api.rb +3 -1
  199. data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
  200. data/spec/spec_app/app/controllers/base_class.rb +6 -5
  201. data/spec/spec_app/app/controllers/instances.rb +31 -34
  202. data/spec/spec_app/app/controllers/volumes.rb +6 -6
  203. data/spec/spec_app/app/responses/multipart.rb +1 -2
  204. data/spec/spec_app/app/responses/other_response.rb +2 -2
  205. data/spec/spec_app/config/environment.rb +19 -6
  206. data/spec/spec_app/config.ru +4 -3
  207. data/spec/spec_app/design/api.rb +13 -15
  208. data/spec/spec_app/design/media_types/instance.rb +6 -6
  209. data/spec/spec_app/design/media_types/volume.rb +2 -1
  210. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
  211. data/spec/spec_app/design/resources/instances.rb +11 -17
  212. data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
  213. data/spec/spec_app/design/resources/volumes.rb +4 -5
  214. data/spec/spec_helper.rb +12 -13
  215. data/spec/support/be_deep_equal_matcher.rb +5 -0
  216. data/spec/support/spec_authorization_plugin.rb +7 -12
  217. data/spec/support/spec_blueprints.rb +5 -4
  218. data/spec/support/spec_complex_authentication_plugin.rb +17 -34
  219. data/spec/support/spec_endpoint_definitions.rb +2 -3
  220. data/spec/support/spec_media_types.rb +28 -35
  221. data/spec/support/spec_resources.rb +22 -16
  222. data/spec/support/spec_simple_authentication_plugin.rb +5 -9
  223. data/tasks/loader.thor +4 -2
  224. data/tasks/thor/app.rb +7 -5
  225. data/tasks/thor/example.rb +23 -22
  226. data/tasks/thor/model.rb +7 -7
  227. data/tasks/thor/scaffold.rb +23 -23
  228. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
  229. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
  230. metadata +72 -84
  231. data/MAINTAINERS +0 -2
  232. data/TODO.md +0 -25
  233. data/spec/praxis/api_resource_spec.rb +0 -0
  234. data/spec/praxis/dispatcher_spec.rb +0 -0
  235. data/spec/spec_app/app/responses/bulk_response.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9391f2cd6fb2c5a605a4b4c9f492371ef1d7f01849e94a9d17fdc51b95c211a9
4
- data.tar.gz: 70c9f00cd12c7b3e99cebd10f86a98ac913433b8b96a41b658893b63ddc4b31b
3
+ metadata.gz: 58ae0c1f92272e7fd0aaaa779fb679db4b14f6e8d2f9df08efaec211bd3cc4f0
4
+ data.tar.gz: 19b73ba6239911b4b0f6d3097e58671f556f7835514b7fa52269c1a81f47d837
5
5
  SHA512:
6
- metadata.gz: edfc42021b347aea6bd7b84853ebd901f55e4963612da2a6870e00b956b3352e51efd8cd3c360123625af48f89553bff6fa0ad7766e076bc164ba8ccf770ba7b
7
- data.tar.gz: da2df887a849f03be390f7218f931807670a58addc8eb3d101b293e0827a522680ca1824f47bafcff2c3f26bfb45bd13e8ad0c6f02577edad12c5c8cfca60904
6
+ metadata.gz: f488d80d4e1495b3574bce59a20826c418ac663d6417838a24f7ef92770e73dd789fe3b9d432a5663b4e6cd5abe665be5d4fc96919c20d93dac0cd981732b4ec
7
+ data.tar.gz: b8ffc24d28567d4b00a83c8b03bfd00539791af3a675ec315858a800d9e7890bf5bbcb1b811cd91ef8a13c9edc10be96eefb605e34139c789b5ec174b6975342
data/.rubocop.yml ADDED
@@ -0,0 +1,54 @@
1
+ # require:
2
+ # - rubocop-thread_safety
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - "_site/**/*"
7
+ - "coverage/**/*"
8
+ - "docs_docusaurus/**/*"
9
+ - "tmp/**/*"
10
+ - "pkg/**/*"
11
+ - "vendor/cache/**/*"
12
+ - "tasks/thor/templates/**/*"
13
+ TargetRubyVersion: 2.5
14
+ NewCops: disable
15
+
16
+ Metrics/BlockLength:
17
+ Enabled: false
18
+
19
+ Metrics/AbcSize:
20
+ Enabled: false
21
+
22
+ Metrics/ClassLength:
23
+ Enabled: false
24
+
25
+ Metrics/ModuleLength:
26
+ Enabled: false
27
+
28
+ Metrics/CyclomaticComplexity:
29
+ Enabled: false
30
+
31
+ Metrics/PerceivedComplexity:
32
+ Enabled: false
33
+
34
+ Metrics/MethodLength:
35
+ Enabled: false
36
+
37
+ Layout/LineLength:
38
+ Enabled: false
39
+
40
+ Style/Documentation:
41
+ Enabled: false
42
+
43
+ #### RE-ENABLE PLEASE
44
+ Metrics/BlockNesting:
45
+ Enabled: false
46
+
47
+ Style/OptionalBooleanParameter:
48
+ Enabled: false
49
+
50
+ Lint/Void:
51
+ Enabled: false
52
+
53
+ Lint/MissingSuper:
54
+ Enabled: false
data/.simplecov CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  SimpleCov.profiles.define 'praxis' do
2
4
  add_filter '/config/'
3
5
  add_filter '/spec/'
4
6
 
5
7
  add_group 'lib', 'lib'
6
8
  add_group 'app', 'app'
7
- end
9
+ end
data/.travis.yml CHANGED
@@ -2,7 +2,8 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.6
5
- - 2.7
5
+ - 2.7
6
+ # - 3.1 # Not available in TravisCI out of the box yet
6
7
  script:
7
8
  - bundle exec rspec spec
8
9
  branches:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## next
4
4
 
5
+ ## 2.0.pre.19
6
+ * Introduced a new DSL for the `FilteringParams` type that allows filters for common attributes in your Media Types:
7
+ * The new `any` DSL allows you to define which final leaf attribute to always allow, and with which operators and/or fuzzy restrictions.
8
+ * For example, you can add `any updated_at, using: ['>','<']` which would allow the type to accept filters like `updated_at>2000-01-01`, or any existing nested fields like `posts.comments.updated_at>2000-01-01`
9
+ * Note that the path of attributes passed in, will still need to exist and will be validated. Also, you still need to make sure that you have the right `filters_mapping` defined in your resources.
10
+ * Changed `filters_mapping` to allow implicitly any filter path that is a valid representation of existing columns and associations. I.e., you do not have to explicitly define long nested filters that correspond to the same underlying path of associations and columns.
11
+ ## 2.0.pre.18
12
+ * Upgraded to newest Attributor, which cleans up the required: true semantics to only work on keys, and introduces null: true for nullability of values (independent from presence of keys or not)
13
+ * Fixed a selector generator bug that would occur when using deep nested resource dependencies as strings 'foo.bar.baz.bam'. In this cases only partial tracking of relationships would be built, which could cause to not fully eager load DB queries.
14
+ ## 2.0.pre.17
15
+ * Changed the Parameter Filtering to use left outer joins (and extra conditions), to allow for the proper results when OR clauses are involved in certain configurations.
16
+ * Built support for allowing filtering directly on associations using `!` and `!!` operators. This allows to filter results where
17
+ there are no associated rows (`!!`) or if there are some associated rows (`!`)
18
+ * Allow implicit definition of `filters_mapping` for filter names that match top-level associations of the model (i.e., like we do for the columns)
19
+ ## 2.0.pre.16
20
+
21
+ * Updated `Resource.property` signature to only accept known named arguments (`dependencies` and `though` at this time) to spare anyone else from going insane wondering why their `depednencies` aren't working.
22
+ * Fixed issue with Filtering Params, that occurred with using the ! or !! operators on String-typed fields.
23
+
5
24
  ## 2.0.pre.14
6
25
 
7
26
  * More encoding/decoding robustness for filters.
data/CONTRIBUTING.md CHANGED
@@ -19,13 +19,8 @@ Before submitting any contributions, please read the below description of what
19
19
  the problems Praxis aims to solve. Contributions that are not aligned with
20
20
  Praxis' goals will not be accepted.
21
21
 
22
- Praxis is a web framework for developing usable, consistent and
23
- self-documenting HTTP APIs for
24
- [SOA](http://en.wikipedia.org/wiki/Service-oriented_architecture) systems.
25
-
26
22
  Praxis is not a Rails plugin or extension. If you want to use Praxis with
27
- Rails, please create a separate gem or equivalent. We will not enhance Praxis
28
- with Rails-specific functionality.
23
+ Rails, please look at the MiddlewareApp class on how to embed it on an existing Rails project.
29
24
 
30
25
  Praxis is also not designed for service any kind of UI, although UIs can
31
26
  certainly be powered by Praxis apps.
@@ -36,10 +31,6 @@ We are always thrilled to receive pull requests, and do our best to process
36
31
  them as fast as possible. Not sure if that typo is worth a pull request? Do it!
37
32
  We will appreciate it.
38
33
 
39
- If your pull request is not accepted on the first try, don't be discouraged! If
40
- there's a problem with the implementation, hopefully you received feedback on
41
- what to improve.
42
-
43
34
  We're trying very hard to keep Praxis lean and focused. We don't want it to do
44
35
  everything for everybody. This means that we might decide against incorporating
45
36
  a new feature. However, there might be a way to implement that feature *on top
@@ -72,13 +63,7 @@ help prioritize the most common problems and requests.
72
63
 
73
64
  ### Conventions
74
65
 
75
- Fork the repository and make changes on your fork in a feature branch:
76
-
77
- - If it's a bug fix branch, name it XXXX-something where XXXX is the number of
78
- the issue.
79
- - If it's a feature branch, create an enhancement issue to announce your
80
- intentions, and name it XXXX-something where XXXX is the number of the issue.
81
-
66
+ Fork the repository and make changes on your fork in a feature branch.
82
67
  Submit unit tests for your changes. Take a look at existing tests for
83
68
  inspiration. Run the full test suite on your branch before submitting a pull
84
69
  request.
@@ -115,68 +100,6 @@ github team.
115
100
 
116
101
  Add a quick summary of your change to the changelog, under the `next` heading.
117
102
 
118
- ### Sign your work
119
-
120
- The sign-off is a simple line at the end of the explanation for the
121
- patch, which certifies that you wrote it or otherwise have the right to
122
- pass it on as an open-source patch. The rules are pretty simple: if you
123
- can certify the below (from
124
- [developercertificate.org](http://developercertificate.org/)):
125
-
126
- ```
127
- Developer Certificate of Origin
128
- Version 1.1
129
-
130
- Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
131
- 660 York Street, Suite 102,
132
- San Francisco, CA 94110 USA
133
-
134
- Everyone is permitted to copy and distribute verbatim copies of this
135
- license document, but changing it is not allowed.
136
-
137
- Developer's Certificate of Origin 1.1
138
-
139
- By making a contribution to this project, I certify that:
140
-
141
- (a) The contribution was created in whole or in part by me and I
142
- have the right to submit it under the open source license
143
- indicated in the file; or
144
-
145
- (b) The contribution is based upon previous work that, to the best
146
- of my knowledge, is covered under an appropriate open source
147
- license and I have the right under that license to submit that
148
- work with modifications, whether created in whole or in part
149
- by me, under the same open source license (unless I am
150
- permitted to submit under a different license), as indicated
151
- in the file; or
152
-
153
- (c) The contribution was provided directly to me by some other
154
- person who certified (a), (b) or (c) and I have not modified
155
- it.
156
-
157
- (d) I understand and agree that this project and the contribution
158
- are public and that a record of the contribution (including all
159
- personal information I submit with it, including my sign-off) is
160
- maintained indefinitely and may be redistributed consistent with
161
- this project or the open source license(s) involved.
162
- ```
163
-
164
- Then you just add a line to every git commit message:
165
-
166
- Signed-off-by: Joe Smith <joe.smith@email.com> Using your real name
167
- (sorry, no pseudonyms or anonymous contributions.)
168
-
169
- If you set your user.name and user.email git configs, you can sign your
170
- commit automatically with git commit -s.
171
-
172
- #### Small patch exception
173
-
174
- There are several exceptions to the signing requirement. Currently these are:
175
-
176
- * Your patch fixes spelling or grammar errors.
177
- * Your patch is a single line change to documentation.
178
- * Your patch fixes Markdown formatting or syntax errors in the documentation.
179
-
180
103
  ### How can I become a maintainer?
181
104
 
182
105
  * Step 1: Learn the component inside out
data/Gemfile CHANGED
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
  gemspec
3
5
 
6
+ gem 'rubocop'
7
+
4
8
  group :test do
5
9
  gem 'builder'
6
10
 
7
- gem 'parslet'
8
11
  gem 'link_header'
12
+ gem 'parslet'
9
13
  end
data/Guardfile CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  guard :rspec, cmd: 'bundle exec rspec --format=Fuubar', \
2
4
  all_after_pass: false, all_on_start: false, failed_mode: :focus do
3
5
  watch(%r{^spec/.+_spec\.rb$})
4
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
- watch('spec/spec_helper.rb') { "spec/" }
6
- watch('spec/functional_spec.rb') { "spec/" }
7
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/functional_spec.rb" }
8
- watch(%r{^app/(.+)\.rb$}) { "spec/" }
7
+ watch('spec/spec_helper.rb') { 'spec/' }
8
+ watch('spec/functional_spec.rb') { 'spec/' }
9
+ watch(%r{^lib/(.+)\.rb$}) { |_m| 'spec/functional_spec.rb' }
10
+ watch(%r{^app/(.+)\.rb$}) { 'spec/' }
9
11
  watch(%r{^spec/support}) { 'spec/' }
10
12
  end
data/LICENSE CHANGED
@@ -1,5 +1,3 @@
1
- Copyright (c) 2014 RightScale
2
-
3
1
  MIT License
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining
data/MAINTAINERS.md CHANGED
@@ -31,6 +31,7 @@ It is every maintainer's responsibility to:
31
31
  list.
32
32
  4. Make sure their component respects the philosophy, design and
33
33
  road map of the project.
34
+ 5. Be inclusive of the different ways to develop and use the framework
34
35
 
35
36
  ## How are decisions made?
36
37
 
data/README.md CHANGED
@@ -1,42 +1,35 @@
1
1
  # Praxis [![TravisCI][travis-img-url]][travis-ci-url] [![Coverage Status][coveralls-img-url]][coveralls-url]
2
2
 
3
- [//]: # ( COMMENTED OUT UNTIL GEMNASIUM CAN SEE THE REPOS: [![Dependency Status][gemnasium-img-url]][gemnasium-url])
4
-
5
- [travis-img-url]: https://travis-ci.org/praxis/praxis.svg?branch=master
6
- [travis-ci-url]:https://travis-ci.org/praxis/praxis
3
+ [travis-img-url]: https://app.travis-ci.com/praxis/praxis.svg?branch=master
4
+ [travis-ci-url]: https://app.travis-ci.com/praxis/praxis
7
5
  [coveralls-img-url]:https://coveralls.io/repos/github/praxis/praxis/badge.svg?branch=master
8
6
  [coveralls-url]:https://coveralls.io/github/praxis/praxis?branch=master
9
- [gemnasium-img-url]:https://gemnasium.com/praxis/praxis.svg
10
- [gemnasium-url]:https://gemnasium.com/praxis/praxis
11
-
12
- Praxis is a framework for both _designing_ and _implementing_ APIs.
13
7
 
14
- An important part of the framework is geared towards the _design_ aspect of building an API. This functionality empowers architects with tools to design every last aspect of their API, resulting in a complete, web-browsable documentation, which includes automatic generation of examples for resources, parameters, headers, etc...as well as requests and responses for the supported encodings. The design process is iterative, and flows from defining new resources, parameters, etc...to reviewing the resulting docs (usually with some of the potential clients of the API), back to updating the design based on feedback, or expanding it with more resources. The design language (i.e. DSL) of Praxis follows a clean 'ruby-type-syntax' and its final outcome is to generates an output that is both a set of schema documents as well as a web-based API browser (driven by those schemas). This allows Praxis to design APIs that can potentially be implemented in any language.
8
+ A fast and highly efficient paradigm to build beautiful service APIs
15
9
 
16
- Another important part of the framework is geared towards helping in the _implementation_ of the API service. In particular, Praxis provides help to Ruby developers for building a service conforming to the designed API. Aside from Ruby there is also sister-project called [Goa](http://goa.design/) that assists in implementing Praxis-like design API using Golang. Since the API design generates schema files very similar in nature to other API document formats like (Swagger, Google Discovery, RAML, etc...) supporting other implementation languages could be easily accomplished in the future by building converters to/from them.
10
+ Praxis is built to empower development teams with extreme productivty tools to create fast, and modern APIs that will delight their customers. Some of the salient features are:
17
11
 
18
- The part of the framework that helps with the ruby service implementation takes an approach that is different from other existing ruby (micro)frameworks such as [Grape](http://www.ruby-grape.org), [Sinatra](https://github.com/sinatra/sinatra), [Scorched](http://scorchedrb.com/), [Lotus](http://lotusrb.org/) or even [RailsAPI](https://github.com/rails-api/rails-api) (now part of Rails). Instead of being developer-centric, it takes an integrated approach treating both designers and implementors as first class citizens throughout the complete API building process. With Praxis you create an API by iterating through the design, review and implementation phases. While Praxis can help Ruby developers in a lot of aspects involved in building a service, the framework is completely componentized as to allow developers to pick and choose which parts to use, which ones not to use, and which other technologies to integrate with. The framework provides help in many areas, for example: all aspects of request and response validation, automatic type-coercion, consistent error-responses, routing and url generation, advanced template/media-type definition and rendering, domain-modeling, optional database ORM (for high-perfomance large datasets), DB integration (with an efficient identityMap), a plugin and extensible framework to easily hook into, available integrations such as newrelic, statsd, etc...
12
+ * **Truthful & Beautiful Docs:** Automatically generate Open API 3.x documents from the actual functioning code, and rest assured they're always correct.
19
13
 
20
- There is a long list of benefits that come from using Praxis. From those, here are a couple of the salient themes:
21
- * Designing APIs with Praxis result in a very precise, consistent and beautiful API documentation that it is apt for both human (web browsable) or machine consumption (JSON spec files).
22
- * Building Ruby APIs with Praxis will result in much faster developer times, and more importantly with a resulting service that will ensure that its implementation is always consistent with its design, and it is enforced at all times.
14
+ * **GraphQL Flexibility, REST Simplicity:** Allow customers to specify which fields they want to receive using the GraphQL syntax, but exposing them through well known REST endpoints.
15
+ * **Fast Runtime and Blazing Fast Development:** Deploy your API using one of the best Ruby performing frameworks and take advantage of an unprecedented development speed.
16
+ * **API Design-First Philosophy:** Craft and visualize your API design upfont, without writing a single line of code. Forget about implementing any of the API validations, the framework fully takes care of it from your design specification.
17
+ * **Feature Rich yet Fully Customizable:** Fully take advantage of the tons of best practices, proven methods, standards and features that the frameworks comes with, or pick and choose only the ones you want to enable.
18
+ * **Hardnened & Battle Tested:** Rest assured you'll get the advertised results as this framework has been deployed in production environments since before 2014.
23
19
 
24
20
  ## Quickstart
25
21
  ```bash
26
22
  # Install the praxis gem
27
23
  gem install praxis
28
24
 
29
- # Generate a praxis application named my-app in ./my-app
30
- praxis example my-app
25
+ # Generate and bundle a praxis application named my-app in ./my-app
26
+ praxis example my-app && cd my-app && bundle
31
27
 
32
28
  # Run it!
33
- cd my-app
34
- bundle
35
29
  rackup
36
30
  ```
37
31
 
38
- Or better yet, checkout a simple, but functional [blog example app](https://github.com/praxis/praxis-example-app) which showcases a few of the main design and implementation aspects that Praxis has to offer.
39
-
32
+ Or check the getting started tutorial and reference docs at https://www.praxis-framework.io all that Praxis has to offer.
40
33
 
41
34
  ## Mailing List
42
35
  Join our Google Groups for discussion, support and announcements.
@@ -59,10 +52,10 @@ for further details on what contributions are accepted and how to go about
59
52
  contributing.
60
53
 
61
54
  ## Requirements
62
- Praxis requires Ruby 2.1.0 or greater.
55
+ Praxis requires Ruby 2.5.0 or greater.
63
56
 
64
57
  ## License
65
58
 
66
59
  This software is released under the [MIT License](http://www.opensource.org/licenses/MIT). Please see [LICENSE](LICENSE) for further details.
67
60
 
68
- Copyright (c) 2014 RightScale
61
+ This framework was initially developed and used at RightScale, and was open sourced in 2014, after a few years of its production use.
data/Rakefile CHANGED
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path('lib',__dir__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
2
4
 
3
5
  require 'praxis'
4
6
  require 'praxis/tasks'
@@ -9,4 +11,4 @@ require 'bundler/gem_tasks'
9
11
 
10
12
  RSpec::Core::RakeTask.new(:spec)
11
13
 
12
- task :default => :spec
14
+ task default: :spec
data/bin/praxis CHANGED
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler'
4
5
 
@@ -9,119 +10,116 @@ rescue Bundler::GemfileNotFound
9
10
  # no-op: we might be installed as a system gem
10
11
  end
11
12
 
12
- if ARGV[0] == "version"
13
+ if ARGV[0] == 'version'
13
14
  require 'praxis/version'
14
15
  puts "Praxis version #{Praxis::VERSION}"
15
16
  exit 0
16
17
  end
17
18
 
18
- if ["routes","docs","console"].include? ARGV[0]
19
+ if %w[routes docs console].include? ARGV[0]
19
20
  require 'rake'
20
21
  require 'praxis'
21
22
  require 'praxis/tasks'
22
23
  load 'Rakefile' # Ensure that we read the App's Rakefile, to pickup any definitions etc.
23
24
 
24
25
  case ARGV[0]
25
- when "routes"
26
- Rake::Task['praxis:routes'].invoke(ARGV[1])
27
- when "docs"
28
- task_name = case ARGV[1]
29
- when nil,'browser'
30
- 'praxis:docs:preview'
31
- when 'generate'
32
- 'praxis:docs:generate'
33
- when 'package'
34
- 'praxis:docs:package'
35
- end
36
- Rake::Task[task_name].invoke
37
- when "console"
38
- Rake::Task['praxis:console'].invoke
26
+ when 'routes'
27
+ Rake::Task['praxis:routes'].invoke(ARGV[1])
28
+ when 'docs'
29
+ task_name = case ARGV[1]
30
+ when nil, 'browser'
31
+ 'praxis:docs:preview'
32
+ when 'generate'
33
+ 'praxis:docs:generate'
34
+ when 'package'
35
+ 'praxis:docs:package'
36
+ end
37
+ Rake::Task[task_name].invoke
38
+ when 'console'
39
+ Rake::Task['praxis:console'].invoke
39
40
  end
40
41
  exit 0
41
42
  end
42
43
  # Thor tasks
43
44
  path_to_praxis = File.expand_path(File.dirname(File.dirname(__FILE__)))
44
- path_to_loader = '%s/tasks/loader.thor' % path_to_praxis
45
+ path_to_loader = format('%<path>s/tasks/loader.thor', path: path_to_praxis)
45
46
 
46
47
  load path_to_loader
47
48
 
48
49
  class PraxisGenerator < Thor
49
-
50
50
  # Include a few fake thor action descriptions (for the rake tasks above) so they can show up in the same usage messages
51
- desc "routes [json]", "Prints the route table of the application. Defaults to table format, but can produce json"
52
- def routes
53
- end
51
+ desc 'routes [json]', 'Prints the route table of the application. Defaults to table format, but can produce json'
52
+ def routes; end
54
53
 
55
- desc "docs [generate|browser|package]", <<~EOF
56
- Generates API documentation and a Web App to inspect it
57
- generate - Generates the JSON docs
58
- browser - (default) Generates JSON docs, and automatically starts a Web app to browse them.
59
- package - Generates JSON docs, and neatly packages all the necessary static files ready for exporting the browsing app.
60
- EOF
61
- def docs
62
- end
63
-
64
- desc "console", "Open a console to the application, with its environment loaded"
65
- def console
66
- end
54
+ desc 'docs [generate|browser|package]', <<~HELP
55
+ Generates API documentation and a Web App to inspect it
56
+ generate - Generates the JSON docs
57
+ browser - (default) Generates JSON docs, and automatically starts a Web app to browse them.
58
+ package - Generates JSON docs, and neatly packages all the necessary static files ready for exporting the browsing app.
59
+ HELP
60
+ def docs; end
61
+
62
+ desc 'console', 'Open a console to the application, with its environment loaded'
63
+ def console; end
67
64
 
68
65
  # Simple helper to go get the existing description for the real action
69
- # Usage must still be provided rather than retrieved (since it is not a
66
+ # Usage must still be provided rather than retrieved (since it is not a
70
67
  # straight "usage" from the remote action when arguments are defined )
71
- def self.desc_for( usage_string, klass, action_name, description_prefix="")
68
+ def self.desc_for(usage_string, klass, action_name, description_prefix = '')
72
69
  action_name = action_name.to_s
73
70
  cmd = klass.commands[action_name]
74
71
  raise "Error, could not find description for #{klass.name}##{action_name}" if cmd.nil?
72
+
75
73
  desc usage_string, "#{description_prefix}#{cmd.description}"
76
74
  end
77
75
 
78
- desc_for "new APP_NAME", ::PraxisGen::App, :new
76
+ desc_for 'new APP_NAME', ::PraxisGen::App, :new
79
77
  def new(app_name)
80
78
  gen = ::PraxisGen::App.new([app_name])
81
79
  gen.destination_root = app_name
82
80
  gen.invoke_all
83
81
  end
84
82
 
85
- desc_for "example APP_NAME", ::PraxisGen::Example, :example
83
+ desc_for 'example APP_NAME', ::PraxisGen::Example, :example
86
84
  def example(app_name)
87
85
  gen = ::PraxisGen::Example.new([app_name])
88
86
  gen.destination_root = app_name
89
87
  gen.invoke(:example)
90
88
  end
91
89
 
92
- desc_for "g COLLECTION_NAME", ::PraxisGen::Scaffold, :g
90
+ desc_for 'g COLLECTION_NAME', ::PraxisGen::Scaffold, :g
93
91
  # Cannot use the argument below or it will apply to all commands (the action in the class has it)
94
92
  # argument :collection_name, required: false
95
93
  # The options, however, since they're optional are fine (But need to be duplicated from the class :( )
96
94
  option :version, required: false, default: '1',
97
- desc: 'Version string for the API endpoint. This also dictates the directory structure (i.e., v1/endpoints/...))'
95
+ desc: 'Version string for the API endpoint. This also dictates the directory structure (i.e., v1/endpoints/...))'
98
96
  option :design, type: :boolean, default: true,
99
- desc: 'Include the Endpoint and MediaType files for the collection'
97
+ desc: 'Include the Endpoint and MediaType files for the collection'
100
98
  option :implementation, type: :boolean, default: true,
101
- desc: 'Include the Controller and (possibly the) Resource files for the collection (see --no-resource)'
99
+ desc: 'Include the Controller and (possibly the) Resource files for the collection (see --no-resource)'
102
100
  option :resource, type: :boolean, default: true,
103
- desc: 'Disable (or enable) the creation of the Resource files when generating implementation'
104
- option :model, type: :string, enum: ['activerecord','sequel'],
105
- desc: 'It also generates a model for the given ORM. An empty --model flag will default to activerecord'
106
- option :actions, type: :string, default: 'crud', enum: ['cr','cru','crud','u','ud','d'],
107
- desc: 'Specifies the actions to generate for the API. cr=create, u=update, d=delete. Index and show actions are always generated'
101
+ desc: 'Disable (or enable) the creation of the Resource files when generating implementation'
102
+ option :model, type: :string, enum: %w[activerecord sequel],
103
+ desc: 'It also generates a model for the given ORM. An empty --model flag will default to activerecord'
104
+ option :actions, type: :string, default: 'crud', enum: %w[cr cru crud u ud d],
105
+ desc: 'Specifies the actions to generate for the API. cr=create, u=update, d=delete. Index and show actions are always generated'
108
106
  def g(*args)
109
107
  # Because we cannot share the :collection_name argument, we need to do this check here, before
110
108
  # we "parse" it and pass it to the g command
111
109
  unless args.size == 1
112
- ::PraxisGen::Scaffold.command_help(shell,:g)
110
+ ::PraxisGen::Scaffold.command_help(shell, :g)
113
111
  exit 1
114
112
  end
115
113
 
116
- collection_name,_ = args
117
- ::PraxisGen::Scaffold.new([collection_name],options).invoke(:g)
118
- if options[:model]
119
- # Make it easy to be able to both enable or not enable the creation of the model, by passing --model=...
120
- # but also make it easy so that if there is no value for it, it default to activerecord
121
- opts = {orm: options[:model] }
122
- opts[:orm] = 'activerecord' if opts[:orm] == 'model' # value is model param passed by no value
123
- ::PraxisGen::Model.new([collection_name.singularize],opts).invoke(:g)
124
- end
114
+ collection_name, = args
115
+ ::PraxisGen::Scaffold.new([collection_name], options).invoke(:g)
116
+ return unless options[:model]
117
+
118
+ # Make it easy to be able to both enable or not enable the creation of the model, by passing --model=...
119
+ # but also make it easy so that if there is no value for it, it default to activerecord
120
+ opts = { orm: options[:model] }
121
+ opts[:orm] = 'activerecord' if opts[:orm] == 'model' # value is model param passed by no value
122
+ ::PraxisGen::Model.new([collection_name.singularize], opts).invoke(:g)
125
123
  end
126
124
 
127
125
  # Initially, the idea was to build some quick model generator, but I think it's better to keep it
@@ -144,7 +142,6 @@ class PraxisGenerator < Thor
144
142
  # model_name,_ = args
145
143
  # ::PraxisGen::Model.new([model_name],options).invoke(:g)
146
144
  # end
147
-
148
145
  end
149
-
146
+
150
147
  PraxisGenerator.start(ARGV)
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  class ActionDefinition
3
5
  class HeadersDSLCompiler < Attributor::DSLCompiler
4
-
5
6
  # it allows to define expectations on incoming headers. For example:
6
7
  # header :X_SpecialCookie => implies the header is required
7
8
  # header :X_Something, /matching_this/ => implies that if the name header exists, it should match the regexp
@@ -11,10 +12,8 @@ module Praxis
11
12
  # required: true => to make it required
12
13
  # description: "lorem ipsum" => to describe it (like any other attribute)
13
14
 
14
- def header(name, val=nil, **options)
15
- if val.kind_of?(Class)
16
- return key name, val, **options
17
- end
15
+ def header(name, val = nil, **options)
16
+ return key name, val, **options if val.is_a?(Class)
18
17
 
19
18
  case val
20
19
  when Regexp
@@ -25,7 +24,7 @@ module Praxis
25
24
  # Defining the existence without any other options can only mean that it is required (otherwise it is a useless definition)
26
25
  options[:required] = true if options.empty?
27
26
  end
28
- key name , String, **options
27
+ key name, String, **options
29
28
  end
30
29
  end
31
30
  end