praxis 0.21 → 0.22.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|