praxis 0.10.1 → 0.11pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/CHANGELOG.md +47 -10
  4. data/Gemfile +1 -1
  5. data/Guardfile +1 -0
  6. data/bin/praxis +33 -4
  7. data/lib/api_browser/app/css/main.css +0 -3
  8. data/lib/praxis.rb +16 -0
  9. data/lib/praxis/action_definition.rb +16 -18
  10. data/lib/praxis/application.rb +31 -2
  11. data/lib/praxis/bootloader.rb +37 -4
  12. data/lib/praxis/bootloader_stages/environment.rb +3 -7
  13. data/lib/praxis/bootloader_stages/plugin_config_load.rb +20 -0
  14. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +18 -0
  15. data/lib/praxis/bootloader_stages/plugin_loader.rb +19 -0
  16. data/lib/praxis/bootloader_stages/plugin_setup.rb +13 -0
  17. data/lib/praxis/bootloader_stages/routing.rb +16 -6
  18. data/lib/praxis/callbacks.rb +0 -2
  19. data/lib/praxis/config.rb +3 -2
  20. data/lib/praxis/dispatcher.rb +25 -13
  21. data/lib/praxis/error_handler.rb +16 -0
  22. data/lib/praxis/links.rb +9 -4
  23. data/lib/praxis/media_type_collection.rb +2 -3
  24. data/lib/praxis/notifications.rb +41 -0
  25. data/lib/praxis/plugin.rb +18 -8
  26. data/lib/praxis/plugin_concern.rb +40 -0
  27. data/lib/praxis/request.rb +27 -7
  28. data/lib/praxis/request_stages/action.rb +7 -2
  29. data/lib/praxis/request_stages/response.rb +7 -3
  30. data/lib/praxis/request_stages/validate_payload.rb +7 -1
  31. data/lib/praxis/resource_definition.rb +37 -16
  32. data/lib/praxis/response.rb +1 -0
  33. data/lib/praxis/responses/internal_server_error.rb +13 -8
  34. data/lib/praxis/responses/validation_error.rb +10 -7
  35. data/lib/praxis/restful_doc_generator.rb +312 -0
  36. data/lib/praxis/router.rb +7 -5
  37. data/lib/praxis/skeletor/restful_routing_config.rb +12 -5
  38. data/lib/praxis/stage.rb +5 -1
  39. data/lib/praxis/stats.rb +106 -0
  40. data/lib/praxis/tasks/api_docs.rb +8 -314
  41. data/lib/praxis/version.rb +1 -1
  42. data/praxis.gemspec +4 -1
  43. data/spec/functional_spec.rb +87 -32
  44. data/spec/praxis/action_definition_spec.rb +13 -12
  45. data/spec/praxis/bootloader_spec.rb +12 -5
  46. data/spec/praxis/notifications_spec.rb +23 -0
  47. data/spec/praxis/plugin_concern_spec.rb +21 -0
  48. data/spec/praxis/request_spec.rb +56 -1
  49. data/spec/praxis/request_stages_validate_spec.rb +3 -3
  50. data/spec/praxis/resource_definition_spec.rb +44 -60
  51. data/spec/praxis/responses/internal_server_error_spec.rb +32 -16
  52. data/spec/praxis/restful_routing_config_spec.rb +15 -2
  53. data/spec/praxis/router_spec.rb +5 -3
  54. data/spec/praxis/stats_spec.rb +9 -0
  55. data/spec/praxis_mapper_plugin_spec.rb +71 -0
  56. data/spec/spec_app/app/controllers/instances.rb +12 -0
  57. data/spec/spec_app/app/controllers/volumes.rb +5 -0
  58. data/spec/spec_app/app/models/person.rb +3 -0
  59. data/spec/spec_app/config/active_record.yml +2 -0
  60. data/spec/spec_app/config/authentication.yml +3 -0
  61. data/spec/spec_app/config/authorization.yml +4 -0
  62. data/spec/spec_app/config/environment.rb +28 -1
  63. data/spec/spec_app/config/praxis_mapper.yml +6 -0
  64. data/spec/spec_app/config/sequel_model.yml +2 -0
  65. data/spec/spec_app/config/stats.yml +8 -0
  66. data/spec/spec_app/config/stats.yml.dis +8 -0
  67. data/spec/spec_app/design/resources/instances.rb +53 -16
  68. data/spec/spec_app/design/resources/volumes.rb +13 -2
  69. data/spec/spec_helper.rb +14 -0
  70. data/spec/support/spec_authentication_plugin.rb +126 -0
  71. data/spec/support/spec_authorization_plugin.rb +95 -0
  72. data/spec/support/spec_praxis_mapper_plugin.rb +157 -0
  73. data/tasks/loader.thor +6 -0
  74. data/tasks/thor/app.rb +48 -0
  75. data/tasks/thor/example.rb +283 -0
  76. data/tasks/thor/templates/generator/empty_app/.gitignore +3 -0
  77. data/tasks/thor/templates/generator/empty_app/.rspec +1 -0
  78. data/tasks/thor/templates/generator/empty_app/Gemfile +29 -0
  79. data/tasks/thor/templates/generator/empty_app/Guardfile +3 -0
  80. data/tasks/thor/templates/generator/empty_app/README.md +4 -0
  81. data/tasks/thor/templates/generator/empty_app/Rakefile +25 -0
  82. data/tasks/thor/templates/generator/empty_app/app/models/.empty_directory +0 -0
  83. data/tasks/thor/templates/generator/empty_app/app/models/.gitkeep +0 -0
  84. data/tasks/thor/templates/generator/empty_app/app/responses/.empty_directory +0 -0
  85. data/tasks/thor/templates/generator/empty_app/app/responses/.gitkeep +0 -0
  86. data/tasks/thor/templates/generator/empty_app/app/v1/controllers/.empty_directory +0 -0
  87. data/tasks/thor/templates/generator/empty_app/app/v1/controllers/.gitkeep +0 -0
  88. data/tasks/thor/templates/generator/empty_app/config.ru +7 -0
  89. data/tasks/thor/templates/generator/empty_app/config/environment.rb +17 -0
  90. data/tasks/thor/templates/generator/empty_app/config/rainbows.rb +57 -0
  91. data/tasks/thor/templates/generator/empty_app/design/api.rb +0 -0
  92. data/tasks/thor/templates/generator/empty_app/design/response_templates/.empty_directory +0 -0
  93. data/tasks/thor/templates/generator/empty_app/design/response_templates/.gitkeep +0 -0
  94. data/tasks/thor/templates/generator/empty_app/design/v1/media_types/.empty_directory +0 -0
  95. data/tasks/thor/templates/generator/empty_app/design/v1/media_types/.gitkeep +0 -0
  96. data/tasks/thor/templates/generator/empty_app/design/v1/resources/.empty_directory +0 -0
  97. data/tasks/thor/templates/generator/empty_app/design/v1/resources/.gitkeep +0 -0
  98. data/tasks/thor/templates/generator/empty_app/spec/spec_helper.rb +18 -0
  99. metadata +97 -6
  100. data/tasks/praxis_app_generator.thor +0 -307
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb59e46b853495a60984f953e66b32484735bbe2
4
- data.tar.gz: 70a1dac56fae430f1ef83fca9879d6f1e2e85227
3
+ metadata.gz: 5925b03e5020e28fc79530a6991c3f030897996d
4
+ data.tar.gz: f301a81c0628df629ae32e805debbb653b0c2477
5
5
  SHA512:
6
- metadata.gz: faa1f7c49da063995145e55ccfefd4f74687b4522f35613783aae4990b88cf82ad8c6da79192f12d43eda39c4057c8f387435513d8ff47b837a04a3685081422
7
- data.tar.gz: aa5c73efd69e49cdde0129c7d5afcb245975b631a90313c5eb2240d50c5727c673a31a5cda3452e62e6a7e7632ba0734580ddb21c09fffc65c18fece9a8e28f9
6
+ metadata.gz: f573ece40063959ee3df1a11db4a36563acd44c68bc7ad70192f90bb8268501b4c128e504ba8080406037303a6fa5dbbf4a854d741843c80aae559df6b2a0f63
7
+ data.tar.gz: 3adb04192e7414e607482a44f2a8f9f83618dee6b0202094ee77c77d4d46ae0dd63e5579dd55e87ed76aa6212a0a294229108d8c5bd8d8bd841de417cf73fb63
data/.gitignore CHANGED
@@ -8,4 +8,3 @@ _site/
8
8
  .tmp/
9
9
  node_modules/
10
10
  *.css.map
11
-
data/CHANGELOG.md CHANGED
@@ -1,8 +1,49 @@
1
1
  # praxis changelog
2
2
 
3
- ## next
3
+ ## 0.11
4
4
 
5
- * next thing here
5
+ * `MediaTypeCollection`:
6
+ * Added support for loading `decorate`ed `Resource` associations.
7
+ * Refined and enhanced support for API versioning:
8
+ * version DSL now can take a `using` option which specifies and array of the methods are allowed: `:header`,`:params`,`:path`(new)
9
+ * if not specified, it will default to `using: [:header, :params]` (so that the version can be passed to the header OR the params)
10
+ * the new `:path` option will build the action routes by prefixing the version given a common pattern (i.e., "/v1.0/...")
11
+ * The effects of path versioning will be visible through `rake praxis:routes`
12
+ * the default api prefix pattern is ("/v(version)/") but can changed by either
13
+ * overriding ``Praxis::Request.path_version_prefix` and return the appropriate string prefix (i.e., by default this returns "/v")
14
+ * or overriding `Praxis::Request.path_version_matcher` and providing the fully custom matching regexp. This regexp must have a capture (named `version`) that would return matched version value.
15
+ * Enhanced praxis generator:
16
+ * Added a new generator (available through `praxis new app_name`) which creates a blank new app, with enough basic structure and setup to start building an API.
17
+ * Changed the example hello world generation command. Instead of `praxis generate app_name`, it is now available through `praxis example app_name`
18
+ * Changed the path lookup for the praxis directory (to not use installed gems, which could be multiple). [Issue #67]
19
+ * `ResourceDefinition`:
20
+ * Added: `action_defaults` method, to define default options for actions. May be called more than once.
21
+ * Removed: `params`, `payload`, `headers`, and `response`. Specify these inside `action_defaults` instead.
22
+ * `Application`:
23
+ * Added `middleware` method to use Rack middleware.
24
+ * `ErrorHandler`
25
+ * It is now possible to register the error handler class to be invoked when an uncaught exception is thrown by setting `Application#error_handler`.
26
+ * The default error handler writes the error and backtrace into the Praxis logger, and returns an `InternalServerError` response
27
+ * Added `Praxis::Notifications` framework backed by ActiveSupport::Notifications
28
+ * Its interface is the same as AS::Notifications (.publish, .instrument, .subscribe, and etc.)
29
+ * Each incoming rack request is instrumented as `rack.request.all`, with a payload of `{response: response}`, where `response` is the `Response` object that will be returned to the client. Internally, Praxis subscribes to this to generate timing statistics with `Praxis::Stats`.
30
+ * Additionally, each request that is dispatched to an action is instrumented as `praxis.request.all`, with a payload of `{request: request, response: response}`, where `response` is as above, and `request` is the `Request` object for the request.
31
+ * Added `Praxis::Stats` framework backed by `Harness` (i.e. a statsd interface)
32
+ * Can be configured with a collector type (fake, Statsd) and an asynchronous queue + thread
33
+ * Wraps the statsd interface: count, increment, decrement, time ...
34
+ * Added a new `decorate_docs` method to enhance generated JSON docs for actions in `ResourceDefinitions`
35
+ * Using this hook, anybody can register a block that can change/enhance the JSON structure of generated documents for any given action
36
+ * Added a brand new Plugins architecture
37
+ * Plugins can easily inject code in the Request, Controller, ResourceDefinition or ActionDefinition
38
+ * Can be instances or singletons (and will be initialized correspondingly)
39
+ * Plugins can be easily configured under a unique "config key" in the Praxis config
40
+ * See the [Plugins](http://praxis-framework.io/reference/plugins/) section in the documentation for more information.
41
+ * Added a Plugin for using the Praxis::Mapper gem
42
+ * Configurable through a simple `praxis_mapper.yml` file
43
+ * Its supports several repositories (by name)
44
+ * Each repository can be of a different type (default is sequel)
45
+ * `praxis:doc_browser` rake task now takes a port argument for specifying the port to run on, e.g. `rake praxis:doc_browser[9000]` to run on port 9000.
46
+ * Added `show_exceptions` configuration option to to control default ErrorHandler behavior.
6
47
 
7
48
  ## 0.10.0
8
49
 
@@ -22,7 +63,7 @@
22
63
  * Fixes [Issue #21](https://github.com/rightscale/praxis/issues/21)
23
64
  * Introduced `around` filters using blocks:
24
65
  * Around filters can be set wrapping any of the request stages (load, validate, action...) and might apply to only certain actions (i.e. exactly the same as the before/after filters)
25
- * Therefore they supports the same attributes as `before` and `after` filters. The only difference is that an around filter block will get an extra parameter with the block to call to continue the chain.
66
+ * Therefore they supports the same attributes as `before` and `after` filters. The only difference is that an around filter block will get an extra parameter with the block to call to continue the chain.
26
67
  * See the [Instances](https://github.com/rightscale/praxis/blob/master/spec/spec_app/app/controllers/instances.rb) controller for examples.
27
68
  * Fix: Change :created response template to take an optiona ‘location’ parameter (instead of a media_type one, since it doesn’t make sense for a 201 type response) [Issue #26](https://github.com/rightscale/praxis/issues/23)
28
69
  * Make the system be more robust in error reporting when controllers do not return a String or a Response
@@ -41,11 +82,7 @@
41
82
  * `MediaTypeCollection`. See [volume_snapshot](spec/spec_app/design/media_types/volume_snapshot.rb) in the specs for an example.
42
83
  * Added `member_view` DSL to define a view that renders the collection's members with the given view.
43
84
  * Change: Now requires all views to be explicitly defined (and will not automatically use the underlying member view if it exists). To define a view for member element (wrapping it in a collection) one can use the new member_view.
44
- * Application.setup now returns the application instance so it can be chained to other application methods.
45
- * This allows a more compact way to rackup the application: `run Praxis::Application.instance.setup`.
46
- * Request headers are now backed by `Attributor::Hash` rather than (Structs)
47
- ** Header keys are case sensitive now (although Praxis gracefully allows loading the uppercased RACK names )
48
- * Added tests for using `Attributor::Hash` attributes that can accept "extra" keys:
49
- ** See the `bulk_create` action for an example applied to `Praxis::Multipart` (which derives from `Attributor::Hash`) payload [here](https://github.com/rightscale/praxis/blob/master/spec/spec_app/design/resources/instances.rb).
50
-
85
+ *
86
+
87
+
51
88
  ## 0.9 Initial release
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
data/Guardfile CHANGED
@@ -5,4 +5,5 @@ guard 'rspec', cmd: 'bundle exec rspec' do
5
5
  watch('spec/functional_spec.rb') { "spec/" }
6
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/functional_spec.rb" }
7
7
  watch(%r{^app/(.+)\.rb$}) { "spec/" }
8
+ watch(%r{^spec/support}) { 'spec/' }
8
9
  end
data/bin/praxis CHANGED
@@ -2,9 +2,38 @@
2
2
 
3
3
  require 'bundler'
4
4
 
5
- path_to_praxis = Bundler.rubygems.find_name('praxis').first.full_gem_path
6
- path_to_generator = '%s/tasks/praxis_app_generator.thor' % path_to_praxis
5
+ path_to_praxis = File.expand_path(File.dirname(File.dirname(__FILE__)))
6
+ path_to_loader = '%s/tasks/loader.thor' % path_to_praxis
7
7
 
8
- load path_to_generator
8
+ load path_to_loader
9
9
 
10
- PraxisAppGenerator.start(ARGV)
10
+ class PraxisGenerator < Thor
11
+
12
+ # Simple helper to go get the existing description for the real action
13
+ # Usage must still be provided rather than retrieved (since it is not a
14
+ # straight "usage" from the remote action when arguments are defined )
15
+ def self.desc_for( usage_string, klass, action_name, description_prefix="")
16
+ action_name = action_name.to_s
17
+ cmd = klass.commands[action_name]
18
+ raise "Error, could not find description for #{klass.name}##{action_name}" if cmd.nil?
19
+ desc usage_string, "#{description_prefix}#{cmd.description}"
20
+ end
21
+
22
+ desc_for "new APP_NAME", ::PraxisGen::App, :new
23
+ def new(app_name)
24
+ ::PraxisGen::App.start(['new' , app_name])
25
+ end
26
+
27
+ desc_for "example APP_NAME", ::PraxisGen::Example, :new
28
+ def example(app_name)
29
+ ::PraxisGen::Example.start(['new', app_name])
30
+ end
31
+
32
+ desc_for "generate APP_NAME", ::PraxisGen::Example, :new, "DEPRECATED!: "
33
+ def generate(app_name)
34
+ warn "This is a deprecated method.\nTo generate a hello world example, please use:\n praxis example #{app_name} "
35
+ end
36
+ end
37
+
38
+
39
+ PraxisGenerator.start(ARGV)
@@ -4111,9 +4111,6 @@ button.close {
4111
4111
  display: none !important;
4112
4112
  visibility: hidden !important; }
4113
4113
 
4114
- .affix {
4115
- position: fixed; }
4116
-
4117
4114
  @-ms-viewport {
4118
4115
  width: device-width; }
4119
4116
 
data/lib/praxis.rb CHANGED
@@ -3,6 +3,8 @@ require 'attributor'
3
3
  require 'praxis-mapper'
4
4
  require 'praxis-blueprints'
5
5
 
6
+ require 'active_support/concern'
7
+
6
8
  $:.unshift File.dirname(__FILE__)
7
9
 
8
10
  module Attributor
@@ -24,6 +26,7 @@ module MIME
24
26
  end
25
27
 
26
28
  module Praxis
29
+
27
30
  autoload :ActionDefinition, 'praxis/action_definition'
28
31
  autoload :ApiDefinition, 'praxis/api_definition'
29
32
  autoload :Application, 'praxis/application'
@@ -32,9 +35,11 @@ module Praxis
32
35
  autoload :Controller, 'praxis/controller'
33
36
  autoload :Callbacks, 'praxis/callbacks'
34
37
  autoload :Dispatcher, 'praxis/dispatcher'
38
+ autoload :ErrorHandler, 'praxis/error_handler'
35
39
  autoload :Exception, 'praxis/exception'
36
40
  autoload :FileGroup,'praxis/file_group'
37
41
  autoload :Plugin, 'praxis/plugin'
42
+ autoload :PluginConcern, 'praxis/plugin_concern'
38
43
  autoload :Request, 'praxis/request'
39
44
  autoload :ResourceDefinition, 'praxis/resource_definition'
40
45
  autoload :Response, 'praxis/response'
@@ -46,6 +51,11 @@ module Praxis
46
51
  autoload :Stage, 'praxis/stage'
47
52
  autoload :ContentTypeParser, 'praxis/content_type_parser'
48
53
 
54
+ autoload :Stats, 'praxis/stats'
55
+ autoload :Notifications, 'praxis/notifications'
56
+
57
+ autoload :RestfulDocGenerator, 'praxis/restful_doc_generator'
58
+
49
59
  # types
50
60
  autoload :Links, 'praxis/links'
51
61
  autoload :MediaType, 'praxis/media_type'
@@ -79,6 +89,12 @@ module Praxis
79
89
  module BootloaderStages
80
90
  autoload :FileLoader, 'praxis/bootloader_stages/file_loader'
81
91
  autoload :Environment, 'praxis/bootloader_stages/environment'
92
+
93
+ autoload :PluginLoader, 'praxis/bootloader_stages/plugin_loader'
94
+ autoload :PluginConfigPrepare, 'praxis/bootloader_stages/plugin_config_prepare'
95
+ autoload :PluginConfigLoad, 'praxis/bootloader_stages/plugin_config_load'
96
+ autoload :PluginSetup, 'praxis/bootloader_stages/plugin_setup'
97
+
82
98
  autoload :SubgroupLoader, 'praxis/bootloader_stages/subgroup_loader'
83
99
  autoload :WarnUnloadedFiles, 'praxis/bootloader_stages/warn_unloaded_files'
84
100
  autoload :Routing, 'praxis/bootloader_stages/routing'
@@ -16,6 +16,16 @@ module Praxis
16
16
  attr_reader :named_routes
17
17
  attr_reader :responses
18
18
 
19
+ class << self
20
+ attr_accessor :doc_decorations
21
+ end
22
+
23
+ @doc_decorations = []
24
+
25
+ def self.decorate_docs(&callback)
26
+ self.doc_decorations << callback
27
+ end
28
+
19
29
  def initialize(name, resource_definition, **opts, &block)
20
30
  @name = name
21
31
  @resource_definition = resource_definition
@@ -27,23 +37,8 @@ module Praxis
27
37
  end
28
38
  end
29
39
 
30
- if resource_definition.params
31
- saved_type, saved_opts, saved_block = resource_definition.params
32
- params(saved_type, saved_opts, &saved_block)
33
- end
34
-
35
- if resource_definition.payload
36
- saved_type, saved_opts, saved_block = resource_definition.payload
37
- payload(saved_type, saved_opts, &saved_block)
38
- end
39
-
40
- if resource_definition.headers
41
- saved_opts, saved_block = resource_definition.headers
42
- headers(saved_opts, &saved_block)
43
- end
44
-
45
- resource_definition.responses.each do |name, args|
46
- response(name, args)
40
+ resource_definition.action_defaults.each do |defaults|
41
+ self.instance_eval(&defaults)
47
42
  end
48
43
 
49
44
  self.instance_eval(&block) if block_given?
@@ -141,13 +136,13 @@ module Praxis
141
136
  end
142
137
  end
143
138
 
139
+
144
140
  def description(text = nil)
145
141
  @description = text if text
146
142
  @description
147
143
  end
148
144
 
149
145
 
150
-
151
146
  def describe
152
147
  {}.tap do |hash|
153
148
  hash[:description] = description
@@ -161,6 +156,9 @@ module Praxis
161
156
  memo[response.name] = response.describe
162
157
  memo
163
158
  end
159
+ self.class.doc_decorations.each do |callback|
160
+ callback.call(self, hash)
161
+ end
164
162
  end
165
163
  end
166
164
 
@@ -9,6 +9,8 @@ module Praxis
9
9
  attr_reader :router
10
10
  attr_reader :controllers
11
11
  attr_reader :resource_definitions
12
+ attr_reader :app
13
+ attr_reader :builder
12
14
 
13
15
  attr_accessor :bootloader
14
16
  attr_accessor :file_layout
@@ -16,6 +18,8 @@ module Praxis
16
18
  attr_accessor :logger
17
19
  attr_accessor :plugins
18
20
  attr_accessor :root
21
+ attr_accessor :error_handler
22
+
19
23
 
20
24
  def self.configure
21
25
  yield(self.instance)
@@ -25,11 +29,16 @@ module Praxis
25
29
  @controllers = Set.new
26
30
  @resource_definitions = Set.new
27
31
 
32
+ @error_handler = ErrorHandler.new
33
+
28
34
  @router = Router.new(self)
29
35
 
36
+ @builder = Rack::Builder.new
37
+ @app = nil
38
+
30
39
  @bootloader = Bootloader.new(self)
31
40
  @file_layout = nil
32
- @plugins = Array.new
41
+ @plugins = Hash.new
33
42
  @loaded_files = Set.new
34
43
  @config = Config.new
35
44
  @root = nil
@@ -40,11 +49,30 @@ module Praxis
40
49
  @root = Pathname.new(root).expand_path
41
50
 
42
51
  @bootloader.setup!
52
+
53
+ @builder.run(@router)
54
+ @app = @builder.to_app
55
+
56
+ Notifications.subscribe 'rack.request.all'.freeze do |name, start, finish, _id, payload|
57
+ duration = (finish - start) * 1000
58
+ Stats.timing(name, duration)
59
+
60
+ status, _, _ = payload[:response]
61
+ Stats.increment "rack.request.#{status}"
62
+ end
63
+
43
64
  self
44
65
  end
45
66
 
67
+ def middleware(middleware, *args, &block)
68
+ @builder.use(middleware, *args, &block)
69
+ end
70
+
46
71
  def call(env)
47
- self.router.call(env)
72
+ response = []
73
+ Notifications.instrument 'rack.request.all'.freeze, response: response do
74
+ response.push(*@app.call(env))
75
+ end
48
76
  end
49
77
 
50
78
  def layout(&block)
@@ -62,5 +90,6 @@ module Praxis
62
90
  def config=(config)
63
91
  @config.set(config)
64
92
  end
93
+
65
94
  end
66
95
  end
@@ -1,9 +1,9 @@
1
1
  module Praxis
2
2
 
3
-
4
3
  class Bootloader
5
4
 
6
- attr_reader :application, :stages
5
+ attr_reader :application
6
+ attr_reader :stages
7
7
 
8
8
  def initialize(application)
9
9
  @application = application
@@ -24,6 +24,9 @@ module Praxis
24
24
  # Require environment first. they define constants and environment specific settings
25
25
  stages << BootloaderStages::Environment.new(:environment, application)
26
26
 
27
+ # then setup plugins
28
+ stages << BootloaderStages::PluginLoader.new(:plugins, application)
29
+
27
30
  # then the initializers. as it is their job to ensure monkey patches and other
28
31
  # config is in place first.
29
32
  stages << BootloaderStages::FileLoader.new(:initializers, application)
@@ -71,11 +74,41 @@ module Praxis
71
74
  stages.find { |stage| stage.name == stage_name }.after(*stage_path, &block)
72
75
  end
73
76
 
74
- def use(plugin,&block)
75
- application.plugins << plugin.new(application, &block)
77
+ def use(plugin,**options, &block)
78
+ if plugin.ancestors.include?(PluginConcern)
79
+ plugin.setup!
80
+ plugin = plugin::Plugin
81
+ end
82
+
83
+ instance = if plugin.ancestors.include?(Singleton)
84
+ plugin.instance
85
+ elsif plugin.kind_of?(Class)
86
+ plugin.new
87
+ else
88
+ plugin
89
+ end
90
+
91
+ instance.application = application
92
+ instance.options.merge!(options)
93
+ instance.block = block if block_given?
94
+
95
+ if application.plugins.key?(instance.config_key)
96
+ raise "Can not use plugin: #{plugin}, another plugin is already registered with key: #{instance.config_key}"
97
+ end
98
+
99
+ if instance.config_key.nil?
100
+ raise "Error initializing plugin: #{plugin}, config_key may not be nil."
101
+ end
102
+
103
+ application.plugins[instance.config_key] = instance
104
+ instance
76
105
  end
77
106
 
78
107
  def setup!
108
+ # use the Stats and Notifications plugins by default
109
+ use Praxis::Stats
110
+ use Praxis::Notifications
111
+
79
112
  run
80
113
  end
81
114
 
@@ -1,6 +1,5 @@
1
1
  module Praxis
2
2
 
3
-
4
3
  module BootloaderStages
5
4
 
6
5
  class Environment < Stage
@@ -9,18 +8,14 @@ module Praxis
9
8
  # 1) the environment.rb file - generic stuff for all environments
10
9
  # 2) "Deployer.environment".rb - environment specific stuff
11
10
  def execute
11
+ setup_initial_config!
12
+
12
13
  env_file = application.root + "config/environment.rb"
13
14
  require env_file if File.exists? env_file
14
15
 
15
- application.plugins.each do |plugin|
16
- plugin.setup!
17
- end
18
-
19
16
  unless application.file_layout
20
17
  setup_default_layout!
21
18
  end
22
-
23
- setup_initial_config!
24
19
  end
25
20
 
26
21
  def setup_default_layout!
@@ -47,6 +42,7 @@ module Praxis
47
42
  application.config do
48
43
  attribute :praxis do
49
44
  attribute :validate_responses, Attributor::Boolean, default: false
45
+ attribute :show_exceptions, Attributor::Boolean, default: false
50
46
  end
51
47
  end
52
48
  end