xrb 0.11.2 → 0.12.0
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
- checksums.yaml.gz.sig +0 -0
- data/lib/xrb/buffer.rb +2 -0
- data/lib/xrb/builder.rb +20 -0
- data/lib/xrb/error.rb +19 -0
- data/lib/xrb/fallback/markup.rb +1220 -1217
- data/lib/xrb/fallback/markup.rl +1 -0
- data/lib/xrb/fallback/query.rb +313 -311
- data/lib/xrb/fallback/template.rb +541 -536
- data/lib/xrb/markup.rb +23 -3
- data/lib/xrb/query.rb +25 -0
- data/lib/xrb/strings.rb +19 -2
- data/lib/xrb/tag.rb +46 -0
- data/lib/xrb/template.rb +11 -1
- data/lib/xrb/version.rb +3 -2
- data/lib/xrb.rb +0 -2
- data/readme.md +27 -3
- data/releases.md +26 -0
- data.tar.gz.sig +0 -0
- metadata +7 -7
- metadata.gz.sig +0 -0
- data/lib/xrb/reference.rb +0 -108
- data/lib/xrb/uri.rb +0 -100
data/lib/xrb/markup.rb
CHANGED
|
@@ -6,20 +6,33 @@
|
|
|
6
6
|
require "cgi"
|
|
7
7
|
|
|
8
8
|
module XRB
|
|
9
|
-
#
|
|
9
|
+
# Provides the default markup rendering protocol, escaping object string representations.
|
|
10
10
|
module Markup
|
|
11
|
+
# Wrap a string as markup without escaping it.
|
|
12
|
+
# @parameter string [String] The already-safe markup string.
|
|
13
|
+
# @returns [MarkupString] The unescaped markup string.
|
|
11
14
|
def self.raw(string)
|
|
12
15
|
MarkupString.raw(string)
|
|
13
16
|
end
|
|
14
17
|
|
|
18
|
+
# Append a value to an output buffer using its markup representation.
|
|
19
|
+
# @parameter output [Interface(:<<)] The output buffer.
|
|
20
|
+
# @parameter value [Interface(:append_markup)] The value to append.
|
|
21
|
+
# @returns [Object] The result of appending the value.
|
|
15
22
|
def self.append(output, value)
|
|
16
23
|
value.append_markup(output)
|
|
17
24
|
end
|
|
18
25
|
|
|
26
|
+
# Append the escaped string representation of the receiver to an output buffer.
|
|
27
|
+
# @parameter output [Interface(:<<)] The output buffer.
|
|
28
|
+
# @returns [Object] The output buffer.
|
|
19
29
|
def append_markup(output)
|
|
20
30
|
output << ::CGI.escape_html(self.to_s)
|
|
21
31
|
end
|
|
22
32
|
|
|
33
|
+
# Append the escaped string representation of the receiver using a builder.
|
|
34
|
+
# @parameter builder [Builder] The builder receiving the escaped markup.
|
|
35
|
+
# @returns [Object] The builder's output buffer.
|
|
23
36
|
def build_markup(builder)
|
|
24
37
|
append_markup(builder.output)
|
|
25
38
|
end
|
|
@@ -29,8 +42,8 @@ module XRB
|
|
|
29
42
|
|
|
30
43
|
# Initialized from text which is escaped to use HTML entities.
|
|
31
44
|
class MarkupString < String
|
|
32
|
-
# @
|
|
33
|
-
# @
|
|
45
|
+
# @parameter string [String | Nil] The string value itself.
|
|
46
|
+
# @parameter escape [Boolean] Whether to escape the string.
|
|
34
47
|
def initialize(string = nil, escape = true)
|
|
35
48
|
if string
|
|
36
49
|
if escape
|
|
@@ -54,12 +67,19 @@ module XRB
|
|
|
54
67
|
true
|
|
55
68
|
end
|
|
56
69
|
|
|
70
|
+
# Append this already-safe markup string to an output buffer.
|
|
71
|
+
# @parameter output [Interface(:<<)] The output buffer.
|
|
72
|
+
# @returns [Object] The output buffer.
|
|
57
73
|
def append_markup(output)
|
|
58
74
|
output << self
|
|
59
75
|
end
|
|
60
76
|
end
|
|
61
77
|
|
|
78
|
+
# Helpers for constructing script-compatible markup values.
|
|
62
79
|
module Script
|
|
80
|
+
# Serialize a value as raw JSON markup without HTML escaping it.
|
|
81
|
+
# @parameter value [Object] The value to serialize.
|
|
82
|
+
# @returns [MarkupString] The serialized JSON markup.
|
|
63
83
|
def self.json(value)
|
|
64
84
|
MarkupString.new(JSON.dump(value), false)
|
|
65
85
|
end
|
data/lib/xrb/query.rb
CHANGED
|
@@ -9,14 +9,21 @@ require_relative "parsers"
|
|
|
9
9
|
require "uri"
|
|
10
10
|
|
|
11
11
|
module XRB
|
|
12
|
+
# Parses URL query strings into nested Ruby objects.
|
|
12
13
|
module Query
|
|
14
|
+
# Parse a query string buffer.
|
|
15
|
+
# @parameter buffer [Buffer] The buffer containing the query string.
|
|
16
|
+
# @returns [Hash] The parsed query parameters.
|
|
13
17
|
def self.parse(buffer)
|
|
14
18
|
Hash.new.tap do |query|
|
|
15
19
|
Parsers.parse_query(buffer, Delegate.new(query))
|
|
16
20
|
end
|
|
17
21
|
end
|
|
18
22
|
|
|
23
|
+
# Receives query parser events and assembles them into nested hashes and arrays.
|
|
19
24
|
class Delegate
|
|
25
|
+
# Initialize a query delegate with the top-level result hash.
|
|
26
|
+
# @parameter top [Hash] The hash which receives parsed query parameters.
|
|
20
27
|
def initialize(top = {})
|
|
21
28
|
@top = top
|
|
22
29
|
|
|
@@ -24,6 +31,10 @@ module XRB
|
|
|
24
31
|
@index = nil
|
|
25
32
|
end
|
|
26
33
|
|
|
34
|
+
# Select a symbolic key parsed from a query string.
|
|
35
|
+
# @parameter key [String] The parsed key.
|
|
36
|
+
# @parameter encoded [Boolean] Whether the key is URL encoded.
|
|
37
|
+
# @returns [Symbol] The selected key.
|
|
27
38
|
def string(key, encoded)
|
|
28
39
|
if encoded
|
|
29
40
|
key = ::URI.decode_www_form_component(key)
|
|
@@ -32,10 +43,16 @@ module XRB
|
|
|
32
43
|
index(key.to_sym)
|
|
33
44
|
end
|
|
34
45
|
|
|
46
|
+
# Select an integer key parsed from an array or hash index.
|
|
47
|
+
# @parameter key [String] The parsed integer key.
|
|
48
|
+
# @returns [Integer] The selected key.
|
|
35
49
|
def integer(key)
|
|
36
50
|
index(key.to_i)
|
|
37
51
|
end
|
|
38
52
|
|
|
53
|
+
# Descend into the previously selected key and select the next nested key.
|
|
54
|
+
# @parameter key [Symbol | Integer] The next key to select.
|
|
55
|
+
# @returns [Symbol | Integer] The selected key.
|
|
39
56
|
def index(key)
|
|
40
57
|
if @index
|
|
41
58
|
@current = @current.fetch(@index) do
|
|
@@ -46,6 +63,8 @@ module XRB
|
|
|
46
63
|
@index = key
|
|
47
64
|
end
|
|
48
65
|
|
|
66
|
+
# Descend into an array and select its next index.
|
|
67
|
+
# @returns [Integer] The selected array index.
|
|
49
68
|
def append
|
|
50
69
|
if @index
|
|
51
70
|
@current = @current.fetch(@index) do
|
|
@@ -56,6 +75,10 @@ module XRB
|
|
|
56
75
|
@index = @current.size
|
|
57
76
|
end
|
|
58
77
|
|
|
78
|
+
# Assign a parsed value to the selected key.
|
|
79
|
+
# @parameter value [String] The parsed value.
|
|
80
|
+
# @parameter encoded [Boolean] Whether the value is URL encoded.
|
|
81
|
+
# @returns [Nil] This method always returns `nil`.
|
|
59
82
|
def assign(value, encoded)
|
|
60
83
|
if encoded
|
|
61
84
|
value = ::URI.decode_www_form_component(value)
|
|
@@ -67,6 +90,8 @@ module XRB
|
|
|
67
90
|
@index = nil
|
|
68
91
|
end
|
|
69
92
|
|
|
93
|
+
# Assign `true` to a query key which has no explicit value.
|
|
94
|
+
# @returns [Nil] This method always returns `nil`.
|
|
70
95
|
def pair
|
|
71
96
|
if @index
|
|
72
97
|
@current[@index] = true
|
data/lib/xrb/strings.rb
CHANGED
|
@@ -4,14 +4,21 @@
|
|
|
4
4
|
# Copyright, 2012-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module XRB
|
|
7
|
+
# String transformation helpers used when generating markup and identifiers.
|
|
7
8
|
module Strings
|
|
8
9
|
HTML_ESCAPE = {"&" => "&", "<" => "<", ">" => ">", "\"" => """}
|
|
9
10
|
HTML_ESCAPE_PATTERN = Regexp.new("[" + Regexp.quote(HTML_ESCAPE.keys.join) + "]")
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
# Escape HTML-sensitive characters in a string.
|
|
13
|
+
# @parameter string [String] The string to escape.
|
|
14
|
+
# @returns [String] The HTML-escaped string.
|
|
11
15
|
def self.to_html(string)
|
|
12
16
|
string.gsub(HTML_ESCAPE_PATTERN){|c| HTML_ESCAPE[c]}
|
|
13
17
|
end
|
|
14
|
-
|
|
18
|
+
|
|
19
|
+
# Quote a string and escape quotes and line breaks within it.
|
|
20
|
+
# @parameter string [String] The string to quote.
|
|
21
|
+
# @returns [String] The quoted string.
|
|
15
22
|
def self.to_quoted_string(string)
|
|
16
23
|
string = string.gsub('"', '\\"')
|
|
17
24
|
string.gsub!(/\r/, "\\r")
|
|
@@ -25,10 +32,17 @@ module XRB
|
|
|
25
32
|
%Q{#{key}="#{value}"}
|
|
26
33
|
end
|
|
27
34
|
|
|
35
|
+
# Format a boolean-style attribute.
|
|
36
|
+
# @parameter key [String | Symbol] The attribute name.
|
|
37
|
+
# @parameter strict [Boolean] Whether to include an explicit attribute value.
|
|
38
|
+
# @returns [String] The formatted attribute.
|
|
28
39
|
def self.to_simple_attribute(key, strict)
|
|
29
40
|
strict ? %Q{#{key}="#{key}"} : key.to_s
|
|
30
41
|
end
|
|
31
42
|
|
|
43
|
+
# Convert a separated identifier to a human-readable title.
|
|
44
|
+
# @parameter string [String] The identifier to transform.
|
|
45
|
+
# @returns [String] The title-cased string.
|
|
32
46
|
def self.to_title(string)
|
|
33
47
|
string = string.gsub(/(^|[ \-_])(.)/){" " + $2.upcase}
|
|
34
48
|
string.strip!
|
|
@@ -36,6 +50,9 @@ module XRB
|
|
|
36
50
|
return string
|
|
37
51
|
end
|
|
38
52
|
|
|
53
|
+
# Convert a constant-like name to a snake-case identifier.
|
|
54
|
+
# @parameter string [String] The name to transform.
|
|
55
|
+
# @returns [String] The snake-case identifier.
|
|
39
56
|
def self.to_snake(string)
|
|
40
57
|
string = string.gsub("::", "")
|
|
41
58
|
string.gsub!(/([A-Z]+)/){"_" + $1.downcase}
|
data/lib/xrb/tag.rb
CHANGED
|
@@ -8,6 +8,9 @@ require_relative "markup"
|
|
|
8
8
|
module XRB
|
|
9
9
|
# This represents an individual SGML tag, e.g. <a>, </a> or <a />, with attributes. Attribute values must be escaped.
|
|
10
10
|
Tag = Struct.new(:name, :closed, :attributes) do
|
|
11
|
+
# Split a qualified tag name into its namespace and local name.
|
|
12
|
+
# @parameter qualified_name [String] The potentially namespaced tag name.
|
|
13
|
+
# @returns [Array(String | Nil)] The namespace and local name.
|
|
11
14
|
def self.split(qualified_name)
|
|
12
15
|
if i = qualified_name.index(":")
|
|
13
16
|
return qualified_name.slice(0...i), qualified_name.slice(i+1..-1)
|
|
@@ -16,38 +19,63 @@ module XRB
|
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
|
|
22
|
+
# Create a self-closing tag.
|
|
23
|
+
# @parameter name [String] The tag name.
|
|
24
|
+
# @parameter attributes [Hash] The tag attributes.
|
|
25
|
+
# @returns [Tag] The self-closing tag.
|
|
19
26
|
def self.closed(name, attributes = {})
|
|
20
27
|
self.new(name, true, attributes)
|
|
21
28
|
end
|
|
22
29
|
|
|
30
|
+
# Create a tag with separate opening and closing markup.
|
|
31
|
+
# @parameter name [String] The tag name.
|
|
32
|
+
# @parameter attributes [Hash] The tag attributes.
|
|
33
|
+
# @returns [Tag] The opened tag.
|
|
23
34
|
def self.opened(name, attributes = {})
|
|
24
35
|
self.new(name, false, attributes)
|
|
25
36
|
end
|
|
26
37
|
|
|
38
|
+
# Append the tag to an output buffer.
|
|
39
|
+
# @parameter output [Interface(:<<)] The output buffer.
|
|
40
|
+
# @returns [Nil] This method always returns `nil`.
|
|
27
41
|
def append_markup(output)
|
|
28
42
|
self.write(output)
|
|
29
43
|
end
|
|
30
44
|
|
|
45
|
+
# Append the tag using a markup builder.
|
|
46
|
+
# @parameter builder [Builder] The builder receiving the tag.
|
|
47
|
+
# @returns [Nil] This method always returns `nil`.
|
|
31
48
|
def build_markup(builder)
|
|
32
49
|
self.append_markup(builder.output)
|
|
33
50
|
end
|
|
34
51
|
|
|
52
|
+
# Fetch an attribute value by key.
|
|
53
|
+
# @parameter key [Object] The attribute key.
|
|
54
|
+
# @returns [Object | Nil] The attribute value.
|
|
35
55
|
def [] key
|
|
36
56
|
attributes[key]
|
|
37
57
|
end
|
|
38
58
|
|
|
39
59
|
alias to_hash attributes
|
|
40
60
|
|
|
61
|
+
# Format the tag and optional content as markup.
|
|
62
|
+
# @parameter content [Object | Nil] The optional tag content.
|
|
63
|
+
# @returns [String] The formatted tag markup.
|
|
41
64
|
def to_s(content = nil)
|
|
42
65
|
self.class.format_tag(name, attributes, content || !closed)
|
|
43
66
|
end
|
|
44
67
|
|
|
45
68
|
alias to_str to_s
|
|
46
69
|
|
|
70
|
+
# Determine whether the tag is self-closing.
|
|
71
|
+
# @returns [Boolean] Whether the tag is self-closing.
|
|
47
72
|
def self_closed?
|
|
48
73
|
closed
|
|
49
74
|
end
|
|
50
75
|
|
|
76
|
+
# Write the opening or self-closing tag to a buffer.
|
|
77
|
+
# @parameter buffer [Interface(:<<)] The output buffer.
|
|
78
|
+
# @returns [Object] The output buffer.
|
|
51
79
|
def write_opening_tag(buffer)
|
|
52
80
|
buffer << "<" << name
|
|
53
81
|
|
|
@@ -60,14 +88,26 @@ module XRB
|
|
|
60
88
|
end
|
|
61
89
|
end
|
|
62
90
|
|
|
91
|
+
# Write the closing tag to a buffer.
|
|
92
|
+
# @parameter buffer [Interface(:<<)] The output buffer.
|
|
93
|
+
# @returns [Object] The output buffer.
|
|
63
94
|
def write_closing_tag(buffer)
|
|
64
95
|
buffer << "</" << name << ">"
|
|
65
96
|
end
|
|
66
97
|
|
|
98
|
+
# Write the complete tag and optional content to a buffer.
|
|
99
|
+
# @parameter buffer [Interface(:<<)] The output buffer.
|
|
100
|
+
# @parameter content [Object | Nil] The optional tag content.
|
|
101
|
+
# @returns [Nil] This method always returns `nil`.
|
|
67
102
|
def write(buffer, content = nil)
|
|
68
103
|
self.class.append_tag(buffer, name, attributes, content || !closed)
|
|
69
104
|
end
|
|
70
105
|
|
|
106
|
+
# Format a tag as a new string.
|
|
107
|
+
# @parameter name [String] The tag name.
|
|
108
|
+
# @parameter attributes [Hash] The tag attributes.
|
|
109
|
+
# @parameter content [Object | Boolean | Nil] The tag content, `true` for empty content, or `nil` for a self-closing tag.
|
|
110
|
+
# @returns [String] The formatted tag markup.
|
|
71
111
|
def self.format_tag(name, attributes, content)
|
|
72
112
|
buffer = String.new.force_encoding(name.encoding)
|
|
73
113
|
|
|
@@ -76,6 +116,12 @@ module XRB
|
|
|
76
116
|
return buffer
|
|
77
117
|
end
|
|
78
118
|
|
|
119
|
+
# Append a complete tag to an output buffer.
|
|
120
|
+
# @parameter buffer [Interface(:<<)] The output buffer.
|
|
121
|
+
# @parameter name [String] The tag name.
|
|
122
|
+
# @parameter attributes [Hash] The tag attributes.
|
|
123
|
+
# @parameter content [Object | Boolean | Nil] The tag content, `true` for empty content, or `nil` for a self-closing tag.
|
|
124
|
+
# @returns [Nil] This method always returns `nil`.
|
|
79
125
|
def self.append_tag(buffer, name, attributes, content)
|
|
80
126
|
buffer << "<" << name.to_s
|
|
81
127
|
|
data/lib/xrb/template.rb
CHANGED
|
@@ -16,6 +16,11 @@ module XRB
|
|
|
16
16
|
BINDING = binding
|
|
17
17
|
|
|
18
18
|
class Builder
|
|
19
|
+
# Capture markup generated by a block using this builder as its output.
|
|
20
|
+
# @parameter arguments [Array] The arguments passed to the block.
|
|
21
|
+
# @yields {|*arguments| ...} The block whose generated markup will be captured.
|
|
22
|
+
# @parameter arguments [Array] The arguments supplied to {capture}.
|
|
23
|
+
# @returns [String] The captured markup.
|
|
19
24
|
def capture(*arguments, &block)
|
|
20
25
|
Template.capture(*arguments, output: self, &block)
|
|
21
26
|
end
|
|
@@ -46,7 +51,10 @@ module XRB
|
|
|
46
51
|
binding.local_variable_get(OUT)
|
|
47
52
|
end
|
|
48
53
|
|
|
54
|
+
# Assembles parser events into executable Ruby source code for a template.
|
|
49
55
|
class Assembler
|
|
56
|
+
# Initialize an empty template assembler.
|
|
57
|
+
# @parameter encoding [Encoding] The encoding of the generated source code.
|
|
50
58
|
def initialize(encoding: Encoding::UTF_8)
|
|
51
59
|
@code = String.new.force_encoding(encoding)
|
|
52
60
|
end
|
|
@@ -60,7 +68,7 @@ module XRB
|
|
|
60
68
|
text = text.gsub("'", "\\\\'")
|
|
61
69
|
@code << "#{OUT}.raw('#{text}');"
|
|
62
70
|
end
|
|
63
|
-
|
|
71
|
+
|
|
64
72
|
# Output a ruby expression (or part of).
|
|
65
73
|
def instruction(text, postfix = nil)
|
|
66
74
|
@code << text << (postfix || ";")
|
|
@@ -93,6 +101,8 @@ module XRB
|
|
|
93
101
|
@compiled = nil
|
|
94
102
|
end
|
|
95
103
|
|
|
104
|
+
# Compile and freeze the template.
|
|
105
|
+
# @returns [Template] The frozen template.
|
|
96
106
|
def freeze
|
|
97
107
|
return self if frozen?
|
|
98
108
|
|
data/lib/xrb/version.rb
CHANGED
data/lib/xrb.rb
CHANGED
data/readme.md
CHANGED
|
@@ -46,6 +46,14 @@ Please see the [project documentation](https://socketry.github.io/xrb/) for more
|
|
|
46
46
|
|
|
47
47
|
- [Markup Parser](https://socketry.github.io/xrb/guides/markup-parser/index) - This guide explains how to parse and manipulate Markup using the XRB templating system.
|
|
48
48
|
|
|
49
|
+
## Releases
|
|
50
|
+
|
|
51
|
+
Please see the [project releases](https://socketry.github.io/xrb/releases/index) for all releases.
|
|
52
|
+
|
|
53
|
+
### v0.12.0
|
|
54
|
+
|
|
55
|
+
- [URL Helpers](https://socketry.github.io/xrb/releases/index#url-helpers)
|
|
56
|
+
|
|
49
57
|
## See Also
|
|
50
58
|
|
|
51
59
|
- [xrb-rails](https://github.com/socketry/xrb-rails) - A Rails ActionView integration for rendering XRB templates.
|
|
@@ -57,11 +65,27 @@ Please see the [project documentation](https://socketry.github.io/xrb/) for more
|
|
|
57
65
|
|
|
58
66
|
We welcome contributions to this project.
|
|
59
67
|
|
|
60
|
-
1. Fork
|
|
68
|
+
1. Fork the repository.
|
|
61
69
|
2. Create your feature branch (`git checkout -b my-new-feature`).
|
|
62
|
-
3. Commit your changes (`git commit -am 'Add some feature'`).
|
|
70
|
+
3. Commit your changes (`git commit -am 'Add some feature.'`).
|
|
63
71
|
4. Push to the branch (`git push origin my-new-feature`).
|
|
64
|
-
5. Create new
|
|
72
|
+
5. Create a new pull request.
|
|
73
|
+
|
|
74
|
+
### Running Tests
|
|
75
|
+
|
|
76
|
+
To run the test suite:
|
|
77
|
+
|
|
78
|
+
``` bash
|
|
79
|
+
$ bundle exec sus
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Making Releases
|
|
83
|
+
|
|
84
|
+
To make a new release:
|
|
85
|
+
|
|
86
|
+
``` bash
|
|
87
|
+
$ bundle exec bake gem:release:patch # or minor or major
|
|
88
|
+
```
|
|
65
89
|
|
|
66
90
|
### Developer Certificate of Origin
|
|
67
91
|
|
data/releases.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
## v0.12.0
|
|
4
|
+
|
|
5
|
+
### URL Helpers
|
|
6
|
+
|
|
7
|
+
**Breaking:** XRB no longer provides the `XRB::Reference` and `XRB::URI` classes or their factory methods. Use `Protocol::URL::Reference` from the `protocol-url` gem instead.
|
|
8
|
+
|
|
9
|
+
Add `gem "protocol-url"` to your application dependencies and replace `require "xrb/reference"` or `require "xrb/uri"` with `require "protocol/url"`.
|
|
10
|
+
|
|
11
|
+
Replace `XRB::Reference(path, **parameters)` with:
|
|
12
|
+
|
|
13
|
+
``` ruby
|
|
14
|
+
reference = Protocol::URL::Reference.parse(path)
|
|
15
|
+
reference.parse_query!.update(parameters)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This preserves the previous behavior of parsing existing query parameters before updating them with the supplied parameters.
|
|
19
|
+
|
|
20
|
+
Replace direct `XRB::Reference.new(path, parameters, fragment: fragment)` calls with `Protocol::URL::Reference.new(path, nil, fragment, parameters)`.
|
|
21
|
+
|
|
22
|
+
Replace `XRB::URI(path, parameters)` with `Protocol::URL::Reference.parse(path, parameters)`. This preserves an existing query string and appends the supplied parameters.
|
|
23
|
+
|
|
24
|
+
Replace direct `XRB::URI.new(path, query, fragment, parameters)` calls with `Protocol::URL::Reference.new(path, query, fragment, parameters)`.
|
|
25
|
+
|
|
26
|
+
`protocol-url` treats path strings as already encoded URL paths. When starting with decoded path components, encode them explicitly with `Protocol::URL::Path.for(components)`.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xrb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -80,22 +80,22 @@ files:
|
|
|
80
80
|
- lib/xrb/native.rb
|
|
81
81
|
- lib/xrb/parsers.rb
|
|
82
82
|
- lib/xrb/query.rb
|
|
83
|
-
- lib/xrb/reference.rb
|
|
84
83
|
- lib/xrb/strings.rb
|
|
85
84
|
- lib/xrb/tag.rb
|
|
86
85
|
- lib/xrb/template.rb
|
|
87
|
-
- lib/xrb/uri.rb
|
|
88
86
|
- lib/xrb/version.rb
|
|
89
87
|
- license.md
|
|
90
88
|
- notes.md
|
|
91
89
|
- readme.md
|
|
92
|
-
|
|
90
|
+
- releases.md
|
|
91
|
+
homepage: https://github.com/socketry/xrb
|
|
93
92
|
licenses:
|
|
94
93
|
- MIT
|
|
95
94
|
metadata:
|
|
95
|
+
bug_tracker_uri: https://github.com/socketry/xrb/issues
|
|
96
96
|
documentation_uri: https://socketry.github.io/xrb/
|
|
97
97
|
funding_uri: https://github.com/sponsors/ioquatix
|
|
98
|
-
source_code_uri: https://github.com/
|
|
98
|
+
source_code_uri: https://github.com/socketry/xrb.git
|
|
99
99
|
rdoc_options: []
|
|
100
100
|
require_paths:
|
|
101
101
|
- lib
|
|
@@ -103,14 +103,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
103
103
|
requirements:
|
|
104
104
|
- - ">="
|
|
105
105
|
- !ruby/object:Gem::Version
|
|
106
|
-
version: '3.
|
|
106
|
+
version: '3.3'
|
|
107
107
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
108
|
requirements:
|
|
109
109
|
- - ">="
|
|
110
110
|
- !ruby/object:Gem::Version
|
|
111
111
|
version: '0'
|
|
112
112
|
requirements: []
|
|
113
|
-
rubygems_version:
|
|
113
|
+
rubygems_version: 4.0.10
|
|
114
114
|
specification_version: 4
|
|
115
115
|
summary: A fast native templating system that compiles directly to Ruby code.
|
|
116
116
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|
data/lib/xrb/reference.rb
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-2024, by Samuel Williams.
|
|
5
|
-
|
|
6
|
-
require_relative "query"
|
|
7
|
-
|
|
8
|
-
module XRB
|
|
9
|
-
class Reference
|
|
10
|
-
def initialize(path, query = {}, fragment: nil)
|
|
11
|
-
@path = path.to_s
|
|
12
|
-
@query = query
|
|
13
|
-
@fragment = fragment
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# The path component of the URI, e.g. /foo/bar/index.html
|
|
17
|
-
attr :path
|
|
18
|
-
|
|
19
|
-
# The query parameters.
|
|
20
|
-
attr :query
|
|
21
|
-
|
|
22
|
-
# A fragment identifier, the part after the '#'
|
|
23
|
-
attr :fragment
|
|
24
|
-
|
|
25
|
-
def append(buffer)
|
|
26
|
-
buffer << escape_path(@path)
|
|
27
|
-
|
|
28
|
-
unless @query.empty?
|
|
29
|
-
buffer << "?" << query_string
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
if @fragment
|
|
33
|
-
buffer << "#" << escape(@fragment)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
return buffer
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def to_str
|
|
40
|
-
append(String.new)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
alias to_s to_str
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
# According to https://tools.ietf.org/html/rfc3986#section-3.3, we escape non-pchar.
|
|
48
|
-
NON_PCHAR = /([^
|
|
49
|
-
a-zA-Z0-9
|
|
50
|
-
\-\._~
|
|
51
|
-
!\$&'\(\)\*\+,;=
|
|
52
|
-
:@\/
|
|
53
|
-
]+)/x.freeze
|
|
54
|
-
|
|
55
|
-
def escape_path(path)
|
|
56
|
-
encoding = path.encoding
|
|
57
|
-
path.b.gsub(NON_PCHAR) do |m|
|
|
58
|
-
"%" + m.unpack("H2" * m.bytesize).join("%").upcase
|
|
59
|
-
end.force_encoding(encoding)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Escapes a generic string, using percent encoding.
|
|
63
|
-
def escape(string)
|
|
64
|
-
encoding = string.encoding
|
|
65
|
-
string.b.gsub(/([^a-zA-Z0-9_.\-]+)/) do |m|
|
|
66
|
-
"%" + m.unpack("H2" * m.bytesize).join("%").upcase
|
|
67
|
-
end.force_encoding(encoding)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def query_string
|
|
71
|
-
build_nested_query(@query)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def build_nested_query(value, prefix = nil)
|
|
75
|
-
case value
|
|
76
|
-
when Array
|
|
77
|
-
value.map { |v|
|
|
78
|
-
build_nested_query(v, "#{prefix}[]")
|
|
79
|
-
}.join("&")
|
|
80
|
-
when Hash
|
|
81
|
-
value.map { |k, v|
|
|
82
|
-
build_nested_query(v, prefix ? "#{prefix}[#{escape(k.to_s)}]" : escape(k.to_s))
|
|
83
|
-
}.reject(&:empty?).join("&")
|
|
84
|
-
when nil
|
|
85
|
-
prefix
|
|
86
|
-
else
|
|
87
|
-
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
|
88
|
-
"#{prefix}=#{escape(value.to_s)}"
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Generate a URI from a path and user parameters. The path may contain a `#fragment` or `?query=parameters`.
|
|
94
|
-
def self.Reference(path = "", **parameters)
|
|
95
|
-
base, fragment = path.split("#", 2)
|
|
96
|
-
path, query_string = base.split("?", 2)
|
|
97
|
-
|
|
98
|
-
if query_string
|
|
99
|
-
query = Query.parse(Buffer.new(query_string))
|
|
100
|
-
else
|
|
101
|
-
query = {}
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
query.update(parameters)
|
|
105
|
-
|
|
106
|
-
Reference.new(path, query, fragment: fragment)
|
|
107
|
-
end
|
|
108
|
-
end
|