optic14n 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +10 -0
- data/.github/pull_request_template.md +1 -0
- data/.github/workflows/autorelease.yml +10 -0
- data/.github/workflows/ci.yml +45 -0
- data/.govuk_dependabot_merger.yml +7 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -2
- data/CHANGELOG.md +17 -0
- data/Gemfile +1 -5
- data/README.md +4 -0
- data/Rakefile +8 -15
- data/lib/optic14n/canonicalized_urls.rb +6 -4
- data/lib/optic14n/version.rb +1 -1
- data/lib/optic14n.rb +8 -8
- data/lib/tasks/measure_reduction.rake +3 -3
- data/lib/uri/bluri.rb +41 -31
- data/lib/uri/query_hash.rb +7 -7
- data/optic14n.gemspec +18 -15
- data/spec/bluri_spec.rb +48 -48
- data/spec/c14n_spec.rb +126 -114
- data/spec/canonicalized_urls_spec.rb +29 -20
- data/spec/query_hash_spec.rb +22 -8
- data/spec/spec_helper.rb +1 -1
- data/spec/uri/query_hash_spec.rb +18 -7
- metadata +36 -17
- data/jenkins.sh +0 -10
- /data/{LICENSE.txt → LICENCE} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7479db1ec2e377b50be0f54167958b1d7f6a49d45d662b26c19c840a61dfbb3f
|
4
|
+
data.tar.gz: ec11d6132aeec024bc68bcf85876645ddd2a4aff726b52468f2e367a119587b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a334df2fd339fbb0ce41554ae781f0fe24ff303116551e69a341ce4279f6239461d57565dcf285c7731328ddf597ae0536214e334251074f08ad02729543db49
|
7
|
+
data.tar.gz: 57c8eb1545b3d422c1ac53599928d41b0536ea445d3c7fc1567caebe58c7bc315266454124c946aa119e498c604a7342fa7ea7b97ecb4e8daeb0ba0660073889
|
@@ -0,0 +1 @@
|
|
1
|
+
This repo is owned by the publishing platform team. Please let us know in #govuk-publishing-platform when you raise any PRs.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
on:
|
2
|
+
workflow_dispatch: {}
|
3
|
+
schedule:
|
4
|
+
- cron: '30 10 * * 1-5' # 10:30am UTC, Mon-Fri.
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
autorelease:
|
8
|
+
uses: alphagov/govuk-infrastructure/.github/workflows/autorelease-rubygem.yml@main
|
9
|
+
secrets:
|
10
|
+
GH_TOKEN: ${{ secrets.GOVUK_CI_GITHUB_API_TOKEN }}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
on: [push, pull_request]
|
2
|
+
|
3
|
+
jobs:
|
4
|
+
codeql-sast:
|
5
|
+
name: CodeQL SAST scan
|
6
|
+
uses: alphagov/govuk-infrastructure/.github/workflows/codeql-analysis.yml@main
|
7
|
+
permissions:
|
8
|
+
security-events: write
|
9
|
+
|
10
|
+
dependency-review:
|
11
|
+
name: Dependency Review scan
|
12
|
+
uses: alphagov/govuk-infrastructure/.github/workflows/dependency-review.yml@main
|
13
|
+
|
14
|
+
# This matrix job runs the test suite against multiple Ruby versions
|
15
|
+
test_matrix:
|
16
|
+
strategy:
|
17
|
+
fail-fast: false
|
18
|
+
matrix:
|
19
|
+
ruby: [3.1, 3.2, 3.3]
|
20
|
+
runs-on: ubuntu-latest
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v4
|
23
|
+
- uses: ruby/setup-ruby@v1
|
24
|
+
with:
|
25
|
+
ruby-version: ${{ matrix.ruby }}
|
26
|
+
bundler-cache: true
|
27
|
+
- run: bundle exec rake
|
28
|
+
|
29
|
+
# Branch protection rules cannot directly depend on status checks from matrix jobs.
|
30
|
+
# So instead we define `test` as a dummy job which only runs after the preceding `test_matrix` checks have passed.
|
31
|
+
# Solution inspired by: https://github.community/t/status-check-for-a-matrix-jobs/127354/3
|
32
|
+
test:
|
33
|
+
needs: test_matrix
|
34
|
+
runs-on: ubuntu-latest
|
35
|
+
steps:
|
36
|
+
- run: echo "All matrix tests have passed 🚀"
|
37
|
+
|
38
|
+
publish:
|
39
|
+
needs: test
|
40
|
+
if: ${{ github.ref == 'refs/heads/main' }}
|
41
|
+
permissions:
|
42
|
+
contents: write
|
43
|
+
uses: alphagov/govuk-infrastructure/.github/workflows/publish-rubygem.yml@main
|
44
|
+
secrets:
|
45
|
+
GEM_HOST_API_KEY: ${{ secrets.ALPHAGOV_RUBYGEMS_API_KEY }}
|
data/.rubocop.yml
ADDED
data/.ruby-version
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
3.1.4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
- We use the [GOV.UK versioning guidelines](https://docs.publishing.service.gov.uk/manual/publishing-a-ruby-gem.html#versioning).
|
4
|
+
- Mark breaking changes with `BREAKING:`. Be sure to include instructions on how applications should be upgraded.
|
5
|
+
- Don't include changes that are purely internal. The CHANGELOG should be a
|
6
|
+
useful summary for people upgrading their application, not a replication
|
7
|
+
of the commit log.
|
8
|
+
|
9
|
+
## Unreleased
|
10
|
+
|
11
|
+
## 3.0.0
|
12
|
+
|
13
|
+
[Full diff](https://github.com/alphagov/optic14n/compare/v2.1.0...v3.0.0)
|
14
|
+
|
15
|
+
- BREAKING: Bump minimum Ruby version from 2.6.5 to 3.1.4
|
16
|
+
- Add support for Ruby 3.3
|
17
|
+
- BREAKING: Bump minimum rubocop-govuk version from 1.0.0 to 5.0.2
|
data/Gemfile
CHANGED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,19 +1,12 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
require 'gem_publisher'
|
10
|
-
desc 'Publish gem to Rubygems'
|
11
|
-
task :publish_gem do
|
12
|
-
gem = GemPublisher.publish_if_updated('optic14n.gemspec', :rubygems)
|
13
|
-
puts "Published #{gem}" if gem
|
14
|
-
end
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
require "rubocop/rake_task"
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
require "optic14n"
|
7
|
+
Dir.glob("lib/tasks/*.rake").each { |r| import r }
|
15
8
|
|
9
|
+
RuboCop::RakeTask.new
|
16
10
|
RSpec::Core::RakeTask.new(:spec)
|
17
11
|
|
18
|
-
task default:
|
19
|
-
task test: :spec
|
12
|
+
task default: %i[rubocop spec]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "set"
|
2
|
+
|
1
3
|
module Optic14n
|
2
4
|
##
|
3
5
|
# Canonicalizes a set of URLs
|
@@ -21,7 +23,7 @@ module Optic14n
|
|
21
23
|
@urls.each do |url|
|
22
24
|
begin
|
23
25
|
@output_set.add(BLURI(url).canonicalize!(@options))
|
24
|
-
rescue
|
26
|
+
rescue StandardError => e
|
25
27
|
failures[url] = e
|
26
28
|
end
|
27
29
|
@seen += 1
|
@@ -29,7 +31,7 @@ module Optic14n
|
|
29
31
|
end
|
30
32
|
|
31
33
|
def write(filename)
|
32
|
-
File.open(filename,
|
34
|
+
File.open(filename, "w") do |file|
|
33
35
|
@output_set.each do |url|
|
34
36
|
file.puts url
|
35
37
|
end
|
@@ -39,7 +41,7 @@ module Optic14n
|
|
39
41
|
##
|
40
42
|
# Canonicalize given urls. +options+ will be passed to +BLURI.parse+
|
41
43
|
def self.from_urls(urls, options = {})
|
42
|
-
CanonicalizedUrls.new(urls, options).tap
|
44
|
+
CanonicalizedUrls.new(urls, options).tap(&:canonicalize!)
|
43
45
|
end
|
44
46
|
end
|
45
|
-
end
|
47
|
+
end
|
data/lib/optic14n/version.rb
CHANGED
data/lib/optic14n.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require "optic14n/version"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
3
|
+
require "uri"
|
4
|
+
require "addressable/uri"
|
5
|
+
require "cgi"
|
6
|
+
require "forwardable"
|
7
|
+
require "uri/query_hash"
|
8
|
+
require "uri/bluri"
|
9
9
|
|
10
|
-
require
|
10
|
+
require "optic14n/canonicalized_urls"
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require "set"
|
2
2
|
|
3
3
|
namespace :opt do
|
4
|
-
desc
|
4
|
+
desc "Measure reduction from canonicalisation"
|
5
5
|
task :measure, [:filename, :output_file] do |_, args|
|
6
6
|
filename = args[:filename]
|
7
7
|
output_file = args[:output_file]
|
@@ -12,4 +12,4 @@ namespace :opt do
|
|
12
12
|
puts "#{urls.seen} urls seen, #{urls.size} after canonicalisation"
|
13
13
|
end
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end
|
data/lib/uri/bluri.rb
CHANGED
@@ -1,39 +1,44 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module URI
|
4
2
|
##
|
5
3
|
# A URI class with a bit extra for canonicalising query strings
|
6
4
|
#
|
7
5
|
class BLURI < URI::HTTP
|
8
6
|
PATH_ESCAPE_MAPPINGS = {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
'"' =>
|
13
|
-
"'" =>
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
7
|
+
"[" => "%5b",
|
8
|
+
"]" => "%5d",
|
9
|
+
"," => "%2c",
|
10
|
+
'"' => "%22",
|
11
|
+
"'" => "%27",
|
12
|
+
"|" => "%7c",
|
13
|
+
"!" => "%21",
|
14
|
+
"£" => "%c2%a3",
|
15
|
+
}.freeze
|
18
16
|
|
19
17
|
PATH_UNESCAPE_MAPPINGS = {
|
20
|
-
|
21
|
-
|
22
|
-
}
|
18
|
+
"%7e" => "~",
|
19
|
+
"%21" => "!",
|
20
|
+
}.freeze
|
23
21
|
|
24
|
-
REQUIRE_REGEX_ESCAPE =
|
22
|
+
REQUIRE_REGEX_ESCAPE = [".", "|", "(", ")", "[", "]", "{", "}", "+", " ^", "$", "*", "?"] & PATH_ESCAPE_MAPPINGS.keys
|
25
23
|
|
26
24
|
extend Forwardable
|
27
25
|
|
28
26
|
def_delegators :@uri, :scheme, :path, :host, :host=, :query, :fragment, :to_s
|
29
27
|
|
30
|
-
def initialize(uri_str)
|
28
|
+
def initialize(uri_str) # rubocop:disable Lint/MissingSuper - This class seems a reimplementation rather than an ancestor
|
31
29
|
@uri = ::Addressable::URI.parse(uri_str)
|
32
|
-
|
30
|
+
|
31
|
+
raise URI::InvalidURIError, "'#{uri_str}' not a valid URI" unless valid_uri?
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_uri?
|
35
|
+
return unless @uri
|
36
|
+
|
37
|
+
%w[http https mailto].include?(@uri.scheme)
|
33
38
|
end
|
34
39
|
|
35
40
|
def query_hash
|
36
|
-
@query_hash ||= CGI
|
41
|
+
@query_hash ||= CGI.parse(query || "").tap do |query_hash|
|
37
42
|
# By default, CGI::parse produces lots of arrays. Usually they have a single element
|
38
43
|
# in them. That's correct but not terribly usable. Fix it here.
|
39
44
|
query_hash.each_pair { |k, v| query_hash[k] = v[0] if v.length == 1 }
|
@@ -43,31 +48,31 @@ module URI
|
|
43
48
|
|
44
49
|
def query_hash=(value)
|
45
50
|
@query_hash = value
|
46
|
-
@uri.query = @query_hash.to_s ==
|
51
|
+
@uri.query = @query_hash.to_s == "" ? nil : @query_hash.to_s
|
47
52
|
end
|
48
53
|
|
49
54
|
def query=(query_str)
|
50
55
|
@query_hash = nil
|
51
|
-
@uri.query = query_str ==
|
56
|
+
@uri.query = query_str == "" ? nil : query_str
|
52
57
|
end
|
53
58
|
|
54
59
|
def self.parse(uri_str)
|
55
60
|
# Deal with known URI spec breaks - leading/trailing spaces and unencoded entities
|
56
61
|
if uri_str.is_a? String
|
57
|
-
uri_str = uri_str.strip.downcase.gsub(
|
58
|
-
uri_str.gsub!(
|
62
|
+
uri_str = uri_str.strip.downcase.gsub(" ", "%20")
|
63
|
+
uri_str.gsub!("&", "%26") if uri_str =~ /^mailto:.*&.*/
|
59
64
|
end
|
60
65
|
BLURI.new(uri_str)
|
61
66
|
end
|
62
67
|
|
63
68
|
def has_query?
|
64
|
-
%w
|
69
|
+
%w[http https].include?(@uri.scheme) && query
|
65
70
|
end
|
66
71
|
|
67
72
|
def canonicalize!(options = {})
|
68
|
-
@uri.scheme =
|
73
|
+
@uri.scheme = "http" if @uri.scheme == "https"
|
69
74
|
|
70
|
-
@uri.path = @uri.path.sub(/\/*$/,
|
75
|
+
@uri.path = @uri.path.sub(/\/*$/, "") if @uri.path =~ /^*\/$/
|
71
76
|
@uri.path.gsub!(BLURI.path_escape_char_regex, PATH_ESCAPE_MAPPINGS)
|
72
77
|
@uri.path.gsub!(BLURI.path_unescape_code_regex, PATH_UNESCAPE_MAPPINGS)
|
73
78
|
|
@@ -82,7 +87,7 @@ module URI
|
|
82
87
|
allowed_keys = [options[:allow_query]].flatten.compact.map(&:to_s) unless allow_all
|
83
88
|
|
84
89
|
query_hash.keep_if do |k, _|
|
85
|
-
allow_all ||
|
90
|
+
allow_all || allowed_keys.include?(k.to_s)
|
86
91
|
end
|
87
92
|
|
88
93
|
self.query_hash = QueryHash[query_hash.sort_by { |k, _| k }]
|
@@ -91,26 +96,31 @@ module URI
|
|
91
96
|
##
|
92
97
|
# Generate a regex which matches all characters in PATH_ESCAPE_MAPPINGS
|
93
98
|
def self.path_escape_char_regex
|
94
|
-
@path_escape_char_regex ||=
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
@path_escape_char_regex ||= begin
|
100
|
+
escaped_characters_for_regex = PATH_ESCAPE_MAPPINGS.keys.map do |char|
|
101
|
+
REQUIRE_REGEX_ESCAPE.include?(char) ? "\\#{char}" : char
|
102
|
+
end
|
103
|
+
|
104
|
+
Regexp.new("[#{escaped_characters_for_regex.join}]")
|
105
|
+
end
|
98
106
|
end
|
99
107
|
|
100
108
|
##
|
101
109
|
# Generate a regex which matches all escape sequences in PATH_UNESCAPE_MAPPINGS
|
102
110
|
def self.path_unescape_code_regex
|
103
111
|
@path_unescape_code_regex ||= Regexp.new(
|
104
|
-
PATH_UNESCAPE_MAPPINGS.keys.map { |code| "(?:#{code})" }.join(
|
112
|
+
PATH_UNESCAPE_MAPPINGS.keys.map { |code| "(?:#{code})" }.join("|"),
|
105
113
|
)
|
106
114
|
end
|
107
115
|
end
|
108
116
|
end
|
109
117
|
|
110
118
|
module Kernel
|
119
|
+
# rubocop:disable Naming/MethodName
|
111
120
|
def BLURI(uri_str)
|
112
121
|
::URI::BLURI.parse(uri_str)
|
113
122
|
end
|
123
|
+
# rubocop:enable Naming/MethodName
|
114
124
|
|
115
125
|
module_function :BLURI
|
116
126
|
end
|
data/lib/uri/query_hash.rb
CHANGED
@@ -4,12 +4,12 @@ module URI
|
|
4
4
|
module QueryHash
|
5
5
|
def [](key)
|
6
6
|
item = super key
|
7
|
-
item = super(key.to_s) if item.nil? || item.
|
8
|
-
item.
|
7
|
+
item = super(key.to_s) if item.nil? || item.empty?
|
8
|
+
item.instance_of?(Array) && item.empty? ? nil : item
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_s
|
12
|
-
keys.map { |key| render_value(key, self[key]) }.join(
|
12
|
+
keys.map { |key| render_value(key, self[key]) }.join("&")
|
13
13
|
end
|
14
14
|
|
15
15
|
##
|
@@ -20,13 +20,13 @@ module URI
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
private
|
24
24
|
|
25
25
|
def render_value(key, value)
|
26
26
|
case value
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
when nil then key
|
28
|
+
when Array then value.map { |el| render_value(key, el) }.join("&")
|
29
|
+
else URI.encode_www_form_component(key) << "=" << URI.encode_www_form_component(value)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/optic14n.gemspec
CHANGED
@@ -1,25 +1,28 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path(
|
1
|
+
require "English"
|
2
|
+
lib = File.expand_path("lib", __dir__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "optic14n/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = "optic14n"
|
8
8
|
spec.version = Optic14n::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email =
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
9
|
+
spec.authors = ["GOV.UK Dev"]
|
10
|
+
spec.email = ["govuk-dev@digital.cabinet-office.gov.uk"]
|
11
|
+
spec.description = "Canonicalises URLs."
|
12
|
+
spec.summary = "Specifically, HTTP URLs, for a limited purpose"
|
13
|
+
spec.homepage = "https://github.com/alphagov/optic14n"
|
14
|
+
spec.license = "MIT"
|
15
15
|
|
16
|
-
spec.
|
16
|
+
spec.required_ruby_version = ">= 3.1.4"
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = %w
|
21
|
+
spec.require_paths = %w[lib]
|
20
22
|
|
21
|
-
spec.add_dependency
|
23
|
+
spec.add_dependency "addressable", "~> 2.7"
|
22
24
|
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "rubocop-govuk", "5.0.2"
|
25
28
|
end
|
data/spec/bluri_spec.rb
CHANGED
@@ -1,88 +1,88 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe URI::BLURI do
|
4
|
-
it
|
5
|
-
bluri = BLURI(
|
6
|
-
bluri.
|
4
|
+
it "should be an HTTP URI" do
|
5
|
+
bluri = BLURI("http://some.where.com")
|
6
|
+
expect(bluri).to be_a URI::HTTP
|
7
7
|
end
|
8
8
|
|
9
|
-
it
|
10
|
-
|
9
|
+
it "should not allow other schemes" do
|
10
|
+
expect { BLURI("ftp://foo") }.to raise_error(URI::InvalidURIError)
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
14
|
-
|
13
|
+
it "should not allow nil" do
|
14
|
+
expect { BLURI(nil) }.to raise_error(URI::InvalidURIError)
|
15
15
|
end
|
16
16
|
|
17
|
-
it
|
18
|
-
BLURI(
|
17
|
+
it "supports scheme" do
|
18
|
+
expect(BLURI("http://foo").scheme).to eq("http")
|
19
19
|
end
|
20
|
-
it
|
21
|
-
BLURI(
|
20
|
+
it "supports host" do
|
21
|
+
expect(BLURI("http://foo").host).to eq("foo")
|
22
22
|
end
|
23
|
-
it
|
24
|
-
BLURI(
|
23
|
+
it "supports path" do
|
24
|
+
expect(BLURI("http://foo/a/path").path).to eq("/a/path")
|
25
25
|
end
|
26
|
-
it
|
27
|
-
BLURI(
|
26
|
+
it "supports query" do
|
27
|
+
expect(BLURI("http://foo?to=you&you=foo").query).to eq("to=you&you=foo")
|
28
28
|
end
|
29
|
-
it
|
30
|
-
BLURI(
|
29
|
+
it "supports fragment" do
|
30
|
+
expect(BLURI("http://foo#fragment").fragment).to eq("fragment")
|
31
31
|
end
|
32
|
-
it
|
33
|
-
BLURI(
|
32
|
+
it "supports mailto:someone@somewhere" do
|
33
|
+
expect(BLURI("mailto:me@there.com").to_s).to eq("mailto:me@there.com")
|
34
34
|
end
|
35
|
-
it
|
36
|
-
BLURI(
|
35
|
+
it "corrects unencoded ampersands ins mailto" do # http://www.faqs.org/rfcs/rfc2368.html
|
36
|
+
expect(BLURI("mailto:fruit&veg.newcastle@rpa.gsi.gov.uk").to_s).to eq("mailto:fruit%26veg.newcastle@rpa.gsi.gov.uk")
|
37
37
|
end
|
38
|
-
it
|
39
|
-
BLURI(
|
38
|
+
it "corrects trailing spaces" do
|
39
|
+
expect(BLURI("http://www.newspapersoc.org.uk ").to_s).to eq("http://www.newspapersoc.org.uk")
|
40
40
|
end
|
41
|
-
it
|
42
|
-
BLURI(
|
41
|
+
it "corrects leading spaces" do
|
42
|
+
expect(BLURI(" http://www.newspapersoc.org.uk").to_s).to eq("http://www.newspapersoc.org.uk")
|
43
43
|
end
|
44
44
|
|
45
|
-
describe
|
46
|
-
context
|
45
|
+
describe "Query string parsing" do
|
46
|
+
context "the query string is of HTML-encoded form k=v&q=p" do
|
47
47
|
before do
|
48
|
-
@bluri = BLURI(
|
48
|
+
@bluri = BLURI("http://some.com/a/path?itemid=1&type=RESOURCE")
|
49
49
|
end
|
50
50
|
|
51
|
-
it
|
52
|
-
@bluri.query_hash[
|
51
|
+
it "indexes the query string" do
|
52
|
+
expect(@bluri.query_hash["itemid"]).to eq("1")
|
53
53
|
end
|
54
54
|
|
55
|
-
it
|
56
|
-
@bluri.query_hash[:itemid].
|
55
|
+
it "allows indexing by symbol" do
|
56
|
+
expect(@bluri.query_hash[:itemid]).to eq("1")
|
57
57
|
end
|
58
58
|
|
59
|
-
it
|
60
|
-
@bluri.query_hash[:eerie_flash].
|
59
|
+
it "shows nil for absent items" do
|
60
|
+
expect(@bluri.query_hash[:eerie_flash]).to eq(nil)
|
61
61
|
end
|
62
62
|
|
63
|
-
it
|
64
|
-
@bluri.query_hash[
|
63
|
+
it "indexes the second query string item" do
|
64
|
+
expect(@bluri.query_hash["type"]).to eq("resource")
|
65
65
|
end
|
66
66
|
|
67
|
-
it
|
68
|
-
@bluri.query =
|
69
|
-
@bluri.to_s.
|
67
|
+
it "allows setting of the query" do
|
68
|
+
@bluri.query = "furry=really"
|
69
|
+
expect(@bluri.to_s).to eq("http://some.com/a/path?furry=really")
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
context
|
73
|
+
context "the querystring is not an HTML-encoded thing" do
|
74
74
|
before do
|
75
|
-
@bluri = BLURI(
|
75
|
+
@bluri = BLURI("http://some.com/a/path?foo&bar")
|
76
76
|
end
|
77
77
|
|
78
|
-
it
|
79
|
-
@bluri.query.
|
78
|
+
it "retains the query string" do
|
79
|
+
expect(@bluri.query).to eq("foo&bar")
|
80
80
|
end
|
81
81
|
|
82
|
-
it
|
83
|
-
@bluri.query_hash[
|
84
|
-
@bluri.query_hash[
|
82
|
+
it "has a query hash with empty elements" do
|
83
|
+
expect(@bluri.query_hash["foo"]).to eq(nil)
|
84
|
+
expect(@bluri.query_hash["foo"]).to eq(nil)
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
88
|
-
end
|
88
|
+
end
|