praxis 2.0.pre.8 → 2.0.pre.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +1 -3
  5. data/CHANGELOG.md +33 -0
  6. data/TODO.md +1 -4
  7. data/bin/praxis +67 -12
  8. data/lib/praxis.rb +10 -3
  9. data/lib/praxis/action_definition.rb +15 -13
  10. data/lib/praxis/action_definition/headers_dsl_compiler.rb +0 -7
  11. data/lib/praxis/api_general_info.rb +1 -1
  12. data/lib/praxis/application.rb +6 -2
  13. data/lib/praxis/blueprint.rb +357 -0
  14. data/lib/praxis/bootloader.rb +9 -3
  15. data/lib/praxis/bootloader_stages/environment.rb +16 -13
  16. data/lib/praxis/collection.rb +1 -11
  17. data/lib/praxis/config_hash.rb +44 -0
  18. data/lib/praxis/docs/{openapi → open_api}/info_object.rb +18 -10
  19. data/lib/praxis/docs/{openapi → open_api}/media_type_object.rb +0 -0
  20. data/lib/praxis/docs/{openapi → open_api}/operation_object.rb +0 -0
  21. data/lib/praxis/docs/{openapi → open_api}/parameter_object.rb +0 -0
  22. data/lib/praxis/docs/{openapi → open_api}/paths_object.rb +0 -0
  23. data/lib/praxis/docs/{openapi → open_api}/request_body_object.rb +0 -0
  24. data/lib/praxis/docs/{openapi → open_api}/response_object.rb +0 -0
  25. data/lib/praxis/docs/{openapi → open_api}/responses_object.rb +0 -0
  26. data/lib/praxis/docs/{openapi → open_api}/schema_object.rb +0 -0
  27. data/lib/praxis/docs/{openapi → open_api}/server_object.rb +0 -0
  28. data/lib/praxis/docs/{openapi → open_api}/tag_object.rb +0 -0
  29. data/lib/praxis/docs/open_api_generator.rb +91 -6
  30. data/lib/praxis/endpoint_definition.rb +273 -0
  31. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +182 -58
  32. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +3 -2
  33. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +47 -56
  34. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +153 -0
  35. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +20 -8
  36. data/lib/praxis/extensions/field_expansion.rb +3 -36
  37. data/lib/praxis/extensions/pagination.rb +5 -32
  38. data/lib/praxis/extensions/pagination/ordering_params.rb +1 -1
  39. data/lib/praxis/extensions/pagination/pagination_params.rb +6 -4
  40. data/lib/praxis/field_expander.rb +90 -0
  41. data/lib/praxis/finalizable.rb +34 -0
  42. data/lib/praxis/mapper/active_model_compat.rb +4 -0
  43. data/lib/praxis/mapper/resource.rb +18 -2
  44. data/lib/praxis/mapper/selector_generator.rb +2 -1
  45. data/lib/praxis/mapper/sequel_compat.rb +7 -0
  46. data/lib/praxis/media_type.rb +3 -68
  47. data/lib/praxis/plugin_concern.rb +1 -1
  48. data/lib/praxis/plugins/mapper_plugin.rb +24 -15
  49. data/lib/praxis/plugins/pagination_plugin.rb +34 -4
  50. data/lib/praxis/renderer.rb +88 -0
  51. data/lib/praxis/request.rb +1 -1
  52. data/lib/praxis/resource_definition.rb +2 -311
  53. data/lib/praxis/response_definition.rb +2 -10
  54. data/lib/praxis/response_template.rb +3 -3
  55. data/lib/praxis/router.rb +2 -2
  56. data/lib/praxis/routing_config.rb +1 -1
  57. data/lib/praxis/tasks/api_docs.rb +17 -64
  58. data/lib/praxis/tasks/routes.rb +1 -1
  59. data/lib/praxis/types/media_type_common.rb +1 -11
  60. data/lib/praxis/version.rb +1 -1
  61. data/praxis.gemspec +0 -1
  62. data/spec/functional_spec.rb +5 -9
  63. data/spec/praxis/action_definition_spec.rb +12 -20
  64. data/spec/praxis/blueprint_spec.rb +373 -0
  65. data/spec/praxis/bootloader_spec.rb +10 -2
  66. data/spec/praxis/collection_spec.rb +0 -13
  67. data/spec/praxis/config_hash_spec.rb +64 -0
  68. data/spec/praxis/{resource_definition_spec.rb → endpoint_definition_spec.rb} +37 -64
  69. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +249 -168
  70. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +25 -6
  71. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +190 -8
  72. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +140 -0
  73. data/spec/praxis/extensions/field_expansion_spec.rb +5 -24
  74. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +1 -1
  75. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +1 -1
  76. data/spec/praxis/extensions/support/spec_resources_active_model.rb +1 -1
  77. data/spec/praxis/field_expander_spec.rb +149 -0
  78. data/spec/praxis/mapper/selector_generator_spec.rb +1 -1
  79. data/spec/praxis/media_type_identifier_spec.rb +5 -4
  80. data/spec/praxis/media_type_spec.rb +4 -93
  81. data/spec/praxis/renderer_spec.rb +188 -0
  82. data/spec/praxis/response_definition_spec.rb +0 -31
  83. data/spec/praxis/response_spec.rb +1 -1
  84. data/spec/praxis/router_spec.rb +8 -8
  85. data/spec/praxis/routing_config_spec.rb +3 -3
  86. data/spec/spec_app/app/controllers/instances.rb +13 -7
  87. data/spec/spec_app/design/media_types/instance.rb +1 -19
  88. data/spec/spec_app/design/media_types/volume.rb +1 -1
  89. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -14
  90. data/spec/spec_app/design/resources/instances.rb +5 -8
  91. data/spec/spec_app/design/resources/volume_snapshots.rb +1 -1
  92. data/spec/spec_app/design/resources/volumes.rb +1 -1
  93. data/spec/support/spec_authorization_plugin.rb +1 -1
  94. data/spec/support/spec_blueprints.rb +72 -0
  95. data/spec/support/{spec_resource_definitions.rb → spec_endpoint_definitions.rb} +2 -2
  96. data/spec/support/spec_media_types.rb +6 -26
  97. data/tasks/thor/app.rb +8 -34
  98. data/tasks/thor/example.rb +51 -285
  99. data/tasks/thor/model.rb +40 -0
  100. data/tasks/thor/scaffold.rb +117 -0
  101. data/tasks/thor/templates/generator/empty_app/.gitignore +0 -1
  102. data/tasks/thor/templates/generator/empty_app/Gemfile +7 -23
  103. data/tasks/thor/templates/generator/empty_app/README.md +1 -1
  104. data/tasks/thor/templates/generator/empty_app/Rakefile +4 -13
  105. data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.empty_directory +0 -0
  106. data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.gitkeep +0 -0
  107. data/tasks/thor/templates/generator/empty_app/config/environment.rb +26 -17
  108. data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.empty_directory +0 -0
  109. data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.gitkeep +0 -0
  110. data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.empty_directory +0 -0
  111. data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.gitkeep +0 -0
  112. data/tasks/thor/templates/generator/empty_app/docs/.empty_directory +0 -0
  113. data/tasks/thor/templates/generator/empty_app/docs/.gitkeep +0 -0
  114. data/tasks/thor/templates/generator/empty_app/spec/spec_helper.rb +14 -9
  115. data/tasks/thor/templates/generator/example_app/.gitignore +1 -0
  116. data/tasks/thor/templates/generator/example_app/Gemfile +19 -0
  117. data/tasks/thor/templates/generator/example_app/Rakefile +61 -0
  118. data/tasks/thor/templates/generator/example_app/app/models/user.rb +6 -0
  119. data/tasks/thor/templates/generator/example_app/app/v1/concerns/controller_base.rb +24 -0
  120. data/tasks/thor/templates/generator/example_app/app/v1/controllers/users.rb +17 -0
  121. data/tasks/thor/templates/generator/example_app/app/v1/resources/base.rb +11 -0
  122. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +25 -0
  123. data/tasks/thor/templates/generator/example_app/config.ru +30 -0
  124. data/tasks/thor/templates/generator/example_app/config/environment.rb +41 -0
  125. data/tasks/thor/templates/generator/example_app/db/migrate/20201010101010_create_users_table.rb +12 -0
  126. data/tasks/thor/templates/generator/example_app/db/seeds.rb +6 -0
  127. data/tasks/thor/templates/generator/example_app/design/api.rb +18 -0
  128. data/tasks/thor/templates/generator/example_app/design/v1/endpoints/users.rb +37 -0
  129. data/tasks/thor/templates/generator/example_app/design/v1/media_types/user.rb +21 -0
  130. data/tasks/thor/templates/generator/example_app/spec/helpers/database_helper.rb +20 -0
  131. data/tasks/thor/templates/generator/example_app/spec/spec_helper.rb +42 -0
  132. data/tasks/thor/templates/generator/example_app/spec/v1/controllers/users_spec.rb +37 -0
  133. data/tasks/thor/templates/generator/scaffold/design/endpoints/collection.rb +98 -0
  134. data/tasks/thor/templates/generator/scaffold/design/media_types/item.rb +18 -0
  135. data/tasks/thor/templates/generator/scaffold/implementation/controllers/collection.rb +77 -0
  136. data/tasks/thor/templates/generator/scaffold/implementation/resources/base.rb +11 -0
  137. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +45 -0
  138. data/tasks/thor/templates/generator/scaffold/models/active_record.rb +6 -0
  139. data/tasks/thor/templates/generator/scaffold/models/sequel.rb +6 -0
  140. metadata +64 -136
  141. data/lib/api_browser/.bowerrc +0 -3
  142. data/lib/api_browser/.editorconfig +0 -21
  143. data/lib/api_browser/Gruntfile.js +0 -581
  144. data/lib/api_browser/app/index.html +0 -59
  145. data/lib/api_browser/app/js/app.js +0 -48
  146. data/lib/api_browser/app/js/controllers/action.js +0 -47
  147. data/lib/api_browser/app/js/controllers/controller.js +0 -10
  148. data/lib/api_browser/app/js/controllers/menu.js +0 -93
  149. data/lib/api_browser/app/js/controllers/trait.js +0 -10
  150. data/lib/api_browser/app/js/controllers/type.js +0 -24
  151. data/lib/api_browser/app/js/directives/attribute_description.js +0 -56
  152. data/lib/api_browser/app/js/directives/attribute_table.js +0 -28
  153. data/lib/api_browser/app/js/directives/conditional_requirements.js +0 -13
  154. data/lib/api_browser/app/js/directives/fixed_if_fits.js +0 -38
  155. data/lib/api_browser/app/js/directives/highlight.js +0 -14
  156. data/lib/api_browser/app/js/directives/menu_item.js +0 -59
  157. data/lib/api_browser/app/js/directives/no_container.js +0 -8
  158. data/lib/api_browser/app/js/directives/readable_list.js +0 -87
  159. data/lib/api_browser/app/js/directives/request_examples.js +0 -31
  160. data/lib/api_browser/app/js/directives/type_placeholder.js +0 -30
  161. data/lib/api_browser/app/js/directives/url.js +0 -15
  162. data/lib/api_browser/app/js/factories/Configuration.js +0 -12
  163. data/lib/api_browser/app/js/factories/Documentation.js +0 -61
  164. data/lib/api_browser/app/js/factories/Example.js +0 -51
  165. data/lib/api_browser/app/js/factories/PageInfo.js +0 -9
  166. data/lib/api_browser/app/js/factories/normalize_attributes.js +0 -20
  167. data/lib/api_browser/app/js/factories/prepare_template.js +0 -15
  168. data/lib/api_browser/app/js/factories/template_for.js +0 -128
  169. data/lib/api_browser/app/js/filters/attribute_name.js +0 -10
  170. data/lib/api_browser/app/js/filters/friendly_json.js +0 -5
  171. data/lib/api_browser/app/js/filters/has_requirement.js +0 -14
  172. data/lib/api_browser/app/js/filters/header_info.js +0 -9
  173. data/lib/api_browser/app/js/filters/is_empty.js +0 -8
  174. data/lib/api_browser/app/js/filters/markdown.js +0 -6
  175. data/lib/api_browser/app/js/filters/resource_name.js +0 -5
  176. data/lib/api_browser/app/js/filters/tag_requirement.js +0 -13
  177. data/lib/api_browser/app/sass/modules/_body.scss +0 -40
  178. data/lib/api_browser/app/sass/modules/_cloke.scss +0 -8
  179. data/lib/api_browser/app/sass/modules/_header.scss +0 -10
  180. data/lib/api_browser/app/sass/modules/_nav.scss +0 -7
  181. data/lib/api_browser/app/sass/modules/_sidebar.scss +0 -134
  182. data/lib/api_browser/app/sass/modules/_switch.scss +0 -55
  183. data/lib/api_browser/app/sass/modules/_table.scss +0 -13
  184. data/lib/api_browser/app/sass/praxis.scss +0 -70
  185. data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +0 -774
  186. data/lib/api_browser/app/views/action.html +0 -97
  187. data/lib/api_browser/app/views/builtin/field-selector.html +0 -24
  188. data/lib/api_browser/app/views/controller.html +0 -55
  189. data/lib/api_browser/app/views/directives/attribute_description.html +0 -2
  190. data/lib/api_browser/app/views/directives/attribute_description/default.html +0 -2
  191. data/lib/api_browser/app/views/directives/attribute_description/example.html +0 -13
  192. data/lib/api_browser/app/views/directives/attribute_description/headers.html +0 -8
  193. data/lib/api_browser/app/views/directives/attribute_description/member_options.html +0 -4
  194. data/lib/api_browser/app/views/directives/attribute_description/values.html +0 -14
  195. data/lib/api_browser/app/views/directives/attribute_table.html +0 -17
  196. data/lib/api_browser/app/views/directives/menu_item.html +0 -8
  197. data/lib/api_browser/app/views/directives/url.html +0 -3
  198. data/lib/api_browser/app/views/examples/general.html +0 -26
  199. data/lib/api_browser/app/views/home.html +0 -5
  200. data/lib/api_browser/app/views/layout.html +0 -8
  201. data/lib/api_browser/app/views/menu.html +0 -42
  202. data/lib/api_browser/app/views/navbar.html +0 -9
  203. data/lib/api_browser/app/views/trait.html +0 -13
  204. data/lib/api_browser/app/views/type.html +0 -6
  205. data/lib/api_browser/app/views/type/details.html +0 -33
  206. data/lib/api_browser/app/views/types/embedded/array.html +0 -2
  207. data/lib/api_browser/app/views/types/embedded/default.html +0 -12
  208. data/lib/api_browser/app/views/types/embedded/field-selector.html +0 -13
  209. data/lib/api_browser/app/views/types/embedded/links.html +0 -11
  210. data/lib/api_browser/app/views/types/embedded/requirements.html +0 -6
  211. data/lib/api_browser/app/views/types/embedded/single_req.html +0 -9
  212. data/lib/api_browser/app/views/types/embedded/struct.html +0 -14
  213. data/lib/api_browser/app/views/types/label/link.html +0 -1
  214. data/lib/api_browser/app/views/types/label/primitive.html +0 -1
  215. data/lib/api_browser/app/views/types/label/primitive_collection.html +0 -1
  216. data/lib/api_browser/app/views/types/label/type.html +0 -1
  217. data/lib/api_browser/app/views/types/label/type_collection.html +0 -1
  218. data/lib/api_browser/app/views/types/main/array.html +0 -22
  219. data/lib/api_browser/app/views/types/main/default.html +0 -23
  220. data/lib/api_browser/app/views/types/main/hash.html +0 -23
  221. data/lib/api_browser/app/views/types/standalone/array.html +0 -3
  222. data/lib/api_browser/app/views/types/standalone/default.html +0 -18
  223. data/lib/api_browser/app/views/types/standalone/struct.html +0 -2
  224. data/lib/api_browser/bower_template.json +0 -41
  225. data/lib/api_browser/package-lock.json +0 -7110
  226. data/lib/api_browser/package.json +0 -43
  227. data/lib/praxis/docs/generator.rb +0 -243
  228. data/lib/praxis/docs/link_builder.rb +0 -30
  229. data/lib/praxis/links.rb +0 -135
  230. data/lib/praxis/types/multipart.rb +0 -109
  231. data/spec/api_browser/directives/type_placeholder_spec.js +0 -134
  232. data/spec/api_browser/factories/configuration_spec.js +0 -32
  233. data/spec/api_browser/factories/documentation_spec.js +0 -100
  234. data/spec/api_browser/factories/normalize_attributes_spec.js +0 -92
  235. data/spec/api_browser/factories/template_for_spec.js +0 -67
  236. data/spec/api_browser/filters/attribute_name_spec.js +0 -23
  237. data/spec/praxis/types/multipart_spec.rb +0 -112
  238. data/tasks/thor/templates/generator/empty_app/.rspec +0 -1
  239. data/tasks/thor/templates/generator/empty_app/Guardfile +0 -3
  240. data/tasks/thor/templates/generator/empty_app/config/rainbows.rb +0 -57
  241. data/tasks/thor/templates/generator/empty_app/docs/app.js +0 -1
  242. data/tasks/thor/templates/generator/empty_app/docs/styles.scss +0 -3
data/tasks/thor/app.rb CHANGED
@@ -13,41 +13,15 @@ module PraxisGen
13
13
  # Generator for a blank new app (with a full skeleton ready to get you going)
14
14
  def new
15
15
  puts "Creating new blank Praxis app under #{app_name}"
16
- create_root_files
17
- create_config
18
- create_app
19
- create_design
20
- create_spec
21
- create_docs
22
- end
23
-
24
- private
25
- def create_root_files
26
- ['config.ru','Gemfile','Guardfile','Rakefile','README.md'].each do |file|
27
- copy_file file, "#{app_name}/#{file}"
16
+ # Copy example files
17
+ ['config.ru','Gemfile','Rakefile','README.md'].each do |file|
18
+ copy_file file, verbose: true
19
+ end
20
+ # Copy example directories
21
+ root_dirs = ['config','app','design','spec','docs']
22
+ root_dirs.each do |dir|
23
+ directory dir, recursive: true
28
24
  end
29
25
  end
30
-
31
- def create_config
32
- copy_file "config/environment.rb", "#{app_name}/config/environment.rb"
33
- copy_file "config/rainbows.rb", "#{app_name}/config/rainbows.rb"
34
- end
35
-
36
- def create_app
37
- directory "app", "#{app_name}/app", :recursive => true
38
- end
39
-
40
- def create_design
41
- directory "design", "#{app_name}/design", :recursive => true
42
- end
43
-
44
- def create_spec
45
- directory "spec", "#{app_name}/spec", :recursive => true
46
- end
47
-
48
- def create_docs
49
- directory "docs", "#{app_name}/docs", :recursive => true
50
- end
51
-
52
26
  end
53
27
  end
@@ -1,291 +1,57 @@
1
1
 
2
2
  module PraxisGen
3
- class Example < Thor
4
- include Thor::Actions
5
-
6
- namespace "praxis:example"
7
-
8
- argument :app_name, required: true
9
- desc "new", "Generates a new 'hello world' example application under an <app_name> directory"
10
-
11
- def new
12
- puts "GENERATION COMMENCED!! (for #{app_name})"
13
- # Fix weird symbols in the app name (if they are)
14
- @app_name = app_name.downcase.gsub(/[^a-z0-9_\/.]/, '')
15
- # Generate a new app
16
- empty_directory path('app')
17
- empty_directory path('design')
18
- empty_directory path('lib')
19
- empty_directory path('spec')
20
- generate_config_environment_rb
21
- generate_gemfile
22
- generate_rakefile
23
- generate_config_ru
24
- generate_app_definitions_hello_world
25
- generate_app_controllers_hello_world
26
- #
27
- puts
28
- puts "To run the example application:"
29
- puts
30
- puts " # terminal 1:"
31
- puts " cd #{app_name}"
32
- puts " bundle"
33
- puts " bundle exec rackup"
34
- puts
35
- puts " # terminal 2:"
36
- puts " # Index: list the hello words (especifying api version through the query string) "
37
- puts " curl -i 'http://localhost:8888/api/hello?api_version=1.0' -H 'Authorization: Bearer XYZ' "
38
- puts ""
39
- puts " # Show: list one of the hello words (especifying api version through a header) "
40
- puts " curl -i 'http://localhost:8888/api/hello/1' -H 'X-Api-Version: 1.0' -H 'Authorization: Bearer XYZ'"
41
- puts ""
42
- puts " # NotFound: Hello word will not be found under API 2.0"
43
- puts " curl -i 'http://localhost:8888/api/hello/1' -H 'X-Api-Version: 2.0' -H 'Authorization: Bearer XYZ'"
44
- puts " #Note: The Authorization header is made required in the application to emulate OAuth2 (but not used)"
45
- nil
46
- end
47
- private
48
- # Returns relative path for the new application
49
- #
50
- # @return [String]
51
- #
52
- # @example
53
- # # > /praxis generate My-test_praxisApp
54
- # app_dir_pathname #=> 'mytest_praxisapp'
55
- #
56
- #
57
- def app_dir_pathname
58
- @app_dir_pathname ||= Pathname.new(app_name)
59
- end
60
-
61
-
62
- # Returns path string built from the set of the given strings
63
- #
64
- # @param [String,Array] strings
65
- #
66
- # @return [String]
67
- #
68
- # @example
69
- # path('a', 'b', 'c') #=> 'my_test_app/a/b/c'
70
- #
71
- def path(*strings)
72
- app_dir_pathname.join(*strings).to_s
73
- end
74
-
75
-
76
- # Creates './config/environment.rb' file
77
- #
78
- # @return [void]
79
- #
80
- def generate_config_environment_rb
81
- create_file path('config/environment.rb') do
82
- <<-RUBY
83
- # Main entry point - DO NOT MODIFY THIS FILE
84
- ENV['RACK_ENV'] ||= 'development'
85
-
86
- Bundler.require(:default, ENV['RACK_ENV'])
87
-
88
- # Default application layout.
89
- # NOTE: This layout need NOT be specified explicitly.
90
- # It is provided just for illustration.
91
- Praxis::Application.instance.layout do
92
- map :initializers, 'config/initializers/**/*'
93
- map :lib, 'lib/**/*'
94
- map :design, 'design/' do
95
- map :api, 'api.rb'
96
- map :media_types, '**/media_types/**/*'
97
- map :resources, '**/resources/**/*'
98
- end
99
- map :app, 'app/' do
100
- map :models, 'models/**/*'
101
- map :controllers, '**/controllers/**/*'
102
- map :responses, '**/responses/**/*'
103
- end
104
- end
105
- RUBY
106
- end
107
- nil
108
- end
109
-
110
-
111
- # Creates './Gemfile' file
112
- #
113
- # @return [void]
114
- #
115
- def generate_gemfile
116
- create_file path('Gemfile') do
117
- <<-RUBY
118
- source 'https://rubygems.org'
119
-
120
- gem 'praxis'
121
- gem 'rack', '~> 1.0'
122
- gem 'rake'
123
-
124
- group :development, :test do
125
- gem 'rspec'
126
- end
127
- RUBY
128
- end
129
- nil
130
- end
131
-
132
-
133
- # Creates './Rakefile' file
134
- #
135
- # @return [void]
136
- #
137
- def generate_rakefile
138
- create_file path('Rakefile') do
139
- <<-RUBY
140
- require 'praxis'
141
- require 'praxis/tasks'
142
- RUBY
143
- end
144
- nil
145
- end
146
-
147
-
148
- # Creates './config.ru' file
149
- #
150
- # @return [void]
151
- #
152
- def generate_config_ru
153
- create_file path('config.ru') do
154
- <<-RUBY
155
- #\\ -p 8888
156
-
157
- require 'bundler/setup'
158
- require 'praxis'
159
-
160
- application = Praxis::Application.instance
161
- application.logger = Logger.new(STDOUT)
162
- application.setup
163
-
164
- run application
165
- RUBY
166
- end
3
+ class Example < Thor
4
+ include Thor::Actions
5
+
6
+ namespace "praxis:example"
7
+
8
+ def self.source_root
9
+ File.dirname(__FILE__) + "/templates/generator/example_app"
10
+ end
11
+
12
+ argument :app_name, required: true
13
+ desc "example", "Generates a new example application under an <app_name> directory to showcase some features"
14
+
15
+ def example
16
+ sanitized = app_name.downcase.gsub(/[^a-z0-9_\-.]/, '')
17
+ puts "APP_NAME: #{app_name}"
18
+ raise "Please use only letters, numbers, underscores, dashes or periods for the app name" unless sanitized == app_name
19
+
20
+ # Copy example files
21
+ root_files = ['Gemfile','config.ru','Rakefile']
22
+ root_files.each do |file|
23
+ copy_file file, verbose: true
24
+ end
25
+ # Copy example directories
26
+ root_dirs = ['app','config','design','db','spec']
27
+ root_dirs.each do |dir|
28
+ directory dir, recursive: true
29
+ end
30
+
31
+ puts
32
+ puts "To run the example application:"
33
+ puts
34
+ puts " cd #{app_name}"
35
+ puts " bundle"
36
+ puts " bundle exec rake db:recreate # To create/migrate/seed the dev DB"
37
+ puts " bundle exec rackup # To start the web server"
38
+ puts
39
+ puts "From another terminal/app, use curl (or your favorite HTTP client) to retrieve data from the API"
40
+ puts " For example: "
41
+ puts " Get all users without filters or limit, and display only id, and first_name fields"
42
+ puts " curl -G -H 'X-Api-Version: 1' http://localhost:9292/users \\"
43
+ puts " --data-urlencode \"fields=id,first_name\""
44
+ puts
45
+ puts " Get the last 5 users, with last_names starting with \"L\" ordered by first_name (descending)"
46
+ puts " and display only id, first_name, last_name, and email fields"
47
+ puts " curl -G -H 'X-Api-Version: 1' http://localhost:9292/users \\"
48
+ puts " --data-urlencode \"filters=last_name=L*\" \\"
49
+ puts " --data-urlencode \"pagination=by=first_name,items=5\" \\"
50
+ puts " --data-urlencode \"order=-first_name\" \\"
51
+ puts " --data-urlencode \"fields=id,first_name,last_name,email\""
52
+ puts " (Note: To list all routes use: bundle exec rake praxis:routes)"
53
+ puts
167
54
  nil
168
55
  end
169
-
170
-
171
- def generate_app_definitions_hello_world
172
- create_file path('design/api.rb') do
173
- <<-RUBY
174
- # Use this file to define your response templates and traits.
175
- #
176
- # For example, to define a response template:
177
- # response_template :custom do |media_type:|
178
- # status 200
179
- # media_type media_type
180
- # end
181
- Praxis::ApiDefinition.define do
182
- # Trait that when included will require a Bearer authorization header to be passed in.
183
- trait :authorized do
184
- headers do
185
- key "Authorization", String, regexp: /^.*Bearer\s/, required: true
186
- end
187
- end
188
- end
189
- RUBY
190
- end
191
-
192
- create_file path('design/resources/hello.rb') do
193
- <<-RUBY
194
- module V1
195
- module ApiResources
196
- class Hello
197
- include Praxis::ResourceDefinition
198
-
199
- media_type V1::MediaTypes::Hello
200
- version '1.0'
201
-
202
- prefix '/api/hello'
203
-
204
- # Will apply to all actions of this resource
205
- trait :authorized
206
-
207
- action_defaults do
208
- response :ok
209
- end
210
-
211
- action :index do
212
-
213
- routing do
214
- get ''
215
- end
216
- end
217
-
218
- action :show do
219
-
220
- routing do
221
- get '/:id'
222
- end
223
- params do
224
- attribute :id, Integer, required: true, min: 0
225
- end
226
- response :not_found
227
- end
228
- end
229
- end
230
- end
231
- RUBY
232
- end
233
-
234
- create_file path('design/media_types/hello.rb') do
235
- <<-RUBY
236
- module V1
237
- module MediaTypes
238
- class Hello < Praxis::MediaType
239
-
240
- identifier 'application/json'
241
-
242
- attributes do
243
- attribute :string, String
244
- end
245
-
246
- view :default do
247
- attribute :string
248
- end
249
- end
250
- end
251
- end
252
- RUBY
253
- end
254
- end
255
-
256
-
257
- def generate_app_controllers_hello_world
258
- create_file path('app/controllers/hello.rb') do
259
- <<-RUBY
260
- module V1
261
- class Hello
262
- include Praxis::Controller
263
-
264
- implements V1::ApiResources::Hello
265
-
266
- HELLO_WORLD = [ 'Hello world!', 'Привет мир!', 'Hola mundo!', '你好世界!', 'こんにちは世界!' ]
267
-
268
- def index(**params)
269
- response.headers['Content-Type'] = 'application/json'
270
- response.body = HELLO_WORLD.to_json
271
- response
272
- end
273
-
274
- def show(id:, **other_params)
275
- hello = HELLO_WORLD[id]
276
- if hello
277
- response.body = { id: id, data: hello }
278
- else
279
- self.response = Praxis::Responses::NotFound.new(body: "Hello word with index \#{id} not found in our DB")
280
- end
281
- response.headers['Content-Type'] = 'application/json'
282
- response
283
- end
284
56
  end
285
57
  end
286
- RUBY
287
- end
288
- end
289
-
290
- end
291
- end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PraxisGen
4
+ class Model < Thor
5
+ require 'active_support/inflector'
6
+ include Thor::Actions
7
+
8
+ def self.source_root
9
+ File.dirname(__FILE__) + "/templates/generator/scaffold"
10
+ end
11
+
12
+ desc "gmodel", "Generates a skeleton model file under app/models for ActiveRecord or Sequel."
13
+ argument :model_name, required: true
14
+ option :orm, required: false, default: 'activerecord', enum: ['activerecord','sequel']
15
+ def g
16
+ #self.class.check_name(model_name)
17
+ template_file = \
18
+ if options[:orm] == 'activerecord'
19
+ 'models/active_record.rb'
20
+ else
21
+ 'models/sequel.rb'
22
+ end
23
+ puts "Generating Model for #{model_name}"
24
+ template template_file, "app/models/#{model_name}.rb"
25
+ nil
26
+ end
27
+ # Helper functions (which are available in the ERB contexts)
28
+ no_commands do
29
+ def model_class
30
+ model_name.camelize
31
+ end
32
+ end
33
+
34
+ # TODO: do we want the argument to be camelcase? or snake case?
35
+ def self.check_name(name)
36
+ sanitized = name.downcase.gsub(/[^a-z0-9_]/, '')
37
+ raise "Please use only downcase letters, numbers and underscores for the model" unless sanitized == name
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PraxisGen
4
+ class Scaffold < Thor
5
+ require 'active_support/inflector'
6
+ include Thor::Actions
7
+
8
+ attr_reader :actions_hash
9
+
10
+ def self.source_root
11
+ File.dirname(__FILE__) + "/templates/generator/scaffold"
12
+ end
13
+
14
+ desc "g","Generates an API design and implementation scaffold for managing a collection of <collection_name>"
15
+ argument :collection_name, required: true
16
+ option :version, required: false, default: '1',
17
+ desc: 'Version string for the API endpoint. This also dictates the directory structure (i.e., v1/endpoints/...))'
18
+ option :design, type: :boolean, default: true,
19
+ desc: 'Include the Endpoint and MediaType files for the collection'
20
+ option :implementation, type: :boolean, default: true,
21
+ desc: 'Include the Controller and (possibly the) Resource files for the collection (see --no-resource)'
22
+ option :resource, type: :boolean, default: true,
23
+ desc: 'Disable (or enable) the creation of the Resource files when generating implementation'
24
+ option :model, type: :string, enum: ['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: ['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
+ def g
29
+ self.class.check_name(collection_name)
30
+ @actions_hash = self.class.compose_actions_hash(options[:actions])
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
+ if options[:design]
34
+ say_status 'Design', "Generating scaffold for #{plural_class}", :blue
35
+ template 'design/media_types/item.rb', "design/#{version_dir}/media_types/#{collection_name.singularize}.rb"
36
+ template 'design/endpoints/collection.rb', "design/#{version_dir}/endpoints/#{collection_name}.rb"
37
+ end
38
+ if options[:implementation]
39
+ say_status 'Implement', "Generating scaffold for #{plural_class}", :blue
40
+ if options[:resource]
41
+ base_resource = Pathname.new(destination_root)+Pathname.new("app/#{version_dir}/resources/base.rb")
42
+ unless base_resource.exist?
43
+ # Copy an appropriate base resource for the version (resources within same version must share same base)
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
+ template 'implementation/resources/base.rb', "app/#{version_dir}/resources/base.rb"
50
+ end
51
+ template 'implementation/resources/item.rb', "app/#{version_dir}/resources/#{collection_name.singularize}.rb"
52
+ end
53
+ template 'implementation/controllers/collection.rb', "app/#{version_dir}/controllers/#{collection_name}.rb"
54
+ end
55
+ nil
56
+ end
57
+
58
+ # Helper functions (which are available in the ERB contexts)
59
+ no_commands do
60
+ def plural_class
61
+ collection_name.camelize
62
+ end
63
+
64
+ def singular_class
65
+ collection_name.singularize.camelize
66
+ end
67
+
68
+ def version
69
+ options[:version]
70
+ end
71
+
72
+ def version_module
73
+ "V#{version}"
74
+ end
75
+
76
+ def version_dir
77
+ version_module.camelize(:lower)
78
+ end
79
+
80
+ def action_enabled?(action)
81
+ @actions_hash[action.to_sym]
82
+ end
83
+
84
+ def pagination_enabled?
85
+ @pagination_plugin_found
86
+ end
87
+ end
88
+
89
+ def self.compose_actions_hash(actions_opt)
90
+ required = { index: true, show: true }
91
+ case actions_opt
92
+ when nil
93
+ required
94
+ when 'cr'
95
+ required.merge(create: true)
96
+ when 'cru'
97
+ required.merge(create: true, update: true)
98
+ when 'crud'
99
+ required.merge(create: true, update: true, delete: true)
100
+ when 'u'
101
+ required.merge(update: true)
102
+ when 'ud'
103
+ required.merge(update: true, delete: true)
104
+ when 'd'
105
+ required.merge(delete: true)
106
+ else
107
+ raise "actions option does not support the string #{actions_opt}"
108
+ end
109
+ end
110
+
111
+ def self.check_name(name)
112
+ sanitized = name.downcase.gsub(/[^a-z0-9_]/, '')
113
+ # TODO: bail or support CamelCase collections (for now only snake case)
114
+ raise "Please use only downcase letters, numbers and underscores for the collection" unless sanitized == name
115
+ end
116
+ end
117
+ end