praxis 0.21 → 0.22.pre.1
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 +5 -5
- data/.travis.yml +20 -12
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +4 -4
- data/README.md +11 -9
- data/lib/api_browser/app/js/directives/attribute_table.js +2 -1
- data/lib/api_browser/app/js/directives/conditional_requirements.js +13 -0
- data/lib/api_browser/app/js/directives/type_placeholder.js +10 -1
- data/lib/api_browser/app/js/factories/normalize_attributes.js +4 -2
- data/lib/api_browser/app/js/factories/template_for.js +5 -2
- data/lib/api_browser/app/js/filters/has_requirement.js +14 -0
- data/lib/api_browser/app/js/filters/tag_requirement.js +13 -0
- data/lib/api_browser/app/sass/praxis.scss +11 -0
- data/lib/api_browser/app/views/action.html +2 -2
- data/lib/api_browser/app/views/directives/attribute_description/member_options.html +2 -2
- data/lib/api_browser/app/views/directives/attribute_table.html +1 -1
- data/lib/api_browser/app/views/type.html +1 -1
- data/lib/api_browser/app/views/type/details.html +2 -2
- data/lib/api_browser/app/views/types/embedded/array.html +2 -0
- data/lib/api_browser/app/views/types/embedded/default.html +3 -1
- data/lib/api_browser/app/views/types/embedded/requirements.html +6 -0
- data/lib/api_browser/app/views/types/embedded/single_req.html +9 -0
- data/lib/api_browser/app/views/types/embedded/struct.html +14 -2
- data/lib/api_browser/app/views/types/standalone/array.html +1 -1
- data/lib/api_browser/app/views/types/standalone/struct.html +2 -1
- data/lib/api_browser/package.json +1 -1
- data/lib/praxis.rb +8 -6
- data/lib/praxis/action_definition.rb +9 -7
- data/lib/praxis/api_definition.rb +44 -27
- data/lib/praxis/api_general_info.rb +3 -2
- data/lib/praxis/application.rb +139 -20
- data/lib/praxis/bootloader.rb +2 -4
- data/lib/praxis/bootloader_stages/environment.rb +0 -13
- data/lib/praxis/controller.rb +2 -0
- data/lib/praxis/dispatcher.rb +16 -10
- data/lib/praxis/docs/generator.rb +20 -9
- data/lib/praxis/docs/link_builder.rb +1 -1
- data/lib/praxis/error_handler.rb +5 -5
- data/lib/praxis/extensions/attribute_filtering.rb +28 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +180 -0
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +273 -0
- data/lib/praxis/extensions/attribute_filtering/query_builder.rb +39 -0
- data/lib/praxis/extensions/field_selection.rb +3 -0
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +57 -0
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +65 -0
- data/lib/praxis/extensions/rails_compat.rb +2 -0
- data/lib/praxis/extensions/rails_compat/request_methods.rb +19 -0
- data/lib/praxis/extensions/rendering.rb +1 -1
- data/lib/praxis/file_group.rb +1 -1
- data/lib/praxis/middleware_app.rb +26 -6
- data/lib/praxis/multipart/parser.rb +14 -2
- data/lib/praxis/multipart/part.rb +5 -3
- data/lib/praxis/plugins/praxis_mapper_plugin.rb +2 -2
- data/lib/praxis/plugins/rails_plugin.rb +104 -0
- data/lib/praxis/request.rb +8 -9
- data/lib/praxis/request_stages/response.rb +3 -2
- data/lib/praxis/request_superclassing.rb +11 -0
- data/lib/praxis/resource_definition.rb +14 -10
- data/lib/praxis/response.rb +6 -7
- data/lib/praxis/response_definition.rb +7 -5
- data/lib/praxis/response_template.rb +4 -3
- data/lib/praxis/responses/http.rb +0 -36
- data/lib/praxis/responses/internal_server_error.rb +3 -12
- data/lib/praxis/responses/multipart_ok.rb +4 -11
- data/lib/praxis/responses/validation_error.rb +1 -10
- data/lib/praxis/router.rb +3 -3
- data/lib/praxis/tasks/api_docs.rb +10 -2
- data/lib/praxis/tasks/routes.rb +1 -0
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +4 -5
- data/spec/functional_spec.rb +4 -6
- data/spec/praxis/action_definition_spec.rb +26 -15
- data/spec/praxis/api_definition_spec.rb +13 -8
- data/spec/praxis/api_general_info_spec.rb +3 -8
- data/spec/praxis/application_spec.rb +13 -7
- data/spec/praxis/middleware_app_spec.rb +24 -10
- data/spec/praxis/request_spec.rb +17 -7
- data/spec/praxis/request_stages/validate_spec.rb +1 -1
- data/spec/praxis/resource_definition_spec.rb +12 -10
- data/spec/praxis/response_definition_spec.rb +22 -5
- data/spec/praxis/response_spec.rb +12 -5
- data/spec/praxis/responses/internal_server_error_spec.rb +4 -7
- data/spec/praxis/responses/validation_error_spec.rb +2 -2
- data/spec/praxis/router_spec.rb +8 -4
- data/spec/spec_app/config.ru +1 -6
- data/spec/spec_helper.rb +3 -3
- data/tasks/thor/templates/generator/empty_app/Gemfile +3 -3
- metadata +36 -32
- data/.ruby-version +0 -1
- data/lib/praxis/stats.rb +0 -113
- data/spec/praxis/stats_spec.rb +0 -9
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative 'active_record_filter_query_builder'
|
|
3
|
+
|
|
4
|
+
module Praxis
|
|
5
|
+
module Extensions
|
|
6
|
+
module QueryBuilder
|
|
7
|
+
# To include in a resource object...
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# TODO: this shouldn't be needed if we incorporate it with the properties of the mapper...
|
|
12
|
+
def self.filters_mapping(hash)
|
|
13
|
+
@query_builder_class = ActiveRecordFilterQueryBuilder.for(**hash)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.query_builder_class
|
|
17
|
+
@query_builder_class
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.craft_query(base_query, filters) # rubocop:disable Metrics/AbcSize
|
|
21
|
+
# Assume QueryBuilder
|
|
22
|
+
if query_builder_class
|
|
23
|
+
unless query_builder_class.ancestors.include?(ActiveRecordFilterQueryBuilder)
|
|
24
|
+
raise ArgumentError, ':query_builder_class must a class extending FilterQueryBuilder'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if filters && query_builder_class
|
|
28
|
+
base_query = query_builder_class.new(query: base_query, model: model ).build_clause(filters)
|
|
29
|
+
end
|
|
30
|
+
# puts "FILTERS_QUERY: #{filters_query.sql}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
base_query
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
require 'attributor/extras/field_selector'
|
|
2
2
|
|
|
3
3
|
require 'praxis/extensions/field_selection/field_selector'
|
|
4
|
+
# TODO: we should conditionally require it based on what ORM/s we want...
|
|
5
|
+
require 'praxis/extensions/field_selection/active_record_query_selector'
|
|
6
|
+
|
|
4
7
|
|
|
5
8
|
module Praxis
|
|
6
9
|
module Extensions
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Praxis
|
|
3
|
+
module Extensions
|
|
4
|
+
module FieldSelection
|
|
5
|
+
class ActiveRecordQuerySelector
|
|
6
|
+
attr_reader :selector, :ds, :top_model, :resolved, :root
|
|
7
|
+
# Gets a dataset, a selector...and should return a dataset with the selector definition applied.
|
|
8
|
+
def initialize(ds:, model:, selectors:, resolved:)
|
|
9
|
+
@selector = selectors
|
|
10
|
+
@ds = ds
|
|
11
|
+
@top_model = model
|
|
12
|
+
@resolved = resolved
|
|
13
|
+
@seen = Set.new
|
|
14
|
+
@root = model.table_name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_select(ds:, model:, table_name:)
|
|
18
|
+
if (fields = fields_for(model))
|
|
19
|
+
# Note, let's always add the pk fields so that associations can load properly
|
|
20
|
+
fields = fields | [model.primary_key.to_sym]
|
|
21
|
+
ds.select(*fields)
|
|
22
|
+
else
|
|
23
|
+
ds
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def generate
|
|
28
|
+
# TODO: unfortunately, I think we can only control the select clauses for the top model
|
|
29
|
+
# (as I'm not sure ActiveRecord supports expressing it in the join...)
|
|
30
|
+
@ds = add_select(ds: ds, model: top_model, table_name: root)
|
|
31
|
+
|
|
32
|
+
@ds.includes(_eager(top_model, resolved) )
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def _eager(model, resolved)
|
|
36
|
+
# Cannot select fields in included rels...boooo :()
|
|
37
|
+
# d = add_select(ds: dset, model: model, table_name: model.table_name)
|
|
38
|
+
tracks = only_assoc_for(model, resolved)
|
|
39
|
+
tracks.inject([]) do |dataset, track|
|
|
40
|
+
next dataset if @seen.include?([model, track])
|
|
41
|
+
@seen << [model, track]
|
|
42
|
+
assoc_model = model.associations[track][:model]
|
|
43
|
+
dataset << { track => _eager(assoc_model, resolved[track]) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def only_assoc_for(model, hash)
|
|
48
|
+
hash.keys.reject { |assoc| model.associations[assoc].nil? }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def fields_for(model)
|
|
52
|
+
selector[model][:select].to_a
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Praxis
|
|
3
|
+
module Extensions
|
|
4
|
+
module FieldSelection
|
|
5
|
+
class SequelQuerySelector
|
|
6
|
+
attr_reader :selector, :ds, :top_model, :resolved, :root
|
|
7
|
+
# Gets a dataset, a selector...and should return a dataset with the selector definition applied.
|
|
8
|
+
def initialize(ds:, model:, selectors:, resolved:)
|
|
9
|
+
@selector = selectors
|
|
10
|
+
@ds = ds
|
|
11
|
+
@top_model = model
|
|
12
|
+
@resolved = resolved
|
|
13
|
+
@seen = Set.new
|
|
14
|
+
@root = model.table_name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_select(ds:, model:, table_name:)
|
|
18
|
+
if (fields = fields_for(model))
|
|
19
|
+
# Note, let's always add the pk fields so that associations can load properly
|
|
20
|
+
fields = fields | model.primary_key | [:id]
|
|
21
|
+
qualified = fields.map { |f| Sequel.qualify(table_name, f) }
|
|
22
|
+
ds.select(*qualified)
|
|
23
|
+
else
|
|
24
|
+
ds
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def generate
|
|
29
|
+
@ds = add_select(ds: ds, model: top_model, table_name: root)
|
|
30
|
+
|
|
31
|
+
tracks = only_assoc_for(top_model, resolved)
|
|
32
|
+
@ds = tracks.inject(@ds) do |dataset, track|
|
|
33
|
+
next dataset if @seen.include?([top_model, track])
|
|
34
|
+
@seen << [top_model, track]
|
|
35
|
+
assoc_model = top_model.associations[track][:model]
|
|
36
|
+
# hash[track] = _eager(assoc_model, resolved[track])
|
|
37
|
+
dataset.eager(track => _eager(assoc_model, resolved[track]))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def _eager(model, resolved)
|
|
42
|
+
lambda do |dset|
|
|
43
|
+
d = add_select(ds: dset, model: model, table_name: model.table_name)
|
|
44
|
+
|
|
45
|
+
tracks = only_assoc_for(model, resolved)
|
|
46
|
+
tracks.inject(d) do |dataset, track|
|
|
47
|
+
next dataset if @seen.include?([model, track])
|
|
48
|
+
@seen << [model, track]
|
|
49
|
+
assoc_model = model.associations[track][:model]
|
|
50
|
+
dataset.eager(track => _eager(assoc_model, resolved[track]))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def only_assoc_for(model, hash)
|
|
56
|
+
hash.keys.reject { |assoc| model.associations[assoc].nil? }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def fields_for(model)
|
|
60
|
+
selector[model][:select].to_a
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Make Praxis' request derive from ActionDispatch
|
|
2
|
+
if defined? Praxis::Request
|
|
3
|
+
puts "IT seems that we're trying to redefine Praxis' request parent too late."
|
|
4
|
+
puts "-> try to include the Rails compat pieces earlier in the bootstrap process (before Praxis::Request is requried)"
|
|
5
|
+
exit(-1)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'praxis/request_superclassing'
|
|
10
|
+
|
|
11
|
+
module Praxis
|
|
12
|
+
require 'action_dispatch'
|
|
13
|
+
Praxis.request_superclass = ::ActionDispatch::Request
|
|
14
|
+
end
|
|
15
|
+
require 'praxis/request'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
@@ -24,7 +24,7 @@ module Praxis
|
|
|
24
24
|
response.body = render(object, include_nil: include_nil)
|
|
25
25
|
response
|
|
26
26
|
rescue Praxis::Renderer::CircularRenderingError => e
|
|
27
|
-
Praxis::Application.
|
|
27
|
+
Praxis::Application.current_instance.validation_handler.handle!(
|
|
28
28
|
summary: "Circular Rendering Error when rendering response. " +
|
|
29
29
|
"Please especify a view to narrow the dependent fields, or narrow your field set.",
|
|
30
30
|
exception: e,
|
data/lib/praxis/file_group.rb
CHANGED
|
@@ -2,19 +2,39 @@ module Praxis
|
|
|
2
2
|
class MiddlewareApp
|
|
3
3
|
|
|
4
4
|
attr_reader :target
|
|
5
|
-
|
|
6
5
|
# Initialize the application instance with the desired args, and return the wrapping class.
|
|
7
6
|
def self.for( **args )
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Class.new(self) do
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :app_instance
|
|
10
|
+
attr_reader :app_name, :skip_registration
|
|
11
|
+
end
|
|
12
|
+
@app_name = args.delete(:name)
|
|
13
|
+
@skip_registration = args.delete(:skip_registration) || false
|
|
14
|
+
@args = args
|
|
15
|
+
@app_instance = nil
|
|
16
|
+
|
|
17
|
+
def self.name
|
|
18
|
+
'MiddlewareApp'
|
|
19
|
+
end
|
|
20
|
+
def self.args
|
|
21
|
+
@args
|
|
22
|
+
end
|
|
23
|
+
def self.setup
|
|
24
|
+
app_instance.setup(**args)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
11
28
|
|
|
12
29
|
def initialize( inner )
|
|
13
30
|
@target = inner
|
|
31
|
+
self.class.app_instance = Praxis::Application.new(name: self.class.app_name, skip_registration: self.class.skip_registration)
|
|
14
32
|
end
|
|
15
|
-
|
|
33
|
+
|
|
16
34
|
def call(env)
|
|
17
|
-
|
|
35
|
+
# NOTE: Need to make sure somebody has properly called the setup above before this is called
|
|
36
|
+
#@app_instance ||= Praxis::Application.new.setup(**self.class.args) #I Think that's not right at all...
|
|
37
|
+
result = self.class.app_instance.call(env)
|
|
18
38
|
|
|
19
39
|
unless ( [404,405].include?(result[0].to_i) && result[1]['X-Cascade'] == 'pass' )
|
|
20
40
|
# Respect X-Cascade header if it doesn't specify 'pass'
|
|
@@ -22,6 +22,7 @@ module Praxis
|
|
|
22
22
|
TERMINAL_CRLF = /\r\n$/.freeze
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
PARAMS_BUF_SIZE = 65536 # Same as implicitly in rack 1.x
|
|
25
26
|
BUFSIZE = 16384
|
|
26
27
|
|
|
27
28
|
def self.parse(headers,body)
|
|
@@ -87,9 +88,9 @@ module Praxis
|
|
|
87
88
|
|
|
88
89
|
@buf = ""
|
|
89
90
|
|
|
90
|
-
@params =
|
|
91
|
+
@params = new_params
|
|
91
92
|
|
|
92
|
-
@boundary_size =
|
|
93
|
+
@boundary_size = @boundary.bytesize + EOL.size
|
|
93
94
|
|
|
94
95
|
if @content_length = @headers['Content-Length']
|
|
95
96
|
@content_length = @content_length.to_i
|
|
@@ -98,6 +99,17 @@ module Praxis
|
|
|
98
99
|
true
|
|
99
100
|
end
|
|
100
101
|
|
|
102
|
+
if Rack.const_defined?(:RELEASE) && Rack::RELEASE[0] == '2'
|
|
103
|
+
# Rack 2 requires the buffer size
|
|
104
|
+
def new_params
|
|
105
|
+
Rack::Utils::KeySpaceConstrainedParams.new(PARAMS_BUF_SIZE)
|
|
106
|
+
end
|
|
107
|
+
else
|
|
108
|
+
def new_params
|
|
109
|
+
Rack::Utils::KeySpaceConstrainedParams.new
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
101
113
|
def full_boundary
|
|
102
114
|
@boundary + EOL
|
|
103
115
|
end
|
|
@@ -10,6 +10,7 @@ module Praxis
|
|
|
10
10
|
attr_accessor :headers_attribute
|
|
11
11
|
attr_accessor :filename_attribute
|
|
12
12
|
attr_accessor :default_handler
|
|
13
|
+
attr_accessor :application
|
|
13
14
|
|
|
14
15
|
def self.check_option!(name, definition)
|
|
15
16
|
case name
|
|
@@ -77,7 +78,8 @@ module Praxis
|
|
|
77
78
|
@name = name
|
|
78
79
|
@body = body
|
|
79
80
|
@headers = headers
|
|
80
|
-
@
|
|
81
|
+
@application = Praxis::Application.current_instance
|
|
82
|
+
@default_handler = application.handlers['json']
|
|
81
83
|
|
|
82
84
|
if content_type.nil?
|
|
83
85
|
self.content_type = 'text/plain'
|
|
@@ -212,7 +214,7 @@ module Praxis
|
|
|
212
214
|
end
|
|
213
215
|
|
|
214
216
|
def handler
|
|
215
|
-
handlers =
|
|
217
|
+
handlers = application.handlers
|
|
216
218
|
(content_type && handlers[content_type.handler_name]) || @default_handler
|
|
217
219
|
end
|
|
218
220
|
|
|
@@ -249,7 +251,7 @@ module Praxis
|
|
|
249
251
|
|
|
250
252
|
# and return that one if it already corresponds to a registered handler
|
|
251
253
|
# otherwise, add the encoding
|
|
252
|
-
if
|
|
254
|
+
if application.handlers.include?(pick.handler_name)
|
|
253
255
|
return pick
|
|
254
256
|
else
|
|
255
257
|
return pick + handler_name
|
|
@@ -33,7 +33,7 @@ require 'terminal-table'
|
|
|
33
33
|
# }
|
|
34
34
|
# }
|
|
35
35
|
# 2. log_stats: A String indicating what kind of DB stats you would like
|
|
36
|
-
# output into the Praxis::Application.
|
|
36
|
+
# output into the Praxis::Application.current_instance.logger app log. Possible
|
|
37
37
|
# values are: "detailed", "short", and "skip" (i.e. do not print the stats
|
|
38
38
|
# at all).
|
|
39
39
|
# 3. stats_log_level: the logging level with which the statistics should be logged.
|
|
@@ -238,7 +238,7 @@ module Praxis
|
|
|
238
238
|
end
|
|
239
239
|
|
|
240
240
|
def self.to_logger(message)
|
|
241
|
-
Praxis::Application.
|
|
241
|
+
Praxis::Application.current_instance.logger.__send__(Plugin.instance.config.stats_log_level, "Praxis::Mapper Statistics: #{message}")
|
|
242
242
|
end
|
|
243
243
|
end
|
|
244
244
|
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require 'praxis/plugin'
|
|
2
|
+
require 'praxis/plugin_concern'
|
|
3
|
+
|
|
4
|
+
module Praxis
|
|
5
|
+
module Plugins
|
|
6
|
+
module RailsPlugin
|
|
7
|
+
include Praxis::PluginConcern
|
|
8
|
+
|
|
9
|
+
class Plugin < Praxis::Plugin
|
|
10
|
+
|
|
11
|
+
def setup!
|
|
12
|
+
require 'praxis/dispatcher'
|
|
13
|
+
enable_action_controller_instrumentation
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def enable_action_controller_instrumentation
|
|
18
|
+
Praxis::Dispatcher.class_eval do
|
|
19
|
+
# Wrap the original action dispatch with a method that instruments rails-expected bits...
|
|
20
|
+
alias_method :orig_instrumented_dispatch, :instrumented_dispatch
|
|
21
|
+
|
|
22
|
+
def instrumented_dispatch( praxis_payload )
|
|
23
|
+
rails_payload = {
|
|
24
|
+
:controller => controller.class.name,
|
|
25
|
+
:action => action.name,
|
|
26
|
+
:params => ( (request.params) ? request.params.dump : {} ),
|
|
27
|
+
:method => request.verb,
|
|
28
|
+
:path => (request.fullpath rescue "unknown")
|
|
29
|
+
}
|
|
30
|
+
Praxis::Notifications.instrument("start_processing.action_controller", rails_payload.dup)
|
|
31
|
+
|
|
32
|
+
Praxis::Notifications.instrument 'process_action.action_controller' do |data|
|
|
33
|
+
begin
|
|
34
|
+
res = orig_instrumented_dispatch(praxis_payload)
|
|
35
|
+
# TODO: also add the db_runtime and view_runtime values...
|
|
36
|
+
data[:status] = res[0]
|
|
37
|
+
res
|
|
38
|
+
ensure
|
|
39
|
+
# Append DB runtime to payload
|
|
40
|
+
#data[:db_runtime] = 999
|
|
41
|
+
# Append rendering time to payload
|
|
42
|
+
#data[:view_runtime] = 123
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module Request
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module Controller
|
|
54
|
+
extend ActiveSupport::Concern
|
|
55
|
+
|
|
56
|
+
# Throw in some basic and expected controller methods
|
|
57
|
+
|
|
58
|
+
# Expose a rails-version of params from the controller
|
|
59
|
+
# Avoid using them explicitly in your controllers though. Use request.params object instead, as they are
|
|
60
|
+
# the Praxis ones that have been validated and coerced into the types you've defined.
|
|
61
|
+
def params
|
|
62
|
+
self.request.parameters
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Allow accessing the response headers from the controller
|
|
66
|
+
def headers
|
|
67
|
+
self.response.headers
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def session
|
|
71
|
+
self.request.session
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Allow setting the status and body of the response from the controller itself.
|
|
75
|
+
def status=(code)
|
|
76
|
+
self.response.status = code
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def response_body=(body)
|
|
80
|
+
#TODO: @_rendered = true # Necessary to know if to stop filter chain or not...
|
|
81
|
+
self.response.body = body
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def head(status, options = {})
|
|
85
|
+
options, status = status, nil if status.is_a?(Hash)
|
|
86
|
+
status ||= options.delete(:status) || :ok
|
|
87
|
+
location = options.delete(:location)
|
|
88
|
+
content_type = options.delete(:content_type)
|
|
89
|
+
|
|
90
|
+
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[status]
|
|
91
|
+
response = Praxis::Response.new(status: code, body: status.to_s, location: location)
|
|
92
|
+
|
|
93
|
+
options.each do |key, value|
|
|
94
|
+
response.headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
|
|
95
|
+
end
|
|
96
|
+
response.content_type = content_type if content_type
|
|
97
|
+
response
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|