praxis 2.0.pre.9 → 2.0.pre.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +11 -0
  4. data/TODO.md +1 -4
  5. data/bin/praxis +11 -13
  6. data/lib/praxis.rb +10 -3
  7. data/lib/praxis/action_definition.rb +15 -13
  8. data/lib/praxis/action_definition/headers_dsl_compiler.rb +0 -7
  9. data/lib/praxis/api_general_info.rb +1 -1
  10. data/lib/praxis/application.rb +6 -2
  11. data/lib/praxis/blueprint.rb +357 -0
  12. data/lib/praxis/bootloader.rb +9 -3
  13. data/lib/praxis/bootloader_stages/environment.rb +15 -13
  14. data/lib/praxis/collection.rb +1 -11
  15. data/lib/praxis/config_hash.rb +44 -0
  16. data/lib/praxis/docs/{openapi → open_api}/info_object.rb +0 -0
  17. data/lib/praxis/docs/{openapi → open_api}/media_type_object.rb +0 -0
  18. data/lib/praxis/docs/{openapi → open_api}/operation_object.rb +0 -0
  19. data/lib/praxis/docs/{openapi → open_api}/parameter_object.rb +0 -0
  20. data/lib/praxis/docs/{openapi → open_api}/paths_object.rb +0 -0
  21. data/lib/praxis/docs/{openapi → open_api}/request_body_object.rb +0 -0
  22. data/lib/praxis/docs/{openapi → open_api}/response_object.rb +0 -0
  23. data/lib/praxis/docs/{openapi → open_api}/responses_object.rb +0 -0
  24. data/lib/praxis/docs/{openapi → open_api}/schema_object.rb +0 -0
  25. data/lib/praxis/docs/{openapi → open_api}/server_object.rb +0 -0
  26. data/lib/praxis/docs/{openapi → open_api}/tag_object.rb +0 -0
  27. data/lib/praxis/docs/open_api_generator.rb +90 -5
  28. data/lib/praxis/endpoint_definition.rb +273 -0
  29. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
  30. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +1 -1
  31. data/lib/praxis/extensions/field_expansion.rb +3 -36
  32. data/lib/praxis/extensions/pagination.rb +2 -2
  33. data/lib/praxis/extensions/pagination/ordering_params.rb +1 -1
  34. data/lib/praxis/extensions/pagination/pagination_params.rb +6 -4
  35. data/lib/praxis/field_expander.rb +90 -0
  36. data/lib/praxis/finalizable.rb +34 -0
  37. data/lib/praxis/mapper/selector_generator.rb +1 -1
  38. data/lib/praxis/media_type.rb +3 -68
  39. data/lib/praxis/plugin_concern.rb +1 -1
  40. data/lib/praxis/plugins/mapper_plugin.rb +7 -7
  41. data/lib/praxis/renderer.rb +88 -0
  42. data/lib/praxis/request.rb +1 -1
  43. data/lib/praxis/resource_definition.rb +2 -311
  44. data/lib/praxis/response_definition.rb +2 -10
  45. data/lib/praxis/response_template.rb +3 -3
  46. data/lib/praxis/router.rb +2 -2
  47. data/lib/praxis/routing_config.rb +1 -1
  48. data/lib/praxis/tasks/api_docs.rb +13 -63
  49. data/lib/praxis/tasks/routes.rb +1 -1
  50. data/lib/praxis/types/media_type_common.rb +1 -11
  51. data/lib/praxis/version.rb +1 -1
  52. data/praxis.gemspec +0 -1
  53. data/spec/functional_spec.rb +5 -9
  54. data/spec/praxis/action_definition_spec.rb +12 -20
  55. data/spec/praxis/blueprint_spec.rb +373 -0
  56. data/spec/praxis/bootloader_spec.rb +10 -2
  57. data/spec/praxis/collection_spec.rb +0 -13
  58. data/spec/praxis/config_hash_spec.rb +64 -0
  59. data/spec/praxis/{resource_definition_spec.rb → endpoint_definition_spec.rb} +37 -64
  60. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +4 -6
  61. data/spec/praxis/extensions/field_expansion_spec.rb +5 -24
  62. data/spec/praxis/field_expander_spec.rb +149 -0
  63. data/spec/praxis/media_type_identifier_spec.rb +5 -4
  64. data/spec/praxis/media_type_spec.rb +4 -93
  65. data/spec/praxis/renderer_spec.rb +188 -0
  66. data/spec/praxis/response_definition_spec.rb +0 -31
  67. data/spec/praxis/response_spec.rb +1 -1
  68. data/spec/praxis/router_spec.rb +8 -8
  69. data/spec/praxis/routing_config_spec.rb +3 -3
  70. data/spec/spec_app/app/controllers/instances.rb +13 -7
  71. data/spec/spec_app/design/media_types/instance.rb +1 -19
  72. data/spec/spec_app/design/media_types/volume.rb +1 -1
  73. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -14
  74. data/spec/spec_app/design/resources/instances.rb +5 -8
  75. data/spec/spec_app/design/resources/volume_snapshots.rb +1 -1
  76. data/spec/spec_app/design/resources/volumes.rb +1 -1
  77. data/spec/support/spec_authorization_plugin.rb +1 -1
  78. data/spec/support/spec_blueprints.rb +72 -0
  79. data/spec/support/{spec_resource_definitions.rb → spec_endpoint_definitions.rb} +2 -2
  80. data/spec/support/spec_media_types.rb +6 -26
  81. data/tasks/thor/app.rb +8 -34
  82. data/tasks/thor/example.rb +45 -285
  83. data/tasks/thor/templates/generator/empty_app/.gitignore +0 -1
  84. data/tasks/thor/templates/generator/empty_app/Gemfile +7 -23
  85. data/tasks/thor/templates/generator/empty_app/README.md +1 -1
  86. data/tasks/thor/templates/generator/empty_app/Rakefile +4 -13
  87. data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.empty_directory +0 -0
  88. data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.gitkeep +0 -0
  89. data/tasks/thor/templates/generator/empty_app/config/environment.rb +25 -17
  90. data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.empty_directory +0 -0
  91. data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.gitkeep +0 -0
  92. data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.empty_directory +0 -0
  93. data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.gitkeep +0 -0
  94. data/tasks/thor/templates/generator/empty_app/docs/.empty_directory +0 -0
  95. data/tasks/thor/templates/generator/empty_app/docs/.gitkeep +0 -0
  96. data/tasks/thor/templates/generator/empty_app/spec/spec_helper.rb +14 -9
  97. data/tasks/thor/templates/generator/example_app/.gitignore +1 -0
  98. data/tasks/thor/templates/generator/example_app/Gemfile +19 -0
  99. data/tasks/thor/templates/generator/example_app/Rakefile +54 -0
  100. data/tasks/thor/templates/generator/example_app/app/models/user.rb +6 -0
  101. data/tasks/thor/templates/generator/example_app/app/v1/controllers/users.rb +17 -0
  102. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +46 -0
  103. data/tasks/thor/templates/generator/example_app/config.ru +31 -0
  104. data/tasks/thor/templates/generator/example_app/config/environment.rb +40 -0
  105. data/tasks/thor/templates/generator/example_app/db/migrate/20201010101010_create_users_table.rb +11 -0
  106. data/tasks/thor/templates/generator/example_app/design/api.rb +18 -0
  107. data/tasks/thor/templates/generator/example_app/design/v1/endpoints/users.rb +37 -0
  108. data/tasks/thor/templates/generator/example_app/design/v1/media_types/user.rb +26 -0
  109. data/tasks/thor/templates/generator/example_app/spec/helpers/database_helper.rb +18 -0
  110. data/tasks/thor/templates/generator/example_app/spec/spec_helper.rb +42 -0
  111. data/tasks/thor/templates/generator/example_app/spec/v1/controllers/users_spec.rb +37 -0
  112. metadata +49 -135
  113. data/lib/api_browser/.bowerrc +0 -3
  114. data/lib/api_browser/.editorconfig +0 -21
  115. data/lib/api_browser/Gruntfile.js +0 -581
  116. data/lib/api_browser/app/index.html +0 -59
  117. data/lib/api_browser/app/js/app.js +0 -48
  118. data/lib/api_browser/app/js/controllers/action.js +0 -47
  119. data/lib/api_browser/app/js/controllers/controller.js +0 -10
  120. data/lib/api_browser/app/js/controllers/menu.js +0 -93
  121. data/lib/api_browser/app/js/controllers/trait.js +0 -10
  122. data/lib/api_browser/app/js/controllers/type.js +0 -24
  123. data/lib/api_browser/app/js/directives/attribute_description.js +0 -56
  124. data/lib/api_browser/app/js/directives/attribute_table.js +0 -28
  125. data/lib/api_browser/app/js/directives/conditional_requirements.js +0 -13
  126. data/lib/api_browser/app/js/directives/fixed_if_fits.js +0 -38
  127. data/lib/api_browser/app/js/directives/highlight.js +0 -14
  128. data/lib/api_browser/app/js/directives/menu_item.js +0 -59
  129. data/lib/api_browser/app/js/directives/no_container.js +0 -8
  130. data/lib/api_browser/app/js/directives/readable_list.js +0 -87
  131. data/lib/api_browser/app/js/directives/request_examples.js +0 -31
  132. data/lib/api_browser/app/js/directives/type_placeholder.js +0 -30
  133. data/lib/api_browser/app/js/directives/url.js +0 -15
  134. data/lib/api_browser/app/js/factories/Configuration.js +0 -12
  135. data/lib/api_browser/app/js/factories/Documentation.js +0 -61
  136. data/lib/api_browser/app/js/factories/Example.js +0 -51
  137. data/lib/api_browser/app/js/factories/PageInfo.js +0 -9
  138. data/lib/api_browser/app/js/factories/normalize_attributes.js +0 -20
  139. data/lib/api_browser/app/js/factories/prepare_template.js +0 -15
  140. data/lib/api_browser/app/js/factories/template_for.js +0 -128
  141. data/lib/api_browser/app/js/filters/attribute_name.js +0 -10
  142. data/lib/api_browser/app/js/filters/friendly_json.js +0 -5
  143. data/lib/api_browser/app/js/filters/has_requirement.js +0 -14
  144. data/lib/api_browser/app/js/filters/header_info.js +0 -9
  145. data/lib/api_browser/app/js/filters/is_empty.js +0 -8
  146. data/lib/api_browser/app/js/filters/markdown.js +0 -6
  147. data/lib/api_browser/app/js/filters/resource_name.js +0 -5
  148. data/lib/api_browser/app/js/filters/tag_requirement.js +0 -13
  149. data/lib/api_browser/app/sass/modules/_body.scss +0 -40
  150. data/lib/api_browser/app/sass/modules/_cloke.scss +0 -8
  151. data/lib/api_browser/app/sass/modules/_header.scss +0 -10
  152. data/lib/api_browser/app/sass/modules/_nav.scss +0 -7
  153. data/lib/api_browser/app/sass/modules/_sidebar.scss +0 -134
  154. data/lib/api_browser/app/sass/modules/_switch.scss +0 -55
  155. data/lib/api_browser/app/sass/modules/_table.scss +0 -13
  156. data/lib/api_browser/app/sass/praxis.scss +0 -70
  157. data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +0 -774
  158. data/lib/api_browser/app/views/action.html +0 -97
  159. data/lib/api_browser/app/views/builtin/field-selector.html +0 -24
  160. data/lib/api_browser/app/views/controller.html +0 -55
  161. data/lib/api_browser/app/views/directives/attribute_description.html +0 -2
  162. data/lib/api_browser/app/views/directives/attribute_description/default.html +0 -2
  163. data/lib/api_browser/app/views/directives/attribute_description/example.html +0 -13
  164. data/lib/api_browser/app/views/directives/attribute_description/headers.html +0 -8
  165. data/lib/api_browser/app/views/directives/attribute_description/member_options.html +0 -4
  166. data/lib/api_browser/app/views/directives/attribute_description/values.html +0 -14
  167. data/lib/api_browser/app/views/directives/attribute_table.html +0 -17
  168. data/lib/api_browser/app/views/directives/menu_item.html +0 -8
  169. data/lib/api_browser/app/views/directives/url.html +0 -3
  170. data/lib/api_browser/app/views/examples/general.html +0 -26
  171. data/lib/api_browser/app/views/home.html +0 -5
  172. data/lib/api_browser/app/views/layout.html +0 -8
  173. data/lib/api_browser/app/views/menu.html +0 -42
  174. data/lib/api_browser/app/views/navbar.html +0 -9
  175. data/lib/api_browser/app/views/trait.html +0 -13
  176. data/lib/api_browser/app/views/type.html +0 -6
  177. data/lib/api_browser/app/views/type/details.html +0 -33
  178. data/lib/api_browser/app/views/types/embedded/array.html +0 -2
  179. data/lib/api_browser/app/views/types/embedded/default.html +0 -12
  180. data/lib/api_browser/app/views/types/embedded/field-selector.html +0 -13
  181. data/lib/api_browser/app/views/types/embedded/links.html +0 -11
  182. data/lib/api_browser/app/views/types/embedded/requirements.html +0 -6
  183. data/lib/api_browser/app/views/types/embedded/single_req.html +0 -9
  184. data/lib/api_browser/app/views/types/embedded/struct.html +0 -14
  185. data/lib/api_browser/app/views/types/label/link.html +0 -1
  186. data/lib/api_browser/app/views/types/label/primitive.html +0 -1
  187. data/lib/api_browser/app/views/types/label/primitive_collection.html +0 -1
  188. data/lib/api_browser/app/views/types/label/type.html +0 -1
  189. data/lib/api_browser/app/views/types/label/type_collection.html +0 -1
  190. data/lib/api_browser/app/views/types/main/array.html +0 -22
  191. data/lib/api_browser/app/views/types/main/default.html +0 -23
  192. data/lib/api_browser/app/views/types/main/hash.html +0 -23
  193. data/lib/api_browser/app/views/types/standalone/array.html +0 -3
  194. data/lib/api_browser/app/views/types/standalone/default.html +0 -18
  195. data/lib/api_browser/app/views/types/standalone/struct.html +0 -2
  196. data/lib/api_browser/bower_template.json +0 -41
  197. data/lib/api_browser/package-lock.json +0 -7110
  198. data/lib/api_browser/package.json +0 -43
  199. data/lib/praxis/docs/generator.rb +0 -243
  200. data/lib/praxis/docs/link_builder.rb +0 -30
  201. data/lib/praxis/links.rb +0 -135
  202. data/lib/praxis/types/multipart.rb +0 -109
  203. data/spec/api_browser/directives/type_placeholder_spec.js +0 -134
  204. data/spec/api_browser/factories/configuration_spec.js +0 -32
  205. data/spec/api_browser/factories/documentation_spec.js +0 -100
  206. data/spec/api_browser/factories/normalize_attributes_spec.js +0 -92
  207. data/spec/api_browser/factories/template_for_spec.js +0 -67
  208. data/spec/api_browser/filters/attribute_name_spec.js +0 -23
  209. data/spec/praxis/types/multipart_spec.rb +0 -112
  210. data/tasks/thor/templates/generator/empty_app/.rspec +0 -1
  211. data/tasks/thor/templates/generator/empty_app/Guardfile +0 -3
  212. data/tasks/thor/templates/generator/empty_app/config/rainbows.rb +0 -57
  213. data/tasks/thor/templates/generator/empty_app/docs/app.js +0 -1
  214. data/tasks/thor/templates/generator/empty_app/docs/styles.scss +0 -3
@@ -1,5 +1,4 @@
1
1
  log
2
2
  .bundle
3
- api_docs
4
3
  docs/output
5
4
  docs/json
@@ -1,29 +1,13 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'praxis', github: 'praxis/praxis'
4
- gem 'attributor', github: 'praxis/attributor'
5
- gem 'praxis-blueprints', github: 'praxis/praxis-blueprints'
6
-
7
- gem 'rainbows', require: false
8
- gem 'rake'
9
- gem 'pry'
10
- gem 'pry-byebug'
3
+ gem 'praxis'
4
+ gem 'rack'
11
5
 
12
6
  group :development, :test do
13
- gem "rack-test", :require => "rack/test"
7
+ gem 'rack-test'
14
8
  gem "rspec" # needed for rake task
15
- gem 'rspec-its'
16
- gem 'rspec-collection_matchers'
17
-
18
- gem 'foreman', :require => false
19
-
20
- platform :mri_19, :mri_20 do
21
- gem 'shotgun', :require => false
22
- gem "guard"
23
- gem "guard-rspec"
24
- end
25
- end
26
9
 
27
- platform :jruby do
28
- gem 'jruby-openssl'
29
- end
10
+ gem 'rake'
11
+ gem 'pry'
12
+ gem 'pry-byebug'
13
+ end
@@ -1,4 +1,4 @@
1
- Sample Blog Application
1
+ Example Application
2
2
  =======================
3
3
 
4
4
 
@@ -5,21 +5,12 @@ Bundler.require(:default, ENV['RACK_ENV'])
5
5
 
6
6
  require 'praxis/tasks'
7
7
 
8
- task :default => :spec
9
-
10
- # load up the Praxis app
11
- task :environment do
12
- Praxis::Application.instance.setup
13
- end
14
-
15
- desc "console"
16
- task :console => :environment do
17
- require 'pry'
18
- require 'pry-byebug'
19
- pry
20
- end
8
+ desc "Drop into a console with the full application loaded"
9
+ task console: 'praxis:console'
21
10
 
22
11
  require 'rspec/core/rake_task'
23
12
  RSpec::Core::RakeTask.new(:spec)
24
13
 
25
14
  Dir["lib/tasks/**/*.rake"].each { |ext| load ext }
15
+
16
+ task :default => :spec
@@ -1,17 +1,25 @@
1
- #
2
- #Praxis::Application.instance.layout do
3
- # map :initializers, 'config/initializers/**/*'
4
- # map :lib, 'lib/**/*'
5
- # map :design, 'design/' do
6
- # map :api, 'api.rb'
7
- # map :media_types, '**/media_types/**/*'
8
- # map :resources, '**/resources/**/*'
9
- # end
10
- # map :app, 'app/' do
11
- # map :models, 'models/**/*'
12
- # map :controllers, '**/controllers/**/*'
13
- # map :responses, '**/responses/**/*'
14
- # end
15
- #end
16
- #
17
- #
1
+
2
+ Praxis::Application.configure do |application|
3
+ # This is a commented out copy of the default Praxis layout
4
+ # This example app follows the standard practices, so there is no reason to override it
5
+ # If we wanted to organize the structure and ordering of files, we can uncomment the layout
6
+ # and define it at our own leisure
7
+ # application.layout do
8
+ # map :initializers, 'config/initializers/**/*'
9
+ # map :lib, 'lib/**/*'
10
+ # map :design, 'design/' do
11
+ # map :api, 'api.rb'
12
+ # map :helpers, '**/helpers/**/*'
13
+ # map :types, '**/types/**/*'
14
+ # map :media_types, '**/media_types/**/*'
15
+ # map :endpoints, '**/endpoints/**/*'
16
+ # end
17
+ # map :app, 'app/' do
18
+ # map :models, 'models/**/*'
19
+ # map :responses, '**/responses/**/*'
20
+ # map :exceptions, '**/exceptions/**/*'
21
+ # map :resources, '**/resources/**/*'
22
+ # map :controllers, '**/controllers/**/*'
23
+ # end
24
+ # end
25
+ end
@@ -4,15 +4,20 @@ Bundler.require :default, :test
4
4
  RSpec.configure do |config|
5
5
  config.include Rack::Test::Methods
6
6
 
7
- config.before(:suite) do
8
- Praxis::Blueprint.caching_enabled = true
9
- Praxis::Application.instance.setup
10
- end
7
+ # config.before(:suite) do
8
+ # Praxis::Blueprint.caching_enabled = true
9
+ # end
11
10
 
12
- config.before(:each) do
13
- Praxis::Blueprint.cache = Hash.new do |hash, key|
14
- hash[key] = Hash.new
15
- end
16
- end
11
+ # config.before(:each) do
12
+ # Praxis::Blueprint.cache = Hash.new do |hash, key|
13
+ # hash[key] = Hash.new
14
+ # end
15
+ # end
16
+ end
17
17
 
18
+ def app
19
+ Rack::Builder.parse_file(File.expand_path('../config.ru', __dir__)).first
20
+ rescue => e
21
+ puts "Application failed to initialize: #{e}"
22
+ exit 1
18
23
  end
@@ -0,0 +1 @@
1
+ development.sqlite3
@@ -0,0 +1,19 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'praxis'
4
+ gem 'rack'
5
+ gem 'sqlite3'
6
+ gem 'activerecord'
7
+ gem 'puma' # A much better web server than the default webrick
8
+ gem 'parslet' # For field selection extension
9
+ gem 'link_header' # For pagination extensions
10
+
11
+ group :development, :test do
12
+ gem 'rake'
13
+ gem 'rack-test'
14
+ gem "rspec" # needed for rake task
15
+ gem 'database_cleaner' # For transactional DB tests
16
+
17
+ gem 'pry'
18
+ gem 'pry-byebug'
19
+ end
@@ -0,0 +1,54 @@
1
+ ENV['RACK_ENV'] = 'development'
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ require 'praxis'
6
+ require 'praxis/tasks'
7
+
8
+ # Setup Activerecord migrations
9
+ require 'active_record'
10
+ db_dir = File.expand_path('../db', __dir__)
11
+ ActiveRecord::Tasks::DatabaseTasks.env = ENV['RACK_ENV']
12
+ ActiveRecord::Tasks::DatabaseTasks.db_dir = db_dir
13
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths = File.join(db_dir, 'migrate')
14
+
15
+ # Redefine the environment task to load our app
16
+ Rake::Task['praxis:environment'].clear
17
+ namespace :praxis do
18
+ task :environment do
19
+ FULL_APP = Rack::Builder.parse_file(File.expand_path('./config.ru', __dir__)).first
20
+ end
21
+ end
22
+
23
+ Dir['lib/tasks/**/*.rake'].each { |ext| load ext }
24
+
25
+ # Inlined basic DB tasks to avoid packaging more files and directories
26
+ namespace :db do
27
+ require 'active_record/tasks/database_tasks'
28
+
29
+ desc 'migrate'
30
+ task migrate: 'praxis:environment' do
31
+ ActiveRecord::Tasks::DatabaseTasks.migrate
32
+ puts "Database migrated."
33
+ end
34
+
35
+ desc 'seed with example data'
36
+ task seed: 'praxis:environment' do
37
+ require_relative 'spec/helpers/database_helper'
38
+ DatabaseHelper.seed!
39
+ end
40
+
41
+ desc 'drops current database'
42
+ task drop: 'praxis:environment' do
43
+ db_file = "development.sqlite3"
44
+ puts "Removing file #{db_file}"
45
+ `rm -f #{db_file}`
46
+ end
47
+
48
+ desc 'creates configured database'
49
+ task create: 'praxis:environment' do
50
+ puts "Creating file development.sqlite3"
51
+ ActiveRecord::Base.connection.execute("SELECT 1")
52
+ end
53
+ end
54
+
@@ -0,0 +1,6 @@
1
+
2
+ class User < ActiveRecord::Base
3
+ # So it can be used in all the automatic query/filtering extensions
4
+ include Praxis::Mapper::ActiveModelCompat
5
+
6
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module V1
4
+ module Controllers
5
+ class Users
6
+ include Praxis::Controller
7
+ include Praxis::Extensions::Rendering
8
+
9
+ implements V1::Endpoints::Users
10
+
11
+ def index
12
+ objects = build_query(::User)
13
+ display(objects)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module V1
4
+ module Resources
5
+ class User < Praxis::Mapper::Resource
6
+ model ::User
7
+
8
+ # Mappings for the allowed filterd
9
+ filters_mapping(
10
+ 'email': 'email',
11
+ 'first_name': 'first_name',
12
+ # Complex (convoluted?) mapping of state, just to show how we can modify and adapt the values/fields/operators
13
+ 'state': lambda do |spec|
14
+ case spec[:value].to_s
15
+ when 'pending' # Pending users do not have a uuid
16
+ { name: :uuid, value: nil, op: spec[:op] }
17
+ when 'active' # Active users do not have a uuid (so "flip" the original equality condition)
18
+ opposite_op = spec[:op] == '=' ? '!=' : '='
19
+ { name: :uuid, value: nil, op: opposite_op }
20
+ else
21
+ raise "Cannot filter users by state #{spec[:value]}"
22
+ end
23
+ end,
24
+ )
25
+
26
+ # Example of a property that depends on a differently named DB field
27
+ property :uid, dependencies: %i[id]
28
+ # To compute the full_name (method below) we need to load first and last names from the DB
29
+ property :full_name, dependencies: %i[first_name last_name]
30
+
31
+ def uid
32
+ id # underlying id field of the model
33
+ end
34
+
35
+ # Computed attribute: if uuid nil, user in in a pending stat, else active
36
+ def state
37
+ self.uuid.nil? ? 'pending' : 'active'
38
+ end
39
+
40
+ # Computed attribute the combines first and last
41
+ def full_name
42
+ [first_name, last_name].join(' ')
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ # -p 8888
2
+
3
+ ENV['RACK_ENV'] ||= 'development'
4
+ Bundler.setup(:default, ENV['RACK_ENV'])
5
+ Bundler.require(:default, ENV['RACK_ENV'])
6
+
7
+ # Want to take advantage of some of the Praxis' extensions for:
8
+ # API field selection (a la GraphQL) - for querying and rendering
9
+ # API filtering extensions (to add "where clauses") in listings
10
+ # Views and partial rendering (for ActiveRecord models)
11
+
12
+ require 'praxis/plugins/mapper_plugin'
13
+ require 'praxis/mapper/active_model_compat'
14
+ require 'praxis/extensions/field_selection'
15
+ require 'praxis/plugins/pagination_plugin'
16
+
17
+ # Start the sqlite DB
18
+ case ENV['RACK_ENV']
19
+ when 'test'
20
+ ActiveRecord::Base.establish_connection(
21
+ adapter: 'sqlite3',
22
+ database: ':memory:'
23
+ )
24
+ else
25
+ ActiveRecord::Base.establish_connection(
26
+ adapter: 'sqlite3',
27
+ database: "development.sqlite3"
28
+ )
29
+ end
30
+
31
+ run Praxis::Application.instance.setup
@@ -0,0 +1,40 @@
1
+
2
+ Praxis::Application.configure do |application|
3
+
4
+ # Configure the Mapper plugin (if we want to use all the filtering/field_selection extensions)
5
+ application.bootloader.use Praxis::Plugins::MapperPlugin
6
+ # Cconfigure the Pagination plugin (if we want to use all the pagination/ordering extensions)
7
+ application.bootloader.use Praxis::Plugins::PaginationPlugin, {
8
+ # max_items: 500, # Unlimited by default,
9
+ # default_page_size: 100,
10
+ # paging_default_mode: {by: :id},
11
+ # disallow_paging_by_default: false,
12
+ # disallow_cursor_by_default: false,
13
+ # sorting: {
14
+ # enforce_all_fields: true
15
+ # }
16
+ }
17
+
18
+ # This is a commented out copy of the default Praxis layout
19
+ # This example app follows the standard practices, so there is no reason to override it
20
+ # If we wanted to organize the structure and ordering of files, we can uncomment the layout
21
+ # and define it at our own leisure
22
+ # application.layout do
23
+ # map :initializers, 'config/initializers/**/*'
24
+ # map :lib, 'lib/**/*'
25
+ # map :design, 'design/' do
26
+ # map :api, 'api.rb'
27
+ # map :helpers, '**/helpers/**/*'
28
+ # map :types, '**/types/**/*'
29
+ # map :media_types, '**/media_types/**/*'
30
+ # map :endpoints, '**/endpoints/**/*'
31
+ # end
32
+ # map :app, 'app/' do
33
+ # map :models, 'models/**/*'
34
+ # map :responses, '**/responses/**/*'
35
+ # map :exceptions, '**/exceptions/**/*'
36
+ # map :resources, '**/resources/**/*'
37
+ # map :controllers, '**/controllers/**/*'
38
+ # end
39
+ # end
40
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUsersTable < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :users do |table|
6
+ table.column :uuid, :integer
7
+ table.column :first_name, :string
8
+ table.column :last_name, :string
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # Use this file to define your overall api behavior, response templates and traits.
2
+ Praxis::ApiDefinition.define do
3
+ info do
4
+ name 'example'
5
+ title 'Example API'
6
+
7
+ # Attributes for OpenAPI docs
8
+ termsOfService 'https://mysitehere.com'
9
+ contact name: 'API Info', email: 'info@mysitehere.com'
10
+ end
11
+
12
+ # Trait that when included will require a Bearer authorization header to be passed in.
13
+ trait :authorized do
14
+ headers do
15
+ key "Authorization", String, regexp: /^.*Bearer\s/, required: true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module V1
4
+ module Endpoints
5
+ class Users
6
+ include Praxis::EndpointDefinition
7
+ # include AuthenticatedEndpoint
8
+
9
+ media_type MediaTypes::User
10
+ version '1'
11
+
12
+ description 'Endpoints that allow the listing and manipulation of users'
13
+
14
+ action :index do
15
+ description 'List users'
16
+ routing { get '' }
17
+ params do
18
+ attribute :fields, Praxis::Types::FieldSelector.for(MediaTypes::User),
19
+ description: 'Fields with which to render the result.'
20
+ attribute :filters, Praxis::Types::FilteringParams.for(MediaTypes::User) do
21
+ filter 'state', using: ['=', '!=']
22
+ filter 'first_name', using: ['=', '!='], fuzzy: true
23
+ filter 'last_name', using: ['=', '!='], fuzzy: true
24
+ end
25
+ attribute :pagination, Praxis::Types::PaginationParams.for(MediaTypes::User) do
26
+ by_fields :uid, :first_name, :last_name
27
+ end
28
+ attribute :order, Praxis::Extensions::Pagination::OrderingParams.for(MediaTypes::User) do
29
+ by_fields :uid, :last_name, :first_name
30
+ end
31
+ end
32
+ response :ok, media_type: Praxis::Collection.of(MediaTypes::User)
33
+ end
34
+ end
35
+ end
36
+ end
37
+