praxis 2.0.pre.17 → 2.0.pre.21
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +54 -0
- data/.simplecov +3 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +2 -79
- data/Gemfile +5 -1
- data/Guardfile +6 -4
- data/LICENSE +0 -2
- data/MAINTAINERS.md +1 -0
- data/README.md +15 -22
- data/Rakefile +4 -2
- data/bin/praxis +55 -58
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
- data/lib/praxis/action_definition.rb +65 -95
- data/lib/praxis/api_definition.rb +21 -29
- data/lib/praxis/api_general_info.rb +55 -66
- data/lib/praxis/application.rb +15 -32
- data/lib/praxis/blueprint.rb +80 -73
- data/lib/praxis/bootloader.rb +24 -33
- data/lib/praxis/bootloader_stages/environment.rb +5 -10
- data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
- data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
- data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
- data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
- data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
- data/lib/praxis/bootloader_stages/routing.rb +5 -8
- data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
- data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
- data/lib/praxis/callbacks.rb +12 -11
- data/lib/praxis/collection.rb +11 -14
- data/lib/praxis/config.rb +17 -28
- data/lib/praxis/config_hash.rb +2 -1
- data/lib/praxis/controller.rb +7 -6
- data/lib/praxis/dispatcher.rb +34 -42
- data/lib/praxis/docs/open_api/info_object.rb +11 -8
- data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
- data/lib/praxis/docs/open_api/operation_object.rb +7 -4
- data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
- data/lib/praxis/docs/open_api/paths_object.rb +11 -9
- data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
- data/lib/praxis/docs/open_api/response_object.rb +24 -18
- data/lib/praxis/docs/open_api/responses_object.rb +3 -1
- data/lib/praxis/docs/open_api/schema_object.rb +61 -29
- data/lib/praxis/docs/open_api/server_object.rb +5 -2
- data/lib/praxis/docs/open_api/tag_object.rb +9 -6
- data/lib/praxis/docs/open_api_generator.rb +114 -150
- data/lib/praxis/endpoint_definition.rb +60 -77
- data/lib/praxis/error_handler.rb +2 -2
- data/lib/praxis/exception.rb +2 -0
- data/lib/praxis/exceptions/config.rb +3 -1
- data/lib/praxis/exceptions/config_load.rb +2 -0
- data/lib/praxis/exceptions/config_validation.rb +3 -1
- data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
- data/lib/praxis/exceptions/invalid_response.rb +3 -1
- data/lib/praxis/exceptions/invalid_trait.rb +3 -1
- data/lib/praxis/exceptions/stage_not_found.rb +3 -1
- data/lib/praxis/exceptions/validation.rb +4 -3
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
- data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
- data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
- data/lib/praxis/extensions/attribute_filtering.rb +3 -1
- data/lib/praxis/extensions/field_expansion.rb +6 -4
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
- data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
- data/lib/praxis/extensions/field_selection.rb +3 -1
- data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
- data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
- data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
- data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
- data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
- data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
- data/lib/praxis/extensions/pagination.rb +10 -15
- data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
- data/lib/praxis/extensions/rails_compat.rb +2 -0
- data/lib/praxis/extensions/rendering.rb +12 -12
- data/lib/praxis/field_expander.rb +8 -9
- data/lib/praxis/file_group.rb +8 -12
- data/lib/praxis/finalizable.rb +1 -0
- data/lib/praxis/handlers/json.rb +5 -2
- data/lib/praxis/handlers/plain.rb +2 -1
- data/lib/praxis/handlers/www_form.rb +6 -3
- data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
- data/lib/praxis/mapper/active_model_compat.rb +13 -10
- data/lib/praxis/mapper/resource.rb +196 -181
- data/lib/praxis/mapper/selector_generator.rb +106 -112
- data/lib/praxis/mapper/sequel_compat.rb +70 -67
- data/lib/praxis/media_type.rb +2 -2
- data/lib/praxis/media_type_identifier.rb +26 -22
- data/lib/praxis/middleware_app.rb +18 -15
- data/lib/praxis/multipart/parser.rb +46 -51
- data/lib/praxis/multipart/part.rb +78 -110
- data/lib/praxis/notifications.rb +2 -4
- data/lib/praxis/plugin.rb +11 -18
- data/lib/praxis/plugin_concern.rb +12 -15
- data/lib/praxis/plugins/mapper_plugin.rb +15 -13
- data/lib/praxis/plugins/pagination_plugin.rb +8 -6
- data/lib/praxis/plugins/rails_plugin.rb +33 -28
- data/lib/praxis/renderer.rb +11 -15
- data/lib/praxis/request.rb +48 -44
- data/lib/praxis/request_stages/action.rb +4 -6
- data/lib/praxis/request_stages/load_request.rb +2 -4
- data/lib/praxis/request_stages/request_stage.rb +19 -23
- data/lib/praxis/request_stages/response.rb +4 -6
- data/lib/praxis/request_stages/validate.rb +3 -5
- data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
- data/lib/praxis/request_stages/validate_payload.rb +25 -28
- data/lib/praxis/request_superclassing.rb +3 -3
- data/lib/praxis/resource_definition.rb +1 -0
- data/lib/praxis/response.rb +24 -26
- data/lib/praxis/response_definition.rb +77 -122
- data/lib/praxis/response_template.rb +11 -15
- data/lib/praxis/responses/http.rb +23 -44
- data/lib/praxis/responses/internal_server_error.rb +18 -21
- data/lib/praxis/responses/multipart_ok.rb +4 -9
- data/lib/praxis/responses/validation_error.rb +8 -15
- data/lib/praxis/route.rb +8 -10
- data/lib/praxis/router/rack.rb +13 -7
- data/lib/praxis/router/simple.rb +10 -5
- data/lib/praxis/router.rb +27 -34
- data/lib/praxis/routing_config.rb +52 -29
- data/lib/praxis/simple_media_type.rb +5 -8
- data/lib/praxis/stage.rb +17 -25
- data/lib/praxis/tasks/api_docs.rb +17 -16
- data/lib/praxis/tasks/console.rb +3 -1
- data/lib/praxis/tasks/environment.rb +2 -0
- data/lib/praxis/tasks/routes.rb +26 -24
- data/lib/praxis/tasks.rb +3 -1
- data/lib/praxis/trait.rb +37 -46
- data/lib/praxis/types/fuzzy_hash.rb +13 -14
- data/lib/praxis/types/media_type_common.rb +11 -10
- data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
- data/lib/praxis/types/multipart_array.rb +100 -115
- data/lib/praxis/validation_handler.rb +5 -3
- data/lib/praxis/version.rb +3 -1
- data/lib/praxis.rb +4 -5
- data/praxis.gemspec +22 -21
- data/spec/functional_spec.rb +44 -56
- data/spec/praxis/action_definition_spec.rb +39 -48
- data/spec/praxis/api_definition_spec.rb +45 -47
- data/spec/praxis/api_general_info_spec.rb +28 -29
- data/spec/praxis/application_spec.rb +18 -14
- data/spec/praxis/blueprint_spec.rb +33 -34
- data/spec/praxis/bootloader_spec.rb +32 -30
- data/spec/praxis/callbacks_spec.rb +37 -37
- data/spec/praxis/collection_spec.rb +18 -25
- data/spec/praxis/config_hash_spec.rb +5 -4
- data/spec/praxis/config_spec.rb +27 -26
- data/spec/praxis/controller_spec.rb +8 -9
- data/spec/praxis/endpoint_definition_spec.rb +25 -32
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
- data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
- data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
- data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
- data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
- data/spec/praxis/extensions/rendering_spec.rb +9 -9
- data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
- data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
- data/spec/praxis/field_expander_spec.rb +6 -5
- data/spec/praxis/file_group_spec.rb +3 -1
- data/spec/praxis/handlers/json_spec.rb +6 -5
- data/spec/praxis/mapper/resource_spec.rb +39 -29
- data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
- data/spec/praxis/media_type_identifier_spec.rb +13 -10
- data/spec/praxis/media_type_spec.rb +12 -12
- data/spec/praxis/middleware_app_spec.rb +23 -22
- data/spec/praxis/multipart/parser_spec.rb +7 -9
- data/spec/praxis/notifications_spec.rb +4 -4
- data/spec/praxis/plugin_concern_spec.rb +5 -6
- data/spec/praxis/renderer_spec.rb +10 -9
- data/spec/praxis/request_spec.rb +38 -41
- data/spec/praxis/request_stages/action_spec.rb +14 -15
- data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
- data/spec/praxis/request_stages/validate_spec.rb +3 -1
- data/spec/praxis/response_definition_spec.rb +79 -92
- data/spec/praxis/response_spec.rb +35 -40
- data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
- data/spec/praxis/responses/validation_error_spec.rb +17 -18
- data/spec/praxis/route_spec.rb +4 -7
- data/spec/praxis/router_spec.rb +69 -79
- data/spec/praxis/routing_config_spec.rb +15 -14
- data/spec/praxis/stage_spec.rb +56 -53
- data/spec/praxis/trait_spec.rb +17 -17
- data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
- data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
- data/spec/praxis/types/multipart_array_spec.rb +33 -48
- data/spec/spec_app/app/concerns/authenticated.rb +5 -5
- data/spec/spec_app/app/concerns/basic_api.rb +3 -1
- data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
- data/spec/spec_app/app/controllers/base_class.rb +6 -5
- data/spec/spec_app/app/controllers/instances.rb +31 -34
- data/spec/spec_app/app/controllers/volumes.rb +6 -6
- data/spec/spec_app/app/responses/multipart.rb +1 -2
- data/spec/spec_app/app/responses/other_response.rb +2 -2
- data/spec/spec_app/config/environment.rb +19 -6
- data/spec/spec_app/config.ru +4 -3
- data/spec/spec_app/design/api.rb +13 -15
- data/spec/spec_app/design/media_types/instance.rb +6 -6
- data/spec/spec_app/design/media_types/volume.rb +2 -1
- data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
- data/spec/spec_app/design/resources/instances.rb +11 -17
- data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
- data/spec/spec_app/design/resources/volumes.rb +4 -5
- data/spec/spec_helper.rb +11 -13
- data/spec/support/be_deep_equal_matcher.rb +5 -0
- data/spec/support/spec_authorization_plugin.rb +7 -12
- data/spec/support/spec_blueprints.rb +5 -4
- data/spec/support/spec_complex_authentication_plugin.rb +17 -34
- data/spec/support/spec_endpoint_definitions.rb +2 -3
- data/spec/support/spec_media_types.rb +28 -35
- data/spec/support/spec_resources.rb +22 -16
- data/spec/support/spec_simple_authentication_plugin.rb +5 -9
- data/tasks/loader.thor +4 -2
- data/tasks/thor/app.rb +7 -5
- data/tasks/thor/example.rb +23 -22
- data/tasks/thor/model.rb +7 -7
- data/tasks/thor/scaffold.rb +23 -23
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
- data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
- metadata +72 -84
- data/MAINTAINERS +0 -2
- data/TODO.md +0 -25
- data/spec/praxis/api_resource_spec.rb +0 -0
- data/spec/praxis/dispatcher_spec.rb +0 -0
- data/spec/spec_app/app/responses/bulk_response.rb +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'spec_media_types'
|
|
2
4
|
|
|
3
5
|
Praxis::ApiDefinition.define do
|
|
@@ -37,10 +39,8 @@ class PeopleResource
|
|
|
37
39
|
attribute :id, Integer, required: true
|
|
38
40
|
end
|
|
39
41
|
end
|
|
40
|
-
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
|
|
44
44
|
class AddressResource
|
|
45
45
|
include Praxis::EndpointDefinition
|
|
46
46
|
|
|
@@ -68,5 +68,4 @@ class AddressResource
|
|
|
68
68
|
attribute :id, Integer, required: true
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
|
-
|
|
72
71
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'praxis/extensions/field_selection'
|
|
2
4
|
class Person < Praxis::MediaType
|
|
3
|
-
identifier
|
|
5
|
+
identifier 'application/vnd.acme.person'
|
|
4
6
|
|
|
5
7
|
attributes do
|
|
6
8
|
attribute :id, Integer
|
|
@@ -13,7 +15,6 @@ class Person < Praxis::MediaType
|
|
|
13
15
|
attribute :name
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
|
|
17
18
|
class CollectionSummary < Praxis::MediaType
|
|
18
19
|
attributes do
|
|
19
20
|
attribute :href, String
|
|
@@ -23,11 +24,9 @@ class Person < Praxis::MediaType
|
|
|
23
24
|
default_fieldset do
|
|
24
25
|
attribute :href
|
|
25
26
|
end
|
|
26
|
-
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
|
|
31
30
|
class Address < Praxis::MediaType
|
|
32
31
|
identifier 'application/json'
|
|
33
32
|
|
|
@@ -55,26 +54,23 @@ class Address < Praxis::MediaType
|
|
|
55
54
|
end
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
57
|
class Blog < Praxis::MediaType
|
|
62
58
|
identifier 'application/vnd.bloggy.blog'
|
|
63
|
-
description
|
|
59
|
+
description 'A Bloggy Blog'
|
|
64
60
|
|
|
65
61
|
attributes do
|
|
66
62
|
attribute :id, Integer,
|
|
67
|
-
|
|
63
|
+
description: 'Unique identifier'
|
|
68
64
|
attribute :name, String,
|
|
69
|
-
|
|
65
|
+
description: 'Short name'
|
|
70
66
|
attribute :href, String,
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
example: proc { |o, _ctx| "/api/v1.0/blogs/#{o.id}" },
|
|
68
|
+
description: 'Href for use with this API'
|
|
73
69
|
attribute :description, String,
|
|
74
|
-
|
|
70
|
+
description: 'Longer description'
|
|
75
71
|
attribute :url, String,
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
example: proc { |o, _ctx| "example.com/blogs/#{o.id}" },
|
|
73
|
+
description: 'URL for a web browser'
|
|
78
74
|
|
|
79
75
|
attribute :timestamps do
|
|
80
76
|
attribute :created_at, DateTime
|
|
@@ -82,17 +78,16 @@ class Blog < Praxis::MediaType
|
|
|
82
78
|
end
|
|
83
79
|
|
|
84
80
|
attribute :tags, Attributor::Collection.of(String),
|
|
85
|
-
|
|
81
|
+
description: 'Array of tags'
|
|
86
82
|
|
|
87
83
|
attribute :recent_posts, Praxis::Collection.of(Post),
|
|
88
|
-
|
|
84
|
+
description: 'Array of recent related Post resources'
|
|
89
85
|
attribute :owner, User,
|
|
90
|
-
|
|
86
|
+
description: 'Related User resource'
|
|
91
87
|
|
|
92
88
|
attribute :posts_summary, Post::CollectionSummary,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
example: proc { |blog, ctx| Post::CollectionSummary.example(ctx, href: "#{blog.href}/posts") },
|
|
90
|
+
description: 'Summary of information from related Post resources'
|
|
96
91
|
end
|
|
97
92
|
|
|
98
93
|
default_fieldset do
|
|
@@ -107,7 +102,6 @@ class Blog < Praxis::MediaType
|
|
|
107
102
|
end
|
|
108
103
|
end
|
|
109
104
|
|
|
110
|
-
|
|
111
105
|
class Post < Praxis::MediaType
|
|
112
106
|
identifier 'application/vnd.bloggy.post'
|
|
113
107
|
|
|
@@ -115,23 +109,23 @@ class Post < Praxis::MediaType
|
|
|
115
109
|
|
|
116
110
|
attributes do
|
|
117
111
|
attribute :id, Integer,
|
|
118
|
-
|
|
112
|
+
description: 'Unique identifier'
|
|
119
113
|
attribute :href, String,
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
example: proc { |o, _ctx| "/api/v1.0/posts/#{o.id}" },
|
|
115
|
+
description: 'Href for use with this API'
|
|
122
116
|
|
|
123
117
|
attribute :title, String,
|
|
124
|
-
|
|
118
|
+
example: /\w+/
|
|
125
119
|
attribute :content, String,
|
|
126
|
-
|
|
120
|
+
example: /[:sentence:]{4,5}/
|
|
127
121
|
attribute :url, String,
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
description: 'URL for a web browser',
|
|
123
|
+
example: proc { |o, _ctx| "example.com/posts/#{o.id}" }
|
|
130
124
|
|
|
131
125
|
attribute :author, User,
|
|
132
|
-
|
|
126
|
+
description: 'Related User resource'
|
|
133
127
|
attribute :blog, Blog,
|
|
134
|
-
|
|
128
|
+
description: 'Related Blog resource'
|
|
135
129
|
|
|
136
130
|
attribute :followup_posts, Attributor::Collection.of(Post)
|
|
137
131
|
|
|
@@ -161,21 +155,20 @@ class Post < Praxis::MediaType
|
|
|
161
155
|
end
|
|
162
156
|
end
|
|
163
157
|
|
|
164
|
-
|
|
165
158
|
class User < Praxis::MediaType
|
|
166
159
|
identifier 'application/vnd.bloggy.user'
|
|
167
160
|
|
|
168
161
|
attributes do
|
|
169
162
|
attribute :id, Integer
|
|
170
163
|
attribute :href, String,
|
|
171
|
-
|
|
164
|
+
example: proc { |o, _ctx| "/api/v1.0/users/#{o.id}" }
|
|
172
165
|
|
|
173
166
|
attribute :first, String, example: /[:first_name:]/
|
|
174
167
|
attribute :last, String, example: /[:last_name:]/
|
|
175
168
|
attribute :posts, Attributor::Collection.of(Post)
|
|
176
169
|
|
|
177
170
|
attribute :post_matrix, Attributor::Collection.of(Attributor::Collection.of(Post)),
|
|
178
|
-
|
|
171
|
+
description: 'matrix of posts with some row and some column axes that make sense'
|
|
179
172
|
attribute :daily_posts, Attributor::Collection.of(Attributor::Struct) do
|
|
180
173
|
attribute :day, String
|
|
181
174
|
attribute :posts, Attributor::Collection.of(Post)
|
|
@@ -189,7 +182,7 @@ class User < Praxis::MediaType
|
|
|
189
182
|
end
|
|
190
183
|
|
|
191
184
|
attribute :posts_summary, Post::CollectionSummary,
|
|
192
|
-
|
|
185
|
+
example: proc { |user, ctx| Post::CollectionSummary.example(ctx, href: "#{user.href}/posts") }
|
|
193
186
|
end
|
|
194
187
|
|
|
195
188
|
default_fieldset do
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'ostruct'
|
|
2
4
|
|
|
3
5
|
require 'praxis/mapper/active_model_compat'
|
|
@@ -63,15 +65,15 @@ end
|
|
|
63
65
|
class YamlArrayModel < OpenStruct
|
|
64
66
|
include Praxis::Mapper::ActiveModelCompat
|
|
65
67
|
def self._praxis_associations
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
{
|
|
69
|
+
parents: {
|
|
70
|
+
model: ParentModel,
|
|
71
|
+
primary_key: :id,
|
|
72
|
+
type: :one_to_many,
|
|
73
|
+
local_key_columns: [:id],
|
|
74
|
+
remote_key_columns: [:parent_id]
|
|
75
|
+
}
|
|
73
76
|
}
|
|
74
|
-
}
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
79
|
|
|
@@ -79,7 +81,7 @@ end
|
|
|
79
81
|
class BaseResource < Praxis::Mapper::Resource
|
|
80
82
|
def href
|
|
81
83
|
base_href = '' # "/api"
|
|
82
|
-
base_href + "/#{self.class.collection_name}/#{
|
|
84
|
+
base_href + "/#{self.class.collection_name}/#{id}"
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
property :href, dependencies: [:id]
|
|
@@ -93,30 +95,34 @@ end
|
|
|
93
95
|
|
|
94
96
|
class ParentResource < BaseResource
|
|
95
97
|
model ParentModel
|
|
98
|
+
|
|
99
|
+
property :display_name, dependencies: %i[simple_name id other_attribute]
|
|
96
100
|
end
|
|
97
101
|
|
|
98
102
|
class SimpleResource < BaseResource
|
|
99
103
|
model SimpleModel
|
|
100
104
|
|
|
101
|
-
resource_delegate :
|
|
105
|
+
resource_delegate other_model: [:other_attribute]
|
|
102
106
|
|
|
103
107
|
def other_resource
|
|
104
|
-
|
|
108
|
+
other_model
|
|
105
109
|
end
|
|
106
110
|
|
|
107
|
-
property :aliased_method, dependencies: [
|
|
111
|
+
property :aliased_method, dependencies: %i[column1 other_model]
|
|
108
112
|
property :other_resource, dependencies: [:other_model]
|
|
109
113
|
|
|
110
|
-
property :parent, dependencies: [
|
|
114
|
+
property :parent, dependencies: %i[parent added_column]
|
|
111
115
|
|
|
112
116
|
property :name, dependencies: [:simple_name]
|
|
113
|
-
property :direct_other_name, dependencies: [
|
|
114
|
-
property :aliased_other_name, dependencies: [
|
|
117
|
+
property :direct_other_name, dependencies: ['other_model.name']
|
|
118
|
+
property :aliased_other_name, dependencies: ['other_model.display_name']
|
|
115
119
|
|
|
116
120
|
property :everything, dependencies: [:*]
|
|
117
121
|
property :everything_from_parent, dependencies: ['parent.*']
|
|
118
|
-
property :circular_dep, dependencies: [
|
|
122
|
+
property :circular_dep, dependencies: %i[circular_dep column1]
|
|
119
123
|
property :no_deps, dependencies: []
|
|
124
|
+
|
|
125
|
+
property :deep_nested_deps, dependencies: ['parent.simple_children.other_model.parent.display_name']
|
|
120
126
|
end
|
|
121
127
|
|
|
122
128
|
class YamlArrayResource < BaseResource
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'singleton'
|
|
2
4
|
|
|
3
5
|
module SimpleAuthenticationPlugin
|
|
@@ -7,7 +9,8 @@ module SimpleAuthenticationPlugin
|
|
|
7
9
|
include Singleton
|
|
8
10
|
|
|
9
11
|
def initialize
|
|
10
|
-
@options = {config_file: 'config/authentication.yml'}
|
|
12
|
+
@options = { config_file: 'config/authentication.yml' }
|
|
13
|
+
super
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def config_key
|
|
@@ -23,10 +26,8 @@ module SimpleAuthenticationPlugin
|
|
|
23
26
|
def self.authenticate(request)
|
|
24
27
|
request.current_user == 'guest'
|
|
25
28
|
end
|
|
26
|
-
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
|
|
30
31
|
module Request
|
|
31
32
|
def current_user
|
|
32
33
|
'guest'
|
|
@@ -39,15 +40,11 @@ module SimpleAuthenticationPlugin
|
|
|
39
40
|
included do
|
|
40
41
|
before :action do |controller|
|
|
41
42
|
action = controller.request.action
|
|
42
|
-
if action.authentication_required
|
|
43
|
-
Plugin.authenticate(controller.request)
|
|
44
|
-
end
|
|
43
|
+
Plugin.authenticate(controller.request) if action.authentication_required
|
|
45
44
|
end
|
|
46
45
|
end
|
|
47
|
-
|
|
48
46
|
end
|
|
49
47
|
|
|
50
|
-
|
|
51
48
|
module ActionDefinition
|
|
52
49
|
extend ActiveSupport::Concern
|
|
53
50
|
|
|
@@ -64,6 +61,5 @@ module SimpleAuthenticationPlugin
|
|
|
64
61
|
def authentication_required
|
|
65
62
|
@authentication_required ||= false
|
|
66
63
|
end
|
|
67
|
-
|
|
68
64
|
end
|
|
69
65
|
end
|
data/tasks/loader.thor
CHANGED
data/tasks/thor/app.rb
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module PraxisGen
|
|
2
4
|
class App < Thor
|
|
3
5
|
include Thor::Actions
|
|
4
6
|
|
|
5
|
-
namespace
|
|
7
|
+
namespace 'praxis:app'
|
|
6
8
|
def self.source_root
|
|
7
|
-
File.dirname(__FILE__)
|
|
9
|
+
"#{File.dirname(__FILE__)}/templates/generator/empty_app"
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
argument :app_name, required: true
|
|
11
|
-
desc
|
|
13
|
+
desc 'new', 'Generates a blank new app under <app_name> (with a full skeleton ready to start coding)'
|
|
12
14
|
|
|
13
15
|
# Generator for a blank new app (with a full skeleton ready to get you going)
|
|
14
16
|
def new
|
|
15
17
|
puts "Creating new blank Praxis app under #{app_name}"
|
|
16
18
|
# Copy example files
|
|
17
|
-
['config.ru','Gemfile','Rakefile','README.md'].each do |file|
|
|
19
|
+
['config.ru', 'Gemfile', 'Rakefile', 'README.md'].each do |file|
|
|
18
20
|
copy_file file, verbose: true
|
|
19
21
|
end
|
|
20
22
|
# Copy example directories
|
|
21
|
-
root_dirs = [
|
|
23
|
+
root_dirs = %w[config app design spec docs]
|
|
22
24
|
root_dirs.each do |dir|
|
|
23
25
|
directory dir, recursive: true
|
|
24
26
|
end
|
data/tasks/thor/example.rb
CHANGED
|
@@ -1,55 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
3
|
module PraxisGen
|
|
3
4
|
class Example < Thor
|
|
4
5
|
include Thor::Actions
|
|
5
6
|
|
|
6
|
-
namespace
|
|
7
|
+
namespace 'praxis:example'
|
|
7
8
|
|
|
8
9
|
def self.source_root
|
|
9
|
-
File.dirname(__FILE__)
|
|
10
|
+
"#{File.dirname(__FILE__)}/templates/generator/example_app"
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
argument :app_name, required: true
|
|
13
|
-
desc
|
|
14
|
+
desc 'example', 'Generates a new example application under an <app_name> directory to showcase some features'
|
|
14
15
|
|
|
15
16
|
def example
|
|
16
17
|
sanitized = app_name.downcase.gsub(/[^a-z0-9_\-.]/, '')
|
|
17
18
|
puts "APP_NAME: #{app_name}"
|
|
18
|
-
raise
|
|
19
|
+
raise 'Please use only letters, numbers, underscores, dashes or periods for the app name' unless sanitized == app_name
|
|
19
20
|
|
|
20
21
|
# Copy example files
|
|
21
|
-
root_files = ['Gemfile','config.ru','Rakefile']
|
|
22
|
+
root_files = ['Gemfile', 'config.ru', 'Rakefile']
|
|
22
23
|
root_files.each do |file|
|
|
23
24
|
copy_file file, verbose: true
|
|
24
25
|
end
|
|
25
26
|
# Copy example directories
|
|
26
|
-
root_dirs = [
|
|
27
|
+
root_dirs = %w[app config design db spec]
|
|
27
28
|
root_dirs.each do |dir|
|
|
28
29
|
directory dir, recursive: true
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
puts
|
|
32
|
-
puts
|
|
33
|
+
puts 'To run the example application:'
|
|
33
34
|
puts
|
|
34
35
|
puts " cd #{app_name}"
|
|
35
|
-
puts
|
|
36
|
-
puts
|
|
37
|
-
puts
|
|
38
|
-
puts
|
|
39
|
-
puts
|
|
40
|
-
puts
|
|
41
|
-
puts
|
|
36
|
+
puts ' bundle'
|
|
37
|
+
puts ' bundle exec rake db:recreate # To create/migrate/seed the dev DB'
|
|
38
|
+
puts ' bundle exec rackup # To start the web server'
|
|
39
|
+
puts
|
|
40
|
+
puts 'From another terminal/app, use curl (or your favorite HTTP client) to retrieve data from the API'
|
|
41
|
+
puts ' For example: '
|
|
42
|
+
puts ' Get all users without filters or limit, and display only id, and first_name fields'
|
|
42
43
|
puts " curl -G -H 'X-Api-Version: 1' http://localhost:9292/users \\"
|
|
43
|
-
puts
|
|
44
|
+
puts ' --data-urlencode "fields=id,first_name"'
|
|
44
45
|
puts
|
|
45
|
-
puts
|
|
46
|
-
puts
|
|
46
|
+
puts ' Get the last 5 users, with last_names starting with "L" ordered by first_name (descending)'
|
|
47
|
+
puts ' and display only id, first_name, last_name, and email fields'
|
|
47
48
|
puts " curl -G -H 'X-Api-Version: 1' http://localhost:9292/users \\"
|
|
48
|
-
puts
|
|
49
|
-
puts
|
|
50
|
-
puts
|
|
51
|
-
puts
|
|
52
|
-
puts
|
|
49
|
+
puts ' --data-urlencode "filters=last_name=L*" \\'
|
|
50
|
+
puts ' --data-urlencode "pagination=by=first_name,items=5" \\'
|
|
51
|
+
puts ' --data-urlencode "order=-first_name" \\'
|
|
52
|
+
puts ' --data-urlencode "fields=id,first_name,last_name,email"'
|
|
53
|
+
puts ' (Note: To list all routes use: bundle exec rake praxis:routes)'
|
|
53
54
|
puts
|
|
54
55
|
nil
|
|
55
56
|
end
|
data/tasks/thor/model.rb
CHANGED
|
@@ -4,23 +4,23 @@ module PraxisGen
|
|
|
4
4
|
class Model < Thor
|
|
5
5
|
require 'active_support/inflector'
|
|
6
6
|
include Thor::Actions
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
def self.source_root
|
|
9
|
-
File.dirname(__FILE__)
|
|
9
|
+
"#{File.dirname(__FILE__)}/templates/generator/scaffold"
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
desc
|
|
12
|
+
desc 'gmodel', 'Generates a skeleton model file under app/models for ActiveRecord or Sequel.'
|
|
13
13
|
argument :model_name, required: true
|
|
14
|
-
option :orm, required: false, default: 'activerecord', enum: [
|
|
14
|
+
option :orm, required: false, default: 'activerecord', enum: %w[activerecord sequel]
|
|
15
15
|
def g
|
|
16
|
-
#self.class.check_name(model_name)
|
|
16
|
+
# self.class.check_name(model_name)
|
|
17
17
|
template_file = \
|
|
18
18
|
if options[:orm] == 'activerecord'
|
|
19
19
|
'models/active_record.rb'
|
|
20
20
|
else
|
|
21
21
|
'models/sequel.rb'
|
|
22
22
|
end
|
|
23
|
-
puts "Generating Model for #{model_name}"
|
|
23
|
+
puts "Generating Model for #{model_name}"
|
|
24
24
|
template template_file, "app/models/#{model_name}.rb"
|
|
25
25
|
nil
|
|
26
26
|
end
|
|
@@ -34,7 +34,7 @@ module PraxisGen
|
|
|
34
34
|
# TODO: do we want the argument to be camelcase? or snake case?
|
|
35
35
|
def self.check_name(name)
|
|
36
36
|
sanitized = name.downcase.gsub(/[^a-z0-9_]/, '')
|
|
37
|
-
raise
|
|
37
|
+
raise 'Please use only downcase letters, numbers and underscores for the model' unless sanitized == name
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
end
|
data/tasks/thor/scaffold.rb
CHANGED
|
@@ -4,32 +4,32 @@ module PraxisGen
|
|
|
4
4
|
class Scaffold < Thor
|
|
5
5
|
require 'active_support/inflector'
|
|
6
6
|
include Thor::Actions
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
attr_reader :actions_hash
|
|
9
9
|
|
|
10
10
|
def self.source_root
|
|
11
|
-
File.dirname(__FILE__)
|
|
11
|
+
"#{File.dirname(__FILE__)}/templates/generator/scaffold"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
desc
|
|
14
|
+
desc 'g', 'Generates an API design and implementation scaffold for managing a collection of <collection_name>'
|
|
15
15
|
argument :collection_name, required: true
|
|
16
16
|
option :version, required: false, default: '1',
|
|
17
|
-
|
|
17
|
+
desc: 'Version string for the API endpoint. This also dictates the directory structure (i.e., v1/endpoints/...))'
|
|
18
18
|
option :design, type: :boolean, default: true,
|
|
19
|
-
|
|
19
|
+
desc: 'Include the Endpoint and MediaType files for the collection'
|
|
20
20
|
option :implementation, type: :boolean, default: true,
|
|
21
|
-
|
|
21
|
+
desc: 'Include the Controller and (possibly the) Resource files for the collection (see --no-resource)'
|
|
22
22
|
option :resource, type: :boolean, default: true,
|
|
23
|
-
|
|
24
|
-
option :model, type: :string, enum: [
|
|
25
|
-
|
|
26
|
-
option :actions, type: :string, default: 'crud', enum: [
|
|
27
|
-
|
|
23
|
+
desc: 'Disable (or enable) the creation of the Resource files when generating implementation'
|
|
24
|
+
option :model, type: :string, enum: %w[activerecord sequel],
|
|
25
|
+
desc: 'It also generates a model for the given ORM. An empty --model flag will default to activerecord'
|
|
26
|
+
option :actions, type: :string, default: 'crud', enum: %w[cr cru crud u ud d],
|
|
27
|
+
desc: 'Specifies the actions to generate for the API. cr=create, u=update, d=delete. Index and show actions are always generated'
|
|
28
28
|
def g
|
|
29
29
|
self.class.check_name(collection_name)
|
|
30
30
|
@actions_hash = self.class.compose_actions_hash(options[:actions])
|
|
31
|
-
env_rb = Pathname.new(destination_root)+Pathname.new(
|
|
32
|
-
@pagination_plugin_found = File.open(env_rb).grep(/Praxis::Plugins::PaginationPlugin.*/).reject{|l| l.strip[0] == '#'}.present?
|
|
31
|
+
env_rb = Pathname.new(destination_root) + Pathname.new('config/environment.rb')
|
|
32
|
+
@pagination_plugin_found = File.open(env_rb).grep(/Praxis::Plugins::PaginationPlugin.*/).reject { |l| l.strip[0] == '#' }.present?
|
|
33
33
|
if options[:design]
|
|
34
34
|
say_status 'Design', "Generating scaffold for #{plural_class}", :blue
|
|
35
35
|
template 'design/media_types/item.rb', "design/#{version_dir}/media_types/#{collection_name.singularize}.rb"
|
|
@@ -38,14 +38,14 @@ module PraxisGen
|
|
|
38
38
|
if options[:implementation]
|
|
39
39
|
say_status 'Implement', "Generating scaffold for #{plural_class}", :blue
|
|
40
40
|
if options[:resource]
|
|
41
|
-
base_resource = Pathname.new(destination_root)+Pathname.new("app/#{version_dir}/resources/base.rb")
|
|
41
|
+
base_resource = Pathname.new(destination_root) + Pathname.new("app/#{version_dir}/resources/base.rb")
|
|
42
42
|
unless base_resource.exist?
|
|
43
43
|
# Copy an appropriate base resource for the version (resources within same version must share same base)
|
|
44
|
-
say_status
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
say_status
|
|
48
|
-
|
|
44
|
+
say_status 'NOTE:',
|
|
45
|
+
"Creating a base resource file for resources to inherit from (at 'app/#{version_dir}/resources/base.rb')",
|
|
46
|
+
:yellow
|
|
47
|
+
say_status '',
|
|
48
|
+
'If you had already other resources in the app, change them to derive from this Base'
|
|
49
49
|
template 'implementation/resources/base.rb', "app/#{version_dir}/resources/base.rb"
|
|
50
50
|
end
|
|
51
51
|
template 'implementation/resources/item.rb', "app/#{version_dir}/resources/#{collection_name.singularize}.rb"
|
|
@@ -60,11 +60,11 @@ module PraxisGen
|
|
|
60
60
|
def plural_class
|
|
61
61
|
collection_name.camelize
|
|
62
62
|
end
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
def singular_class
|
|
65
65
|
collection_name.singularize.camelize
|
|
66
66
|
end
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
def version
|
|
69
69
|
options[:version]
|
|
70
70
|
end
|
|
@@ -72,7 +72,7 @@ module PraxisGen
|
|
|
72
72
|
def version_module
|
|
73
73
|
"V#{version}"
|
|
74
74
|
end
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
def version_dir
|
|
77
77
|
version_module.camelize(:lower)
|
|
78
78
|
end
|
|
@@ -111,7 +111,7 @@ module PraxisGen
|
|
|
111
111
|
def self.check_name(name)
|
|
112
112
|
sanitized = name.downcase.gsub(/[^a-z0-9_]/, '')
|
|
113
113
|
# TODO: bail or support CamelCase collections (for now only snake case)
|
|
114
|
-
raise
|
|
114
|
+
raise 'Please use only downcase letters, numbers and underscores for the collection' unless sanitized == name
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
end
|
|
@@ -5,14 +5,6 @@ module V1
|
|
|
5
5
|
class User < Base
|
|
6
6
|
model ::User
|
|
7
7
|
|
|
8
|
-
# Mappings for the allowed filters
|
|
9
|
-
filters_mapping(
|
|
10
|
-
'uuid': 'uuid',
|
|
11
|
-
'first_name': 'first_name',
|
|
12
|
-
'last_name': 'last_name',
|
|
13
|
-
'email': 'email'
|
|
14
|
-
)
|
|
15
|
-
|
|
16
8
|
# To compute the full_name (method below) we need to load first and last names from the DB
|
|
17
9
|
property :full_name, dependencies: %i[first_name last_name]
|
|
18
10
|
|
|
@@ -6,9 +6,8 @@ module <%= version_module %>
|
|
|
6
6
|
model ::<%= singular_class %> # Change it if it maps to a different DB model class
|
|
7
7
|
|
|
8
8
|
# Define the name mapping from API filter params, to model attribute/associations
|
|
9
|
-
# when they aren't 1:1
|
|
9
|
+
# when they aren't 1:1 the same
|
|
10
10
|
# filters_mapping(
|
|
11
|
-
# 'name': 'name',
|
|
12
11
|
# 'label': 'association.label_name'
|
|
13
12
|
# )
|
|
14
13
|
|