Class: SpinelKit::Url
- Inherits:
-
Object
- Object
- SpinelKit::Url
- Defined in:
- lib/spinel_kit/url.rb
Class Method Summary collapse
-
.escape(s) ⇒ Object
Percent-encode everything outside the RFC 3986 unreserved set (ALPHA / DIGIT /
-._~); the rest becomes%XXwith UPPERCASE hex. -
.find_idx(s, needle, start) ⇒ Object
First index of
needleinsat/afterstart, or -1. -
.parse_query(s) ⇒ Object
"a=1&b=2&c" -> "a"=>"1","b"=>"2","c"=>"".
-
.split_url(u) ⇒ Object
Split
http(s)://host[:port]/path?queryinto a str=>str hash keyed scheme / host / port / path / query. -
.unescape(s) ⇒ Object
"%41+b" -> "A b" (form-decode:
+is space,%XXis a byte).
Class Method Details
.escape(s) ⇒ Object
Percent-encode everything outside the RFC 3986 unreserved set
(ALPHA / DIGIT / -._~); the rest becomes %XX with UPPERCASE hex.
(Space -> %20, not + -- this is the URI-component, not form, encoder.)
Byte-oriented: under Spinel String#[] indexes BYTES, so a multi-byte
UTF-8 char is encoded byte-by-byte (correct %XX of each byte). Under CRuby,
pass a binary string for the same behaviour (else [] splits on chars).
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/spinel_kit/url.rb', line 50 def self.escape(s) out = "" i = 0 while i < s.length c = s[i] if (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c >= "0" && c <= "9") || c == "-" || c == "." || c == "_" || c == "~" out = out + c else b = c.getbyte(0) out = out + "%" + Hex.nibble_char(b / 16) + Hex.nibble_char(b % 16) end i += 1 end out end |
.find_idx(s, needle, start) ⇒ Object
First index of needle in s at/after start, or -1. Internal
(Spinel-safe substring search; see the module comment).
153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/spinel_kit/url.rb', line 153 def self.find_idx(s, needle, start) nlen = needle.length slen = s.length pos = start while pos <= slen - nlen if s[pos, nlen] == needle return pos end pos += 1 end -1 end |
.parse_query(s) ⇒ Object
"a=1&b=2&c" -> "a"=>"1","b"=>"2","c"=>"". Keys + values are
form-decoded (unescape).
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/spinel_kit/url.rb', line 70 def self.parse_query(s) h = {"" => ""} h.delete("") if s.length == 0 return h end pairs = s.split("&") pairs.each do |pair| if pair.length > 0 eq = Url.find_idx(pair, "=", 0) if eq < 0 h[Url.unescape(pair)] = "" else k = pair[0, eq] v = pair[eq + 1, pair.length - eq - 1] h[Url.unescape(k)] = Url.unescape(v) end end end h end |
.split_url(u) ⇒ Object
Split http(s)://host[:port]/path?query into a str=>str hash keyed
scheme / host / port / path / query. Without a scheme the input is
treated as a path (host stays empty). Default ports follow the scheme
(80 / 443); query is the raw substring after ? (not decoded).
One body on purpose: Spinel widens a Hash-typed value when a helper
mutates it and the caller keeps reading, so out stays StrStrHash only
if nothing factors the mutation out (find_idx returns an int, no mutate).
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/spinel_kit/url.rb', line 100 def self.split_url(u) out = {"" => ""} out.delete("") out["scheme"] = "" out["host"] = "" out["port"] = "" out["path"] = "/" out["query"] = "" rest = u if rest.length >= 7 && rest[0, 7] == "http://" out["scheme"] = "http" out["port"] = "80" rest = rest[7, rest.length - 7] elsif rest.length >= 8 && rest[0, 8] == "https://" out["scheme"] = "https" out["port"] = "443" rest = rest[8, rest.length - 8] end if out["scheme"].length > 0 slash = Url.find_idx(rest, "/", 0) hostport = rest tail = "/" if slash >= 0 hostport = rest[0, slash] tail = rest[slash, rest.length - slash] end colon = Url.find_idx(hostport, ":", 0) if colon >= 0 out["host"] = hostport[0, colon] out["port"] = hostport[colon + 1, hostport.length - colon - 1] else out["host"] = hostport end rest = tail end qi = Url.find_idx(rest, "?", 0) if qi >= 0 out["path"] = rest[0, qi] out["query"] = rest[qi + 1, rest.length - qi - 1] else out["path"] = rest end if out["path"].length == 0 out["path"] = "/" end out end |
.unescape(s) ⇒ Object
"%41+b" -> "A b" (form-decode: + is space, %XX is a byte).
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/spinel_kit/url.rb', line 16 def self.unescape(s) out = "" i = 0 n = s.length while i < n c = s[i] if c == "+" out = out + " " i += 1 elsif c == "%" && i + 2 < n hi = Hex.nibble(s[i + 1]) lo = Hex.nibble(s[i + 2]) if hi >= 0 && lo >= 0 out = out + ((hi * 16 + lo).chr) i += 3 else out = out + c i += 1 end else out = out + c i += 1 end end out end |