nyara 0.0.1.pre.5 → 0.0.1.pre.6
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/example/factorial.rb +19 -0
- data/ext/accept.c +2 -2
- data/ext/event.c +48 -23
- data/ext/extconf.rb +2 -0
- data/ext/hashes.c +28 -3
- data/ext/http-parser/http_parser.h +1 -0
- data/ext/nyara.c +20 -3
- data/ext/nyara.h +13 -2
- data/ext/request.c +90 -13
- data/ext/request.h +8 -2
- data/ext/request_parse.c +135 -6
- data/ext/route.cc +7 -10
- data/ext/test_response.c +155 -0
- data/ext/url_encoded.c +0 -5
- data/lib/nyara/config.rb +5 -0
- data/lib/nyara/controller.rb +91 -28
- data/lib/nyara/cookie.rb +7 -0
- data/lib/nyara/flash.rb +23 -0
- data/lib/nyara/hashes/header_hash.rb +2 -0
- data/lib/nyara/nyara.rb +14 -2
- data/lib/nyara/part.rb +156 -0
- data/lib/nyara/patches/array.rb +5 -0
- data/lib/nyara/patches/blank.rb +128 -0
- data/lib/nyara/patches/json.rb +15 -0
- data/lib/nyara/patches/mini_support.rb +6 -0
- data/lib/nyara/patches/string.rb +21 -0
- data/lib/nyara/patches/to_query.rb +113 -0
- data/lib/nyara/request.rb +13 -15
- data/lib/nyara/route.rb +15 -80
- data/lib/nyara/route_entry.rb +69 -2
- data/lib/nyara/session.rb +66 -21
- data/lib/nyara/test.rb +170 -0
- data/lib/nyara/view.rb +5 -6
- data/lib/nyara.rb +7 -6
- data/nyara.gemspec +2 -2
- data/rakefile +34 -4
- data/readme.md +8 -1
- data/spec/config_spec.rb +28 -0
- data/spec/cpu_counter_spec.rb +9 -0
- data/spec/evented_io_spec.rb +1 -0
- data/spec/flash_spec.rb +29 -0
- data/spec/hashes_spec.rb +8 -0
- data/spec/mini_support_spec.rb +54 -0
- data/spec/part_spec.rb +52 -0
- data/spec/path_helper_spec.rb +22 -14
- data/spec/request_delegate_spec.rb +19 -11
- data/spec/route_entry_spec.rb +55 -0
- data/spec/session_spec.rb +69 -7
- data/spec/spec_helper.rb +3 -0
- data/spec/test_spec.rb +58 -0
- data/tools/hello.rb +11 -3
- data/tools/memcheck.rb +33 -0
- data/tools/s.rb +11 -0
- metadata +23 -7
- data/example/design.rb +0 -62
- data/example/fib.rb +0 -15
- data/spec/route_spec.rb +0 -84
- /data/ext/inc/{status_codes.inc → status_codes.h} +0 -0
data/lib/nyara/flash.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Nyara
|
2
|
+
class Flash
|
3
|
+
def initialize session
|
4
|
+
# NOTE no need to convert hash type because Session uses ParamHash for json parsing
|
5
|
+
@now = session.delete('flash.next') || ParamHash.new
|
6
|
+
session['flash.next'] = @next = ParamHash.new
|
7
|
+
end
|
8
|
+
attr_reader :now, :next
|
9
|
+
|
10
|
+
def [] key
|
11
|
+
@now[key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def []= key, value
|
15
|
+
@next[key] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def clear
|
19
|
+
@now.clear
|
20
|
+
@next.clear
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/nyara/nyara.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# patch core classes first
|
2
|
+
require_relative "patches/mini_support"
|
3
|
+
|
1
4
|
# master require
|
2
5
|
require "fiber"
|
3
6
|
require "cgi"
|
7
|
+
require "uri"
|
4
8
|
require "openssl"
|
5
|
-
require "json"
|
6
|
-
require "base64"
|
7
9
|
require "socket"
|
8
10
|
require "tilt"
|
9
11
|
|
@@ -16,15 +18,19 @@ require_relative "controller"
|
|
16
18
|
require_relative "request"
|
17
19
|
require_relative "cookie"
|
18
20
|
require_relative "session"
|
21
|
+
require_relative "flash"
|
19
22
|
require_relative "config"
|
20
23
|
require_relative "route"
|
21
24
|
require_relative "route_entry"
|
22
25
|
require_relative "view"
|
23
26
|
require_relative "cpu_counter"
|
27
|
+
require_relative "part"
|
24
28
|
|
25
29
|
module Nyara
|
26
30
|
HTTP_STATUS_FIRST_LINES = Hash[HTTP_STATUS_CODES.map{|k,v|[k, "HTTP/1.1 #{k} #{v}\r\n".freeze]}].freeze
|
27
31
|
|
32
|
+
HTTP_REDIRECT_STATUS = [300, 301, 302, 303, 307]
|
33
|
+
|
28
34
|
# base header response for 200
|
29
35
|
# caveat: these entries can not be deleted
|
30
36
|
OK_RESP_HEADER = HeaderHash.new
|
@@ -41,6 +47,12 @@ module Nyara
|
|
41
47
|
Config
|
42
48
|
end
|
43
49
|
|
50
|
+
def setup
|
51
|
+
Session.init
|
52
|
+
Route.compile
|
53
|
+
View.init
|
54
|
+
end
|
55
|
+
|
44
56
|
def start_server
|
45
57
|
port = Config[:port] || 3000
|
46
58
|
|
data/lib/nyara/part.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
module Nyara
|
2
|
+
# a part in multipart<br>
|
3
|
+
# for an easy introduction, http://msdn.microsoft.com/en-us/library/ms526943(v=exchg.10).aspx
|
4
|
+
#
|
5
|
+
# - todo make it possible to store data into /tmp (this requires memory threshold counting)
|
6
|
+
# - todo nested multipart?
|
7
|
+
class Part < ParamHash
|
8
|
+
MECHANISMS = %w[base64 quoted-printable 7bit 8bit binary].freeze
|
9
|
+
MECHANISMS.each &:freeze
|
10
|
+
|
11
|
+
# rfc2616
|
12
|
+
#
|
13
|
+
# token := 1*<any CHAR except CTLs or separators>
|
14
|
+
# separators := "(" | ")" | "<" | ">" | "@"
|
15
|
+
# | "," | ";" | ":" | "\" | <">
|
16
|
+
# | "/" | "[" | "]" | "?" | "="
|
17
|
+
# | "{" | "}" | " " | "\t"
|
18
|
+
# CTL := <any US-ASCII control character
|
19
|
+
# (octets 0 - 31) and DEL (127)>
|
20
|
+
#
|
21
|
+
TOKEN = /[^\x00-\x1f\x7f()<>@,;:\\"\/\[\]?=\{\}\ \t]+/ni
|
22
|
+
|
23
|
+
# rfc5978
|
24
|
+
#
|
25
|
+
# attr-char := ALPHA / DIGIT ; rfc5234
|
26
|
+
# / "!" / "#" / "$" / "&" / "+" / "-" / "."
|
27
|
+
# / "^" / "_" / "`" / "|" / "~"
|
28
|
+
#
|
29
|
+
ATTR_CHAR = /[a-z0-9!#$&+\-\.\^_`|~]/ni
|
30
|
+
|
31
|
+
# rfc5978 (NOTE rfc2231 param continuations is not recommended)
|
32
|
+
#
|
33
|
+
# value-chars := pct-encoded / attr-char
|
34
|
+
# pct-encoded := "%" HEXDIG HEXDIG
|
35
|
+
#
|
36
|
+
EX_PARAM = /\s*;\s*(filename|name)\s*(?:
|
37
|
+
= \s* "((?>\\"|[^"])*)" # quoted string - 2
|
38
|
+
| = \s* (#{TOKEN}) # token - 3
|
39
|
+
| \*= \s* ([\w\-]+) # charset - 4
|
40
|
+
'[\w\-]+' # language
|
41
|
+
((?>%\h\h|#{ATTR_CHAR})+) # value-chars - 5
|
42
|
+
)/xni
|
43
|
+
|
44
|
+
# analyse given +head+ and build a param hash representing the part
|
45
|
+
#
|
46
|
+
# [head] header
|
47
|
+
# [mechanism] 7bit, 8bit, binary, base64, or quoted-printable
|
48
|
+
# [type] mime type
|
49
|
+
# [data] decoded data (incomplete before Part#final called)
|
50
|
+
# [filename] basename of uploaded data
|
51
|
+
# [name] param name
|
52
|
+
#
|
53
|
+
def initialize head
|
54
|
+
self['head'] = head
|
55
|
+
if mechanism = head['Content-Transfer-Encoding']
|
56
|
+
self['mechanism'] = mechanism.strip.downcase
|
57
|
+
end
|
58
|
+
if self['type'] = head['Content-Type']
|
59
|
+
self['type'] = self['type'][/.*?(?=;|$)/]
|
60
|
+
end
|
61
|
+
self['data'] = ''.force_encoding('binary')
|
62
|
+
|
63
|
+
disposition = head['Content-Disposition']
|
64
|
+
if disposition
|
65
|
+
# skip first token
|
66
|
+
ex_params = disposition.sub TOKEN, ''
|
67
|
+
|
68
|
+
# store values not so specific as encoded value
|
69
|
+
tmp_values = {}
|
70
|
+
ex_params.scan EX_PARAM do |name, v1, v2, enc, v3|
|
71
|
+
name.downcase!
|
72
|
+
if enc
|
73
|
+
# value with charset and lang is more specific
|
74
|
+
self[name] ||= enc_unescape enc, v3
|
75
|
+
else
|
76
|
+
tmp_values[name] ||= (v1 || (CGI.unescape(v2) rescue nil))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
self['filename'] ||= tmp_values['filename']
|
80
|
+
self['name'] ||= tmp_values['name']
|
81
|
+
end
|
82
|
+
if self['filename']
|
83
|
+
self['filename'] = File.basename self['filename']
|
84
|
+
end
|
85
|
+
self['name'] ||= head['Content-Id']
|
86
|
+
end
|
87
|
+
|
88
|
+
# prereq: +raw+ in binary encoding
|
89
|
+
def update raw
|
90
|
+
case self['mechanism']
|
91
|
+
when 'base64'
|
92
|
+
# rfc2045#section-6.8
|
93
|
+
raw.gsub! /\s+/n, ''
|
94
|
+
if self['tmp']
|
95
|
+
raw = (self['tmp'] << raw)
|
96
|
+
end
|
97
|
+
# last part can be at most 4 bytes and 2 '='s
|
98
|
+
size = raw.bytesize - 6
|
99
|
+
if size >= 4
|
100
|
+
size = size / 4 * 4
|
101
|
+
self['data'] << raw.slice!(0...size).unpack('m').first
|
102
|
+
end
|
103
|
+
self['tmp'] = raw
|
104
|
+
|
105
|
+
when 'quoted-printable'
|
106
|
+
# http://en.wikipedia.org/wiki/Quoted-printable
|
107
|
+
if self['tmp']
|
108
|
+
raw = (self['tmp'] << raw)
|
109
|
+
end
|
110
|
+
if i = raw.rindex("\r\n")
|
111
|
+
s = raw.slice! i
|
112
|
+
s.gsub!(/=(?:(\h\h)|\r\n)/n) do
|
113
|
+
[$1].pack 'H*'
|
114
|
+
end
|
115
|
+
self['data'] << s
|
116
|
+
end
|
117
|
+
self['tmp'] = raw
|
118
|
+
|
119
|
+
else # '7bit', '8bit', 'binary', ...
|
120
|
+
self['data'] << raw
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def final
|
125
|
+
case self['mechanism']
|
126
|
+
when 'base64'
|
127
|
+
if tmp = self['tmp']
|
128
|
+
self['data'] << tmp.unpack('m').first
|
129
|
+
delete 'tmp'
|
130
|
+
end
|
131
|
+
|
132
|
+
when 'quoted-printable'
|
133
|
+
if tmp = self['tmp']
|
134
|
+
self['data'] << tmp.gsub(/=(\h\h)|=\r\n/n) do
|
135
|
+
[$1].pack 'H*'
|
136
|
+
end
|
137
|
+
delete 'tmp'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
# ---
|
144
|
+
# private
|
145
|
+
# +++
|
146
|
+
|
147
|
+
def enc_unescape enc, v
|
148
|
+
enc = (Encoding.find enc rescue nil)
|
149
|
+
v = CGI.unescape v
|
150
|
+
v.force_encoding(enc).encode!('utf-8') if enc
|
151
|
+
v
|
152
|
+
rescue
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# copied from activesupport
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Copyright (c) 2005-2013 David Heinemeier Hansson
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
=end
|
25
|
+
|
26
|
+
class Object
|
27
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
28
|
+
# For example, '', ' ', +nil+, [], and {} are all blank.
|
29
|
+
#
|
30
|
+
# This simplifies:
|
31
|
+
#
|
32
|
+
# if address.nil? || address.empty?
|
33
|
+
#
|
34
|
+
# ...to:
|
35
|
+
#
|
36
|
+
# if address.blank?
|
37
|
+
def blank?
|
38
|
+
respond_to?(:empty?) ? empty? : !self
|
39
|
+
end
|
40
|
+
|
41
|
+
# An object is present if it's not <tt>blank?</tt>.
|
42
|
+
def present?
|
43
|
+
!blank?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns object if it's <tt>present?</tt> otherwise returns +nil+.
|
47
|
+
# <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
|
48
|
+
#
|
49
|
+
# This is handy for any representation of objects where blank is the same
|
50
|
+
# as not present at all. For example, this simplifies a common check for
|
51
|
+
# HTTP POST/query parameters:
|
52
|
+
#
|
53
|
+
# state = params[:state] if params[:state].present?
|
54
|
+
# country = params[:country] if params[:country].present?
|
55
|
+
# region = state || country || 'US'
|
56
|
+
#
|
57
|
+
# ...becomes:
|
58
|
+
#
|
59
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
60
|
+
def presence
|
61
|
+
self if present?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class NilClass
|
66
|
+
# +nil+ is blank:
|
67
|
+
#
|
68
|
+
# nil.blank? # => true
|
69
|
+
def blank?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class FalseClass
|
75
|
+
# +false+ is blank:
|
76
|
+
#
|
77
|
+
# false.blank? # => true
|
78
|
+
def blank?
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class TrueClass
|
84
|
+
# +true+ is not blank:
|
85
|
+
#
|
86
|
+
# true.blank? # => false
|
87
|
+
def blank?
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Array
|
93
|
+
# An array is blank if it's empty:
|
94
|
+
#
|
95
|
+
# [].blank? # => true
|
96
|
+
# [1,2,3].blank? # => false
|
97
|
+
alias_method :blank?, :empty?
|
98
|
+
end
|
99
|
+
|
100
|
+
class Hash
|
101
|
+
# A hash is blank if it's empty:
|
102
|
+
#
|
103
|
+
# {}.blank? # => true
|
104
|
+
# { key: 'value' }.blank? # => false
|
105
|
+
alias_method :blank?, :empty?
|
106
|
+
end
|
107
|
+
|
108
|
+
class String
|
109
|
+
# A string is blank if it's empty or contains whitespaces only:
|
110
|
+
#
|
111
|
+
# ''.blank? # => true
|
112
|
+
# ' '.blank? # => true
|
113
|
+
# ' '.blank? # => true
|
114
|
+
# ' something here '.blank? # => false
|
115
|
+
def blank?
|
116
|
+
self !~ /[^[:space:]]/
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Numeric #:nodoc:
|
121
|
+
# No number is blank:
|
122
|
+
#
|
123
|
+
# 1.blank? # => false
|
124
|
+
# 0.blank? # => false
|
125
|
+
def blank?
|
126
|
+
false
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
# +JSON.parse '{"json_class":"MyClass"}'+ should not create object as MyClass
|
4
|
+
JSON.load_default_options.merge! create_additions: false
|
5
|
+
JSON::GenericObject.json_creatable = false
|
6
|
+
|
7
|
+
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
|
8
|
+
klass.class_eval do
|
9
|
+
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
|
10
|
+
# "</" is escaped for convenience use in javascript
|
11
|
+
def to_json(options = nil)
|
12
|
+
super(options).gsub '</', "<\\/"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# patch with 2.1 methods if not defined
|
2
|
+
class String
|
3
|
+
unless defined? b
|
4
|
+
def b
|
5
|
+
dup.force_encoding 'binary'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
unless defined? scrub
|
10
|
+
# NOTE: block unsupported
|
11
|
+
def scrub replacement=nil
|
12
|
+
if replacement
|
13
|
+
replacement = replacement.encode 'UTF-16BE'
|
14
|
+
else
|
15
|
+
replacement = "\xFF\xFD".force_encoding 'UTF-16BE'
|
16
|
+
end
|
17
|
+
r = encode("UTF-16BE", undef: :replace, invalid: :replace, replace: replacement)
|
18
|
+
r.encode("UTF-8").gsub("\0".encode("UTF-8"), '')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# copied from activesupport
|
2
|
+
require "cgi"
|
3
|
+
|
4
|
+
=begin
|
5
|
+
Copyright (c) 2005-2013 David Heinemeier Hansson
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
=end
|
26
|
+
|
27
|
+
class Object
|
28
|
+
# Alias of <tt>to_s</tt>.
|
29
|
+
def to_param
|
30
|
+
to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class String
|
35
|
+
alias to_param to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
class NilClass
|
39
|
+
# Returns +self+.
|
40
|
+
def to_param
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class TrueClass
|
46
|
+
# Returns +self+.
|
47
|
+
def to_param
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class FalseClass
|
53
|
+
# Returns +self+.
|
54
|
+
def to_param
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Array
|
60
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
61
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
62
|
+
def to_param
|
63
|
+
collect { |e| e.to_param }.join '/'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Hash
|
68
|
+
# Returns a string representation of the receiver suitable for use as a URL
|
69
|
+
# query string:
|
70
|
+
#
|
71
|
+
# {name: 'David', nationality: 'Danish'}.to_param
|
72
|
+
# # => "name=David&nationality=Danish"
|
73
|
+
#
|
74
|
+
# An optional namespace can be passed to enclose the param names:
|
75
|
+
#
|
76
|
+
# {name: 'David', nationality: 'Danish'}.to_param('user')
|
77
|
+
# # => "user[name]=David&user[nationality]=Danish"
|
78
|
+
#
|
79
|
+
# The string pairs "key=value" that conform the query string
|
80
|
+
# are sorted lexicographically in ascending order.
|
81
|
+
#
|
82
|
+
# This method is also aliased as +to_query+.
|
83
|
+
def to_param(namespace = nil)
|
84
|
+
collect do |key, value|
|
85
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
86
|
+
end.sort * '&'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Object
|
91
|
+
# Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
|
92
|
+
# param name.
|
93
|
+
#
|
94
|
+
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
|
95
|
+
def to_query(key)
|
96
|
+
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Array
|
101
|
+
# Converts an array into a string suitable for use as a URL query string,
|
102
|
+
# using the given +key+ as the param name.
|
103
|
+
#
|
104
|
+
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
105
|
+
def to_query(key)
|
106
|
+
prefix = "#{key}[]"
|
107
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Hash
|
112
|
+
alias_method :to_query, :to_param
|
113
|
+
end
|
data/lib/nyara/request.rb
CHANGED
@@ -4,6 +4,7 @@ module Nyara
|
|
4
4
|
# request and handler
|
5
5
|
class Request
|
6
6
|
# c-ext: http_method, scope, path, query, path_with_query format, accept, header
|
7
|
+
# cookie, session, flash
|
7
8
|
# status, response_content_type, response_header, response_header_extra_lines
|
8
9
|
# todo: body, move all underline methods into Ext
|
9
10
|
|
@@ -54,7 +55,7 @@ module Nyara
|
|
54
55
|
if r
|
55
56
|
r.split(':', 2).first
|
56
57
|
else
|
57
|
-
''
|
58
|
+
Config['host'] || 'localhost'
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|
@@ -65,12 +66,19 @@ module Nyara
|
|
65
66
|
if r
|
66
67
|
r = r.split(':', 2).last
|
67
68
|
end
|
68
|
-
r ? r.to_i :
|
69
|
+
r ? r.to_i : (Config['port'] || 80)
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
72
|
-
def
|
73
|
-
header['Host']
|
73
|
+
def host_with_port
|
74
|
+
header['Host'] || begin
|
75
|
+
p = port
|
76
|
+
if p == 80
|
77
|
+
domain
|
78
|
+
else
|
79
|
+
"#{domain}:#{p}"
|
80
|
+
end
|
81
|
+
end
|
74
82
|
end
|
75
83
|
|
76
84
|
def xhr?
|
@@ -121,18 +129,8 @@ module Nyara
|
|
121
129
|
end
|
122
130
|
end
|
123
131
|
|
124
|
-
def cookie
|
125
|
-
@cookie ||= Cookie.decode header
|
126
|
-
end
|
127
|
-
|
128
|
-
def session
|
129
|
-
@session ||= Session.decode cookie
|
130
|
-
end
|
131
|
-
|
132
|
-
# todo serialize the changed cookie
|
133
|
-
|
134
132
|
# todo rename and move it into Ext
|
135
|
-
def not_found
|
133
|
+
def not_found # :nodoc:
|
136
134
|
Ext.request_send_data self, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
|
137
135
|
end
|
138
136
|
end
|