praxis 0.21 → 2.0.pre.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -15
- data/CHANGELOG.md +328 -299
- 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 +9 -3
- data/lib/praxis/action_definition.rb +1 -1
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +1 -1
- data/lib/praxis/application.rb +1 -9
- data/lib/praxis/bootloader.rb +1 -4
- data/lib/praxis/config.rb +1 -1
- data/lib/praxis/dispatcher.rb +10 -6
- data/lib/praxis/docs/generator.rb +2 -1
- 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/sequel_filter_query_builder.rb +125 -0
- data/lib/praxis/extensions/field_selection.rb +1 -9
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +51 -0
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +61 -0
- data/lib/praxis/extensions/rails_compat.rb +2 -0
- data/lib/praxis/extensions/rails_compat/request_methods.rb +19 -0
- data/lib/praxis/handlers/xml.rb +1 -1
- data/lib/praxis/mapper/active_model_compat.rb +98 -0
- data/lib/praxis/mapper/resource.rb +242 -0
- data/lib/praxis/mapper/selector_generator.rb +149 -0
- data/lib/praxis/mapper/sequel_compat.rb +76 -0
- data/lib/praxis/media_type_identifier.rb +2 -1
- data/lib/praxis/middleware_app.rb +20 -2
- data/lib/praxis/multipart/parser.rb +14 -2
- data/lib/praxis/notifications.rb +1 -1
- data/lib/praxis/plugins/mapper_plugin.rb +64 -0
- data/lib/praxis/plugins/rails_plugin.rb +104 -0
- data/lib/praxis/request.rb +7 -1
- data/lib/praxis/request_superclassing.rb +11 -0
- data/lib/praxis/resource_definition.rb +5 -5
- data/lib/praxis/response.rb +1 -1
- data/lib/praxis/route.rb +1 -1
- data/lib/praxis/routing_config.rb +1 -1
- data/lib/praxis/trait.rb +1 -1
- data/lib/praxis/types/media_type_common.rb +2 -2
- data/lib/praxis/types/multipart.rb +1 -1
- data/lib/praxis/types/multipart_array.rb +2 -2
- data/lib/praxis/types/multipart_array/part_definition.rb +1 -1
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +14 -13
- data/spec/functional_spec.rb +4 -7
- data/spec/praxis/action_definition_spec.rb +1 -1
- data/spec/praxis/application_spec.rb +1 -1
- data/spec/praxis/collection_spec.rb +3 -2
- data/spec/praxis/config_spec.rb +2 -2
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +106 -0
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +147 -0
- data/spec/praxis/extensions/field_selection/support/spec_resources_active_model.rb +130 -0
- data/spec/praxis/extensions/field_selection/support/spec_resources_sequel.rb +106 -0
- data/spec/praxis/handlers/xml_spec.rb +2 -2
- data/spec/praxis/mapper/resource_spec.rb +169 -0
- data/spec/praxis/mapper/selector_generator_spec.rb +293 -0
- data/spec/praxis/media_type_spec.rb +0 -10
- data/spec/praxis/middleware_app_spec.rb +29 -9
- data/spec/praxis/request_stages/action_spec.rb +8 -1
- data/spec/praxis/response_definition_spec.rb +7 -4
- data/spec/praxis/response_spec.rb +1 -1
- data/spec/praxis/responses/internal_server_error_spec.rb +2 -2
- data/spec/praxis/responses/validation_error_spec.rb +2 -2
- data/spec/praxis/router_spec.rb +1 -1
- data/spec/spec_app/app/controllers/instances.rb +1 -1
- data/spec/spec_app/config/environment.rb +3 -21
- data/spec/spec_helper.rb +11 -15
- data/spec/support/be_deep_equal_matcher.rb +39 -0
- data/spec/support/spec_resources.rb +124 -0
- data/tasks/thor/templates/generator/empty_app/Gemfile +3 -3
- metadata +102 -77
- data/.ruby-version +0 -1
- data/lib/praxis/extensions/mapper_selectors.rb +0 -16
- data/lib/praxis/media_type_collection.rb +0 -127
- data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
- data/lib/praxis/stats.rb +0 -113
- data/spec/praxis/media_type_collection_spec.rb +0 -157
- data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
- data/spec/praxis/stats_spec.rb +0 -9
- data/spec/spec_app/app/models/person.rb +0 -3
@@ -0,0 +1,149 @@
|
|
1
|
+
module Praxis::Mapper
|
2
|
+
|
3
|
+
class SelectorGeneratorNode
|
4
|
+
attr_reader :select, :model, :resource, :tracks
|
5
|
+
|
6
|
+
def initialize(resource)
|
7
|
+
@resource = resource
|
8
|
+
|
9
|
+
@select = Set.new
|
10
|
+
@select_star = false
|
11
|
+
@tracks = Hash.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(fields)
|
15
|
+
fields.each do |name, field|
|
16
|
+
map_property(name, field)
|
17
|
+
end
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def map_property(name, fields)
|
22
|
+
if resource.properties.key?(name)
|
23
|
+
add_property(name, fields)
|
24
|
+
elsif resource.model._praxis_associations.key?(name)
|
25
|
+
add_association(name, fields)
|
26
|
+
else
|
27
|
+
add_select(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_association(name, fields)
|
32
|
+
|
33
|
+
association = resource.model._praxis_associations.fetch(name) do
|
34
|
+
raise "missing association for #{resource} with name #{name}"
|
35
|
+
end
|
36
|
+
associated_resource = resource.model_map[association[:model]]
|
37
|
+
unless associated_resource
|
38
|
+
raise "Whoops! could not find a resource associated with model #{association[:model]} (root resource #{resource})"
|
39
|
+
end
|
40
|
+
# Add the required columns in this model to make sure the association can be loaded
|
41
|
+
association[:local_key_columns].each {|col| add_select(col) }
|
42
|
+
|
43
|
+
node = SelectorGeneratorNode.new(associated_resource)
|
44
|
+
if association[:remote_key_columns].nil?
|
45
|
+
binding.pry
|
46
|
+
puts association
|
47
|
+
end
|
48
|
+
unless association[:remote_key_columns].empty?
|
49
|
+
# Make sure we add the required columns for this association to the remote model query
|
50
|
+
fields = {} if fields == true
|
51
|
+
new_fields_as_hash = association[:remote_key_columns].each_with_object({}) do|name, hash|
|
52
|
+
hash[name] = true
|
53
|
+
end
|
54
|
+
fields.merge!(new_fields_as_hash)
|
55
|
+
end
|
56
|
+
|
57
|
+
node.add(fields) unless fields == true
|
58
|
+
|
59
|
+
self.merge_track(name, node)
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_select(name)
|
63
|
+
return @select_star = true if name == :*
|
64
|
+
return if @select_star
|
65
|
+
@select.add name
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_property(name, fields)
|
69
|
+
dependencies = resource.properties[name][:dependencies]
|
70
|
+
# Always add the underlying association if we're overriding the name...
|
71
|
+
add_association(name, fields) if resource.model._praxis_associations.key?(name)
|
72
|
+
if dependencies
|
73
|
+
dependencies.each do |dependency|
|
74
|
+
# To detect recursion, let's allow mapping depending fields to the same name of the property
|
75
|
+
# but properly detecting if it's a real association...in which case we've already added it above
|
76
|
+
if dependency == name
|
77
|
+
add_select(name) unless resource.model._praxis_associations.key?(name)
|
78
|
+
else
|
79
|
+
apply_dependency(dependency)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
head, *tail = resource.properties[name][:through]
|
85
|
+
return if head.nil?
|
86
|
+
|
87
|
+
new_fields = tail.reverse.inject(fields) do |thing, step|
|
88
|
+
{step => thing}
|
89
|
+
end
|
90
|
+
|
91
|
+
add_association(head, new_fields)
|
92
|
+
end
|
93
|
+
|
94
|
+
def apply_dependency(dependency)
|
95
|
+
case dependency
|
96
|
+
when Symbol
|
97
|
+
map_property(dependency, true)
|
98
|
+
when String
|
99
|
+
head, tail = dependency.split('.').collect(&:to_sym)
|
100
|
+
raise "String dependencies can not be singular" if tail.nil?
|
101
|
+
|
102
|
+
add_association(head, {tail => true})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def merge_track( track_name, node )
|
107
|
+
raise "Cannot merge another node for association #{track_name}: incompatible model" unless node.model == self.model
|
108
|
+
|
109
|
+
existing = self.tracks[track_name]
|
110
|
+
if existing
|
111
|
+
node.select.each do|col_name|
|
112
|
+
existing.add_select(col_name)
|
113
|
+
end
|
114
|
+
node.tracks.each do |name, n|
|
115
|
+
existing.merge(name, n)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
self.tracks[track_name] = node
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
def dump
|
124
|
+
hash = {}
|
125
|
+
hash[:model] = resource.model
|
126
|
+
if !@select.empty? || @select_star
|
127
|
+
hash[:columns] = @select_star ? [ :* ] : @select.to_a
|
128
|
+
end
|
129
|
+
unless @tracks.empty?
|
130
|
+
hash[:tracks] = @tracks.each_with_object({}) {|(name, node), hash| hash[name] = node.dump }
|
131
|
+
end
|
132
|
+
hash
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Generates a set of selectors given a resource and
|
137
|
+
# list of resource attributes.
|
138
|
+
class SelectorGenerator
|
139
|
+
# Entry point
|
140
|
+
def add(resource, fields)
|
141
|
+
@root = SelectorGeneratorNode.new(resource)
|
142
|
+
@root.add(fields)
|
143
|
+
end
|
144
|
+
|
145
|
+
def selectors
|
146
|
+
@root
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
|
4
|
+
module Praxis::Mapper
|
5
|
+
module SequelCompat
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_accessor :_resource
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def _filter_query_builder_class
|
14
|
+
Praxis::Extensions::SequelFilterQueryBuilder
|
15
|
+
end
|
16
|
+
|
17
|
+
def _field_selector_query_builder_class
|
18
|
+
Praxis::Extensions::FieldSelection::SequelQuerySelector
|
19
|
+
end
|
20
|
+
|
21
|
+
def _praxis_associations
|
22
|
+
orig = self.association_reflections.clone
|
23
|
+
orig.each do |k,v|
|
24
|
+
v[:model] = v.associated_class
|
25
|
+
v[:local_key_columns] = local_columns_used_for_the_association(v[:type], v)
|
26
|
+
v[:remote_key_columns] = remote_columns_used_for_the_association(v[:type], v)
|
27
|
+
if v.respond_to?(:primary_key)
|
28
|
+
v[:primary_key] = v.primary_key
|
29
|
+
else
|
30
|
+
# FIXME: figure out exactly what to do here.
|
31
|
+
# not super critical, as we can't track these associations
|
32
|
+
# directly, but it would be nice to traverse these
|
33
|
+
# properly.
|
34
|
+
v[:primary_key] = :unsupported
|
35
|
+
end
|
36
|
+
end
|
37
|
+
orig
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def local_columns_used_for_the_association(type, assoc_reflection)
|
42
|
+
case type
|
43
|
+
when :one_to_many
|
44
|
+
# The associated table (or middle table if many to many) will point to us by PK
|
45
|
+
assoc_reflection[:primary_key_columns]
|
46
|
+
when :many_to_one
|
47
|
+
# We have the FKs to the associated model
|
48
|
+
assoc_reflection[:keys]
|
49
|
+
when :many_to_many
|
50
|
+
# The middle table if many to many) will point to us by key (usually the PK, but not always)
|
51
|
+
assoc_reflection[:left_primary_keys]
|
52
|
+
else
|
53
|
+
raise "association type #{type} not supported"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def remote_columns_used_for_the_association(type, assoc_reflection)
|
58
|
+
case type
|
59
|
+
when :one_to_many
|
60
|
+
# The columns in the associated table that will point back to the original association
|
61
|
+
assoc_reflection[:keys]
|
62
|
+
when :many_to_one
|
63
|
+
# The columns in the associated table that the children will point to (usually the PK, but not always) ??
|
64
|
+
[assoc_reflection.associated_class.primary_key]
|
65
|
+
when :many_to_many
|
66
|
+
# The middle table if many to many will point to us by key (usually the PK, but not always) ??
|
67
|
+
[assoc_reflection.associated_class.primary_key]
|
68
|
+
else
|
69
|
+
raise "association type #{type} not supported"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -146,7 +146,8 @@ module Praxis
|
|
146
146
|
if self.parameters.empty?
|
147
147
|
self
|
148
148
|
else
|
149
|
-
|
149
|
+
val = {type: self.type, subtype: self.subtype, suffix: self.suffix}
|
150
|
+
MediaTypeIdentifier.load(val)
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
@@ -5,15 +5,33 @@ module Praxis
|
|
5
5
|
|
6
6
|
# Initialize the application instance with the desired args, and return the wrapping class.
|
7
7
|
def self.for( **args )
|
8
|
-
|
9
|
-
|
8
|
+
Class.new(self) do
|
9
|
+
@args = args
|
10
|
+
@setup_done = false
|
11
|
+
def self.name
|
12
|
+
'MiddlewareApp'
|
13
|
+
end
|
14
|
+
def self.args
|
15
|
+
@args
|
16
|
+
end
|
17
|
+
def self.setup_done
|
18
|
+
@setup_done
|
19
|
+
end
|
20
|
+
def self.setup
|
21
|
+
@setup_done = true
|
22
|
+
Praxis::Application.instance.setup(**@args)
|
23
|
+
end
|
24
|
+
end
|
10
25
|
end
|
11
26
|
|
12
27
|
def initialize( inner )
|
13
28
|
@target = inner
|
29
|
+
@setup_done = false
|
14
30
|
end
|
15
31
|
|
16
32
|
def call(env)
|
33
|
+
self.class.setup unless self.class.setup_done
|
34
|
+
|
17
35
|
result = Praxis::Application.instance.call(env)
|
18
36
|
|
19
37
|
unless ( [404,405].include?(result[0].to_i) && result[1]['X-Cascade'] == '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
|
data/lib/praxis/notifications.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'praxis/extensions/attribute_filtering/filtering_params'
|
3
|
+
|
4
|
+
module Praxis
|
5
|
+
module Plugins
|
6
|
+
module MapperPlugin
|
7
|
+
include Praxis::PluginConcern
|
8
|
+
|
9
|
+
class Plugin < Praxis::Plugin
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def config_key
|
13
|
+
:mapper
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_config!
|
17
|
+
{} # override the default one, since we don't necessarily want to configure it via a yaml file.
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare_config!(node)
|
21
|
+
node.attributes do
|
22
|
+
attribute :debug_queries, Attributor::Boolean, default: false,
|
23
|
+
description: 'Weather or not to log debug information about queries executed in the build_query automation module'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Controller
|
29
|
+
extend ActiveSupport::Concern
|
30
|
+
|
31
|
+
included do
|
32
|
+
include Praxis::Extensions::FieldExpansion
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_selectors
|
36
|
+
return unless self.media_type.respond_to?(:domain_model) &&
|
37
|
+
self.media_type.domain_model < Praxis::Mapper::Resource
|
38
|
+
|
39
|
+
resolved = Praxis::MediaType::FieldResolver.resolve(self.media_type, self.expanded_fields)
|
40
|
+
selector_generator.add(self.media_type.domain_model, resolved)
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_query(base_query) # rubocop:disable Metrics/AbcSize
|
44
|
+
domain_model = self.media_type&.domain_model
|
45
|
+
raise "No domain model defined for #{self.name}. Cannot use the attribute filtering helpers without it" unless domain_model
|
46
|
+
|
47
|
+
filters = request.params.filters if request.params&.respond_to?(:filters)
|
48
|
+
base_query = domain_model.craft_filter_query( base_query , filters: filters )
|
49
|
+
|
50
|
+
base_query = domain_model.craft_field_selection_query(base_query, selectors: selector_generator.selectors)
|
51
|
+
|
52
|
+
# TODO: handle pagination and ordering
|
53
|
+
base_query
|
54
|
+
end
|
55
|
+
|
56
|
+
def selector_generator
|
57
|
+
@selector_generator ||= Praxis::Mapper::SelectorGenerator.new
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
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
|