syntropy 0.11 → 0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Rakefile +1 -1
- data/TODO.md +185 -135
- data/bin/syntropy +8 -3
- data/lib/syntropy/app.rb +232 -113
- data/lib/syntropy/errors.rb +40 -12
- data/lib/syntropy/markdown.rb +4 -2
- data/lib/syntropy/module.rb +41 -79
- data/lib/syntropy/request_extensions.rb +112 -2
- data/lib/syntropy/routing_tree.rb +550 -0
- data/lib/syntropy/utils.rb +42 -0
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +4 -1
- data/syntropy.gemspec +1 -1
- data/test/app/_lib/callable.rb +1 -1
- data/test/app/_lib/env.rb +21 -0
- data/test/app/params/[foo].rb +3 -0
- data/test/app/tmp.rb +1 -1
- data/test/app_multi_site/_site.rb +1 -1
- data/test/helper.rb +18 -2
- data/test/test_app.rb +17 -25
- data/test/test_errors.rb +38 -0
- data/test/test_module.rb +15 -5
- data/test/test_request_extensions.rb +163 -0
- data/test/test_routing_tree.rb +427 -0
- metadata +10 -6
- data/lib/syntropy/router.rb +0 -245
- data/test/test_router.rb +0 -116
- data/test/test_validation.rb +0 -35
@@ -3,15 +3,59 @@
|
|
3
3
|
require 'qeweney'
|
4
4
|
|
5
5
|
module Syntropy
|
6
|
+
# Extensions for the Qeweney::Request class
|
6
7
|
module RequestExtensions
|
8
|
+
attr_reader :route_params
|
9
|
+
attr_accessor :route
|
10
|
+
|
11
|
+
# Initializes request with additional fields
|
12
|
+
def initialize(headers, adapter)
|
13
|
+
@headers = headers
|
14
|
+
@adapter = adapter
|
15
|
+
@route = nil
|
16
|
+
@route_params = {}
|
17
|
+
@ctx = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets up mock request additional fields
|
21
|
+
def setup_mock_request
|
22
|
+
@route = nil
|
23
|
+
@route_params = {}
|
24
|
+
@ctx = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the request context
|
7
28
|
def ctx
|
8
29
|
@ctx ||= {}
|
9
30
|
end
|
10
31
|
|
32
|
+
# Checks the request's HTTP method against the given accepted values. If not
|
33
|
+
# included in the accepted values, raises an exception. Otherwise, returns
|
34
|
+
# the request's HTTP method.
|
35
|
+
#
|
36
|
+
# @param accepted [Array<String>] list of accepted HTTP methods
|
37
|
+
# @return [String] request's HTTP method
|
11
38
|
def validate_http_method(*accepted)
|
12
39
|
raise Syntropy::Error.method_not_allowed if !accepted.include?(method)
|
40
|
+
|
41
|
+
method
|
13
42
|
end
|
14
43
|
|
44
|
+
# Responds according to the given map. The given map defines the responses
|
45
|
+
# for each method. The value for each method is either an array containing
|
46
|
+
# the body and header values to use as response, or a proc returning such an
|
47
|
+
# array. For example:
|
48
|
+
#
|
49
|
+
# req.respond_by_http_method(
|
50
|
+
# 'head' => [nil, headers],
|
51
|
+
# 'get' => -> { [IO.read(fn), headers] }
|
52
|
+
# )
|
53
|
+
#
|
54
|
+
# If the request's method is not included in the given map, an exception is
|
55
|
+
# raised.
|
56
|
+
#
|
57
|
+
# @param map [Hash] hash mapping HTTP methods to responses
|
58
|
+
# @return [void]
|
15
59
|
def respond_by_http_method(map)
|
16
60
|
value = map[self.method]
|
17
61
|
raise Syntropy::Error.method_not_allowed if !value
|
@@ -21,6 +65,12 @@ module Syntropy
|
|
21
65
|
respond(body, headers)
|
22
66
|
end
|
23
67
|
|
68
|
+
# Responds to GET requests with the given body and headers. Otherwise raises
|
69
|
+
# an exception.
|
70
|
+
#
|
71
|
+
# @param body [String, nil] response body
|
72
|
+
# @param headers [Hash] response headers
|
73
|
+
# @return [void]
|
24
74
|
def respond_on_get(body, headers = {})
|
25
75
|
case self.method
|
26
76
|
when 'head'
|
@@ -28,10 +78,16 @@ module Syntropy
|
|
28
78
|
when 'get'
|
29
79
|
respond(body, headers)
|
30
80
|
else
|
31
|
-
|
81
|
+
raise Syntropy::Error.method_not_allowed
|
32
82
|
end
|
33
83
|
end
|
34
84
|
|
85
|
+
# Responds to POST requests with the given body and headers. Otherwise
|
86
|
+
# raises an exception.
|
87
|
+
#
|
88
|
+
# @param body [String, nil] response body
|
89
|
+
# @param headers [Hash] response headers
|
90
|
+
# @return [void]
|
35
91
|
def respond_on_post(body, headers = {})
|
36
92
|
case self.method
|
37
93
|
when 'head'
|
@@ -43,8 +99,38 @@ module Syntropy
|
|
43
99
|
end
|
44
100
|
end
|
45
101
|
|
102
|
+
# Validates and optionally converts request parameter value for the given
|
103
|
+
# parameter name against the given clauses. If no clauses are given,
|
104
|
+
# verifies the parameter value is not nil. A clause can be a class, such as
|
105
|
+
# String, Integer, etc, in which case the value is converted into the
|
106
|
+
# corresponding value. A clause can also be a range, for verifying the value
|
107
|
+
# is within the range. A clause can also be an array of two or more clauses,
|
108
|
+
# at least one of which should match the value. If the validation fails, an
|
109
|
+
# exception is raised. Example:
|
110
|
+
#
|
111
|
+
# height = req.validate_param(:height, Integer, 1..100)
|
112
|
+
#
|
113
|
+
# @param name [Symbol] parameter name
|
114
|
+
# @clauses [Array] one or more validation clauses
|
115
|
+
# @return [any] validated parameter value
|
46
116
|
def validate_param(name, *clauses)
|
47
|
-
|
117
|
+
validate(query[name], *clauses)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Validates and optionally converts a value against the given clauses. If no
|
121
|
+
# clauses are given, verifies the parameter value is not nil. A clause can
|
122
|
+
# be a class, such as String, Integer, etc, in which case the value is
|
123
|
+
# converted into the corresponding value. A clause can also be a range, for
|
124
|
+
# verifying the value is within the range. A clause can also be an array of
|
125
|
+
# two or more clauses, at least one of which should match the value. If the
|
126
|
+
# validation fails, an exception is raised.
|
127
|
+
#
|
128
|
+
# @param value [any] value
|
129
|
+
# @clauses [Array] one or more validation clauses
|
130
|
+
# @return [any] validated value
|
131
|
+
def validate(value, *clauses)
|
132
|
+
raise Syntropy::ValidationError, 'Validation error' if clauses.empty? && !value
|
133
|
+
|
48
134
|
clauses.each do |c|
|
49
135
|
valid = param_is_valid?(value, c)
|
50
136
|
raise(Syntropy::ValidationError, 'Validation error') if !valid
|
@@ -54,6 +140,20 @@ module Syntropy
|
|
54
140
|
value
|
55
141
|
end
|
56
142
|
|
143
|
+
# Reads the request body and returns form data.
|
144
|
+
#
|
145
|
+
# @return [Hash] form data
|
146
|
+
def get_form_data
|
147
|
+
body = read
|
148
|
+
if !body || body.empty?
|
149
|
+
raise Syntropy::Error.new(Qeweney::Status::BAD_REQUEST, 'Missing form data')
|
150
|
+
end
|
151
|
+
|
152
|
+
Qeweney::Request.parse_form_data(body, headers)
|
153
|
+
rescue Qeweney::BadRequestError
|
154
|
+
raise Syntropy::Error.new(Qeweney::Status::BAD_REQUEST, 'Invalid form data')
|
155
|
+
end
|
156
|
+
|
57
157
|
private
|
58
158
|
|
59
159
|
BOOL_REGEXP = /^(t|f|true|false|on|off|1|0|yes|no)$/
|
@@ -61,6 +161,11 @@ module Syntropy
|
|
61
161
|
INTEGER_REGEXP = /^[+-]?[0-9]+$/
|
62
162
|
FLOAT_REGEXP = /^[+-]?[0-9]+(\.[0-9]+)?$/
|
63
163
|
|
164
|
+
# Returns true the given value matches the given condition.
|
165
|
+
#
|
166
|
+
# @param value [any] value
|
167
|
+
# @param cond [any] condition
|
168
|
+
# @return [bool]
|
64
169
|
def param_is_valid?(value, cond)
|
65
170
|
return cond.any? { |c| param_is_valid?(value, c) } if cond.is_a?(Array)
|
66
171
|
|
@@ -77,6 +182,11 @@ module Syntropy
|
|
77
182
|
cond === value
|
78
183
|
end
|
79
184
|
|
185
|
+
# Converts the given value according to the given class.
|
186
|
+
#
|
187
|
+
# @param value [any] value
|
188
|
+
# @param klass [Class] class
|
189
|
+
# @return [any] converted value
|
80
190
|
def param_convert(value, klass)
|
81
191
|
if klass == :bool
|
82
192
|
value =~ BOOL_TRUE_REGEXP ? true : false
|