link-header-parser 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 740f4f2172b36ed9b95799c1050809fdb0e2069c5c3dddcab82c3c295ca09c40
4
- data.tar.gz: 45942dc43d28bd7048da8ded2c4f6290c4d959940d8d15ab03efd293d84ee957
3
+ metadata.gz: f27e7cee875b551a9a48c7d37b498eeec2a5dae6a3cd3695dddd5bdb59c3ae5b
4
+ data.tar.gz: 59113fb0ff7c0ce6d82150b3143599a995841b114778f1cc63cd7625cb973c59
5
5
  SHA512:
6
- metadata.gz: c4d4b3b394a946ccdc13a0047b281ee37e13ce527acb344044068807d2742dc242a884875b30039c163384511f19bad8d65afd583fdcfefb7d6196a45f4cdd4a
7
- data.tar.gz: c990a1bcd6d4b0aa88d57c820876521b5111208b7b835859bd818acb81fe2f331eb379c59484631583be4a71528e8fdb894a7cc1add5aa4cc78545752c5a3531
6
+ metadata.gz: 5db8f753ed8a5562d212368f647cf1eb32e4b23dad387ed06f5d9d6083ec4cd706cfec35fe2116b1d41813599b560b60c7152b5a786eecee3bcbdc319aea5660
7
+ data.tar.gz: 77aa58a97d9f87789f03b5206a9878368446675c1b46c4184bd697cd55965811f4d797d2f11b572cf9dbf286a2680af9967feb9ac8a4a81a3090b3f22dcfbc03
data/.reek.yml CHANGED
@@ -1,6 +1,9 @@
1
1
  detectors:
2
2
  IrresponsibleModule:
3
3
  enabled: false
4
+ NestedIterators:
5
+ exclude:
6
+ - LinkHeaderParser::LinkHeadersCollection#group_by_relation_type
4
7
 
5
8
  exclude_paths:
6
9
  - vendor/
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.4.10
1
+ 2.5.8
data/.travis.yml CHANGED
@@ -1,7 +1,6 @@
1
1
  dist: bionic
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4
5
4
  - 2.5
6
5
  - 2.6
7
6
  - 2.7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0 / 2020-05-21
4
+
5
+ - **Breaking changes:** Rewrite gem code (5351010)
6
+ - `LinkHeaderParser.parse` returns `LinkHeadersCollection`
7
+ - New classes: `LinkHeadersCollection`, `LinkHeader`, and `LinkHeaderParameter`
8
+ - Renamed collection's `by_relation_type` method to `group_by_relation_type`
9
+ - **Breaking change:** Update project Ruby version to 2.5.8 and minimum Ruby version to 2.5 (05b2e82)
10
+ - Update inline documentation and refactor `ParsedHeader` and `ParsedHeaderCollection` classes (31ec43e)
11
+
3
12
  ## 1.0.0 / 2020-05-14
4
13
 
5
14
  - Update Absolutely dependency to 4.0 (4b78347)
@@ -8,11 +17,11 @@
8
17
 
9
18
  ## 0.3.0 / 2020-01-20
10
19
 
11
- - Expand supported Ruby versions to include 2.7 ([c55624a](https://github.com/jgarber623/link-header-parser-ruby/commit/c55624a)).
20
+ - Expand supported Ruby versions to include 2.7 (c55624a)
12
21
 
13
22
  ## 0.2.0 / 2019-07-02
14
23
 
15
- - Add support for the `anchor` parameter to `ParsedHeader` exposed via `context` and `context_uri` methods ([d2dff52](https://github.com/jgarber623/link-header-parser-ruby/commit/d2dff52)).
24
+ - Add support for the `anchor` parameter to `ParsedHeader` exposed via `context` and `context_uri` methods (d2dff52)
16
25
 
17
26
  ## 0.1.0 / 2019-06-06
18
27
 
data/CONTRIBUTING.md CHANGED
@@ -8,9 +8,9 @@ There are a couple ways you can help improve link-header-parser-ruby:
8
8
 
9
9
  ## Getting Started
10
10
 
11
- link-header-parser-ruby is developed using Ruby 2.4.10 and is additionally tested against Ruby 2.5, 2.6, and 2.7 using [Travis CI](https://travis-ci.com/jgarber623/link-header-parser-ruby).
11
+ link-header-parser-ruby is developed using Ruby 2.5.8 and is additionally tested against Ruby 2.6 and 2.7 using [Travis CI](https://travis-ci.com/jgarber623/link-header-parser-ruby).
12
12
 
13
- Before making changes to link-header-parser-ruby, you'll want to install Ruby 2.4.10. It's recommended that you use a Ruby version managment tool like [rbenv](https://github.com/rbenv/rbenv), [chruby](https://github.com/postmodern/chruby), or [rvm](https://github.com/rvm/rvm). Once you've installed Ruby 2.4.10 using your method of choice, install the project's gems by running:
13
+ Before making changes to link-header-parser-ruby, you'll want to install Ruby 2.5.8. It's recommended that you use a Ruby version managment tool like [rbenv](https://github.com/rbenv/rbenv), [chruby](https://github.com/postmodern/chruby), or [rvm](https://github.com/rvm/rvm). Once you've installed Ruby 2.5.8 using your method of choice, install the project's gems by running:
14
14
 
15
15
  ```sh
16
16
  bundle install
data/README.md CHANGED
@@ -11,9 +11,9 @@
11
11
 
12
12
  ## Getting Started
13
13
 
14
- Before installing and using link-header-parser-ruby, you'll want to have [Ruby](https://www.ruby-lang.org) 2.4 (or newer) installed. It's recommended that you use a Ruby version managment tool like [rbenv](https://github.com/rbenv/rbenv), [chruby](https://github.com/postmodern/chruby), or [rvm](https://github.com/rvm/rvm).
14
+ Before installing and using link-header-parser-ruby, you'll want to have [Ruby](https://www.ruby-lang.org) 2.5 (or newer) installed. It's recommended that you use a Ruby version managment tool like [rbenv](https://github.com/rbenv/rbenv), [chruby](https://github.com/postmodern/chruby), or [rvm](https://github.com/rvm/rvm).
15
15
 
16
- link-header-parser-ruby is developed using Ruby 2.4.10 and is additionally tested against Ruby 2.5, 2.6, and 2.7 using [Travis CI](https://travis-ci.com/jgarber623/link-header-parser-ruby).
16
+ link-header-parser-ruby is developed using Ruby 2.5.8 and is additionally tested against Ruby 2.6 and 2.7 using [Travis CI](https://travis-ci.com/jgarber623/link-header-parser-ruby).
17
17
 
18
18
  ## Installation
19
19
 
@@ -51,79 +51,98 @@ The `parse` method accepts two arguments:
51
51
  1. an `Array` of strings representing HTTP Link headers (e.g. `['</>; rel="home"', '</chapters/1>; anchor="#copyright"; rel="license"']`)
52
52
  1. a `String` representing the absolute URL of the resource providing the HTTP Link headers
53
53
 
54
- In the example above, `collection` is an instance of `ParsedHeaderCollection` which includes Ruby's [`Enumerable`](https://ruby-doc.org/core/Enumerable.html) mixin. This mixin allows for use of common methods like `each`, `first`/`last`, and `map`.
54
+ In the example above, `collection` is an instance of `LinkHeadersCollection` which includes Ruby's [`Enumerable`](https://ruby-doc.org/core/Enumerable.html) mixin. This mixin allows for use of common methods like `each`, `first`/`last`, and `map`.
55
55
 
56
56
  For example, you could retrieve an array of `target_uri`s:
57
57
 
58
58
  ```ruby
59
- puts collection.map(&:target_uri) # => ["https://assets.sixtwothree.org/", "https://fonts.googleapis.com/", "https://fonts.gstatic.com/", "https://sixtwothree.org/webmentions"]
59
+ puts collection.map(&:target_uri)
60
+ #=> ["https://assets.sixtwothree.org/", "https://fonts.googleapis.com/", "https://fonts.gstatic.com/", "https://sixtwothree.org/webmentions"]
60
61
  ```
61
62
 
62
- ### Working with a `ParsedHeaderCollection`
63
+ ### Working with a `LinkHeadersCollection`
63
64
 
64
- In addition to the included `Enumerable` methods, the following methods may be used to interact with a `ParsedHeaderCollection`:
65
+ In addition to the included `Enumerable` methods, the following methods may be used to interact with a `LinkHeadersCollection`:
65
66
 
66
67
  #### The `relation_types` Method
67
68
 
68
69
  ```ruby
69
- puts collection.relation_types # => ["preconnect", "webmention"]
70
+ puts collection.relation_types
71
+ #=> ["preconnect", "webmention"]
70
72
  ```
71
73
 
72
- #### The `by_relation_type` Method
74
+ #### The `group_by_relation_type` Method
73
75
 
74
- Using the `collection` from above, the `by_relation_type` method returns an `OpenStruct` with the following attributes:
76
+ Using the `collection` from above, the `group_by_relation_type` method returns a `Hash`:
75
77
 
76
78
  ```ruby
77
79
  {
78
80
  preconnect: [
79
- #<LinkHeaderParser::ParsedHeader @header="<https://assets.sixtwothree.org/>; rel=\"preconnect\"">,
80
- #<LinkHeaderParser::ParsedHeader @header="<https://fonts.googleapis.com/>; rel=\"preconnect\"">,
81
- #<LinkHeaderParser::ParsedHeader @header="<https://fonts.gstatic.com/>; rel=\"preconnect\"">
81
+ #<LinkHeaderParser::LinkHeader target_uri: "https://assets.sixtwothree.org/", relation_types: ["preconnect"]>,
82
+ #<LinkHeaderParser::LinkHeader target_uri: "https://fonts.googleapis.com/", relation_types: ["preconnect"]>,
83
+ #<LinkHeaderParser::LinkHeader target_uri: "https://fonts.gstatic.com/", relation_types: ["preconnect"]>
82
84
  ],
83
85
  webmention: [
84
- #<LinkHeaderParser::ParsedHeader @header="<https://sixtwothree.org/webmentions>; rel=\"webmention\"">
86
+ #<LinkHeaderParser::LinkHeader target_uri: "https://sixtwothree.org/webmentions", relation_types: ["webmention"]>
85
87
  ]
86
88
  }
87
89
  ```
88
90
 
89
- ### Working with a `ParsedHeader`
91
+ ### Working with a `LinkHeader`
90
92
 
91
- You may interact with one or more `ParsedHeader`s in a `ParsedHeaderCollection` using the methods outlined below. The naming conventions for these methods draws heavily on the terminology established in [RFC-5988](https://tools.ietf.org/html/rfc5988) and [RFC-8288](https://tools.ietf.org/html/rfc8288).
93
+ You may interact with one or more `LinkHeader`s in a `LinkHeadersCollection` using the methods outlined below. The naming conventions for these methods draws heavily on the terminology established in [RFC-5988](https://tools.ietf.org/html/rfc5988) and [RFC-8288](https://tools.ietf.org/html/rfc8288).
92
94
 
93
95
  #### Link Target ([§ 3.1](https://tools.ietf.org/html/rfc8288#section-3.1))
94
96
 
95
97
  ```ruby
96
- link_headers = ['</index.html>; rel="home"']
97
- parsed_header = LinkHeaderParser.parse(link_headers, base: 'https://example.com/').first
98
+ link_header = LinkHeaderParser.parse('</index.html>; rel="home"', base: 'https://example.com/').first
98
99
 
99
- parsed_header.target # => '/index.html'
100
- parsed_header.target_uri # => 'https://example.com/index.html'
100
+ link_header.target_string
101
+ #=> "/index.html"
102
+
103
+ link_header.target_uri
104
+ #=> "https://example.com/index.html"
101
105
  ```
102
106
 
103
- The `target` method returns a string of the value between the opening and closing angle brackets at the beginning of the Link header. The `target_uri` method returns a string representing the resolved URL.
107
+ The `target_string` method returns a string of the value between the opening and closing angle brackets at the beginning of the Link header. The `target_uri` method returns a string representing the resolved URL.
104
108
 
105
109
  #### Link Context ([§ 3.2](https://tools.ietf.org/html/rfc8288#section-3.2))
106
110
 
107
111
  ```ruby
108
- link_headers = ['<https://example.com/chapters/1>; anchor="#copyright"; rel="license"']
109
- parsed_header = LinkHeaderParser.parse(link_headers, base: 'https://example.com/').first
112
+ link_header = LinkHeaderParser.parse('</chapters/1>; anchor="#copyright"; rel="license"', base: 'https://example.com/').first
113
+
114
+ link_header.context_string
115
+ #=> "#copyright"
110
116
 
111
- parsed_header.context # => '#copyright'
112
- parsed_header.context_uri # => 'https://example.com/chapters/1#copyright'
117
+ link_header.context_uri
118
+ #=> "https://example.com/chapters/1#copyright"
113
119
  ```
114
120
 
115
- The `anchor` parameter's value may be a fragment identifier (e.g. `#foo`), a relative URL (e.g. `/foo`), or an absolute URL (e.g. `https://context.example.com`). The `context` method returns the `anchor` parameter's value (when present) and defaults to the `target` value.
121
+ The `anchor` parameter's value may be a fragment identifier (e.g. `#foo`), a relative URL (e.g. `/foo`), or an absolute URL (e.g. `https://context.example.com`). The `context_string` method returns the `anchor` parameter's value (when present) and defaults to the `target_string` value. The `context_uri` method returns a string representing the resolved URL.
116
122
 
117
123
  #### Relation Type ([§ 3.3](https://tools.ietf.org/html/rfc8288#section-3.3))
118
124
 
119
125
  ```ruby
120
- link_headers = ['<https://example.com/chapters/1>; rel="prev start"']
121
- parsed_header = LinkHeaderParser.parse(link_headers, base: 'https://example.com/').first
126
+ link_header = LinkHeaderParser.parse('</chapters/1>; rel="prev start"', base: 'https://example.com/').first
127
+
128
+ link_header.relations_string
129
+ #=> "prev start"
122
130
 
123
- parsed_header.relations # => 'prev start'
124
- parsed_header.relation_types # => ['prev', 'start']
131
+ link_header.relation_types
132
+ #=> ["prev", "start"]
125
133
  ```
126
134
 
135
+ #### Link Parameters ([Appendix B.3](https://tools.ietf.org/html/rfc8288#appendix-B.3))
136
+
137
+ ```ruby
138
+ link_header = LinkHeaderParser.parse('</posts.rss>; rel="alternate"; hreflang="en-US"; title="sixtwothree.org: Posts"; type="application/rss+xml"', base: 'https://sixtwothree.org').first
139
+
140
+ link_header.link_parameters
141
+ #=> [#<LinkHeaderParser::LinkHeaderParameter:0x3fdea54716ac name: "rel", value: "alternate">, #<LinkHeaderParser::LinkHeaderParameter:0x3fdea5471684 name: "hreflang", value: "en-US">, #<LinkHeaderParser::LinkHeaderParameter:0x3fdea5471670 name: "title", value: "sixtwothree.org: Posts">, #<LinkHeaderParser::LinkHeaderParameter:0x3fdea547165c name: "type", value: "application/rss+xml">]
142
+ ```
143
+
144
+ Note that the `Array` returned by the `link_parameters` method may include multiple `LinkHeaderParameter`s with the same name depending on the provided Link header. Certain methods on `LinkHeader` will return values from the first occurrence of a parameter name (e.g. `link_header.relations_string`) in accordance with [RFC-8288](https://tools.ietf.org/html/rfc8288).
145
+
127
146
  ## Contributing
128
147
 
129
148
  Interested in helping improve link-header-parser-ruby? Awesome! Your help is greatly appreciated. See [CONTRIBUTING.md](https://github.com/jgarber623/link-header-parser-ruby/blob/master/CONTRIBUTING.md) for details.
@@ -1,17 +1,21 @@
1
- require 'ostruct'
1
+ require 'forwardable'
2
2
 
3
3
  require 'absolutely'
4
4
 
5
5
  require 'link_header_parser/version'
6
6
  require 'link_header_parser/exceptions'
7
7
 
8
- require 'link_header_parser/parsed_header'
9
- require 'link_header_parser/parsed_header_collection'
8
+ require 'link_header_parser/link_header'
9
+ require 'link_header_parser/link_header_parameter'
10
+ require 'link_header_parser/link_headers_collection'
10
11
 
11
12
  module LinkHeaderParser
12
- class << self
13
- def parse(*headers, base:)
14
- ParsedHeaderCollection.new(headers, base: base)
15
- end
13
+ # Parse an array of HTTP Link headers
14
+ #
15
+ # @param headers [Array<String>]
16
+ # @param base [String]
17
+ # @return [LinkHeaderParser::LinkHeadersCollection]
18
+ def self.parse(*headers, base:)
19
+ LinkHeadersCollection.new(*headers, base: base)
16
20
  end
17
21
  end
@@ -0,0 +1,106 @@
1
+ module LinkHeaderParser
2
+ class LinkHeader
3
+ FIELD_VALUE_REGEXP_PATTERN = /^\s*<\s*(?<target_string>[^>]+)\s*>\s*(?<parameters>;\s*.*)$/.freeze
4
+ PARAMETERS_REGEXP_PATTERN = /(?<!;)\s*([^;]+)/.freeze
5
+
6
+ attr_reader :field_value
7
+
8
+ # Create a new parsed Link header
9
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2
10
+ #
11
+ # @param field_value [String]
12
+ # @param base [String]
13
+ def initialize(field_value, base:)
14
+ raise ArgumentError, "field_value must be a String (given #{field_value.class})" unless field_value.is_a?(String)
15
+ raise ArgumentError, "base must be a String (given #{base.class})" unless base.is_a?(String)
16
+
17
+ @field_value = field_value
18
+ @base = base
19
+ end
20
+
21
+ # The context URL for this Link header extracted from field_value (or target URL if no context URL is present)
22
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2 (Appendix B.2.2.11)
23
+ #
24
+ # @return [String]
25
+ def context_string
26
+ @context_string ||= grouped_link_parameters[:anchor]&.first || target_string
27
+ end
28
+
29
+ # The resolved context URL for this Link header
30
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2 (Appendix B.2.2.12)
31
+ #
32
+ # @return [String]
33
+ def context_uri
34
+ @context_uri ||= Absolutely.to_abs(base: target_uri, relative: context_string)
35
+ end
36
+
37
+ def inspect
38
+ format(%(#<#{self.class.name}:%#0x target_uri: #{target_uri.inspect}, relation_types: #{relation_types.inspect}>), object_id)
39
+ end
40
+
41
+ # The parsed parameters for this Link header extracted from field_value
42
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.3
43
+ #
44
+ # @return [Array<LinkHeaderParser::LinkHeaderParameter>]
45
+ def link_parameters
46
+ @link_parameters ||= field_value_match_data[:parameters].scan(PARAMETERS_REGEXP_PATTERN).flatten.map { |parameter| LinkHeaderParameter.new(parameter) }
47
+ end
48
+
49
+ # The relations_string value returned as an Array
50
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2 (Appendix B.2.2.10 and Appendix B.2.2.17.1)
51
+ #
52
+ # @return [Array<String>]
53
+ def relation_types
54
+ @relation_types ||= relations_string.split.map(&:downcase)
55
+ end
56
+
57
+ # The relation types for this Link header extracted from field_value
58
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2 (Appendix B.2.2.9)
59
+ #
60
+ # @return [String]
61
+ def relations_string
62
+ @relations_string ||= grouped_link_parameters[:rel]&.first || ''
63
+ end
64
+
65
+ # The target URL for this Link header extracted from field_value
66
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2 (Appendix B.2.2.4)
67
+ #
68
+ # @return [String]
69
+ def target_string
70
+ @target_string ||= field_value_match_data[:target_string]
71
+ end
72
+
73
+ # The resolved target URL for this Link header
74
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.2 (Appendix B.2.2.8)
75
+ #
76
+ # @return [String]
77
+ def target_uri
78
+ @target_uri ||= Absolutely.to_abs(base: base, relative: target_string)
79
+ end
80
+
81
+ # @return [Hash{Symbol => String, Array, Hash{Symbol => Array}}]
82
+ def to_h
83
+ {
84
+ target_string: target_string,
85
+ target_uri: target_uri,
86
+ context_string: context_string,
87
+ context_uri: context_uri,
88
+ relations_string: relations_string,
89
+ relation_types: relation_types,
90
+ link_parameters: grouped_link_parameters
91
+ }
92
+ end
93
+
94
+ private
95
+
96
+ attr_reader :base
97
+
98
+ def field_value_match_data
99
+ @field_value_match_data ||= field_value.match(FIELD_VALUE_REGEXP_PATTERN)
100
+ end
101
+
102
+ def grouped_link_parameters
103
+ @grouped_link_parameters ||= link_parameters.map(&:to_a).group_by(&:shift).transform_keys(&:to_sym).transform_values(&:flatten)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,41 @@
1
+ module LinkHeaderParser
2
+ class LinkHeaderParameter
3
+ PARAMETER_REGEXP_PATTERN = /^(?<name>.+?)(?:="?(?<value>.*?)"?)?$/.freeze
4
+
5
+ attr_reader :parameter
6
+
7
+ # @param parameter [String]
8
+ def initialize(parameter)
9
+ @parameter = parameter
10
+ end
11
+
12
+ def inspect
13
+ format(%(#<#{self.class.name}:%#0x name: #{name.inspect}, value: #{value.inspect}>), object_id)
14
+ end
15
+
16
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.3 (Appendix B.3.2.9)
17
+ #
18
+ # @return [String]
19
+ def name
20
+ @name ||= parameter_match_data[:name].downcase
21
+ end
22
+
23
+ # @see https://tools.ietf.org/html/rfc8288#appendix-B.3 (Appendix B.3.2.8)
24
+ #
25
+ # @return [String, nil]
26
+ def value
27
+ @value ||= parameter_match_data[:value] || ''
28
+ end
29
+
30
+ # @return [Array<String>]
31
+ def to_a
32
+ [name, value]
33
+ end
34
+
35
+ private
36
+
37
+ def parameter_match_data
38
+ @parameter_match_data ||= parameter.match(PARAMETER_REGEXP_PATTERN)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ module LinkHeaderParser
2
+ class LinkHeadersCollection
3
+ extend Forwardable
4
+
5
+ include Enumerable
6
+
7
+ def_delegators :members, :[], :<<, :each, :last, :length, :push
8
+
9
+ attr_reader :headers
10
+
11
+ # @param headers [Array<String>]
12
+ # @param base [String]
13
+ def initialize(*headers, base:)
14
+ @headers = headers.flatten
15
+ @base = base
16
+
17
+ discrete_headers.each { |header| push(LinkHeader.new(header, base: base)) }
18
+ end
19
+
20
+ # @return [Hash{Symbol => Array<LinkHeaderParser::LinkHeader>}]
21
+ def group_by_relation_type
22
+ relation_types.map do |relation_type|
23
+ [relation_type, find_all { |member| member.relation_types.include?(relation_type) }]
24
+ end.to_h.transform_keys(&:to_sym)
25
+ end
26
+
27
+ def inspect
28
+ format(%(#<#{self.class.name}:%#0x headers: #{headers.inspect}, relation_types: #{relation_types.inspect}>), object_id)
29
+ end
30
+
31
+ # @return [Array<String>]
32
+ def relation_types
33
+ @relation_types ||= flat_map(&:relation_types).uniq.sort
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :base
39
+
40
+ def discrete_headers
41
+ @discrete_headers ||= headers.flat_map { |header| header.split(/,(?=[\s|<])/) }.map(&:strip)
42
+ end
43
+
44
+ def members
45
+ @members ||= []
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module LinkHeaderParser
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require_relative 'lib/link_header_parser/version'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.required_ruby_version = Gem::Requirement.new('>= 2.4', '< 2.8')
4
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5', '< 2.8')
5
5
 
6
6
  spec.name = 'link-header-parser'
7
7
  spec.version = LinkHeaderParser::VERSION
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: link-header-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Garber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-14 00:00:00.000000000 Z
11
+ date: 2020-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: absolutely
@@ -48,8 +48,9 @@ files:
48
48
  - Rakefile
49
49
  - lib/link-header-parser.rb
50
50
  - lib/link_header_parser/exceptions.rb
51
- - lib/link_header_parser/parsed_header.rb
52
- - lib/link_header_parser/parsed_header_collection.rb
51
+ - lib/link_header_parser/link_header.rb
52
+ - lib/link_header_parser/link_header_parameter.rb
53
+ - lib/link_header_parser/link_headers_collection.rb
53
54
  - lib/link_header_parser/version.rb
54
55
  - link-header-parser.gemspec
55
56
  homepage: https://github.com/jgarber623/link-header-parser-ruby
@@ -57,7 +58,7 @@ licenses:
57
58
  - MIT
58
59
  metadata:
59
60
  bug_tracker_uri: https://github.com/jgarber623/link-header-parser-ruby/issues
60
- changelog_uri: https://github.com/jgarber623/link-header-parser-ruby/blob/v1.0.0/CHANGELOG.md
61
+ changelog_uri: https://github.com/jgarber623/link-header-parser-ruby/blob/v2.0.0/CHANGELOG.md
61
62
  post_install_message:
62
63
  rdoc_options: []
63
64
  require_paths:
@@ -66,7 +67,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
67
  requirements:
67
68
  - - ">="
68
69
  - !ruby/object:Gem::Version
69
- version: '2.4'
70
+ version: '2.5'
70
71
  - - "<"
71
72
  - !ruby/object:Gem::Version
72
73
  version: '2.8'
@@ -76,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  - !ruby/object:Gem::Version
77
78
  version: '0'
78
79
  requirements: []
79
- rubygems_version: 3.1.3
80
+ rubygems_version: 3.1.2
80
81
  signing_key:
81
82
  specification_version: 4
82
83
  summary: Parse HTTP Link headers.
@@ -1,70 +0,0 @@
1
- module LinkHeaderParser
2
- class ParsedHeader
3
- attr_reader :header
4
-
5
- def initialize(header, base:)
6
- raise ArgumentError, "header must be a String (given #{header.class})" unless header.is_a?(String)
7
- raise ArgumentError, "base must be a String (given #{base.class})" unless base.is_a?(String)
8
-
9
- @header = header
10
- @base = base
11
- end
12
-
13
- # https://tools.ietf.org/html/rfc8288#section-3.2
14
- def context
15
- @context ||= parameters.anchor || target
16
- end
17
-
18
- def context_uri
19
- @context_uri ||= Absolutely.to_abs(base: target_uri, relative: context)
20
- end
21
-
22
- def inspect
23
- format(%(#<#{self.class.name}:%#0x @header="#{header.gsub('"', '\"')}">), object_id)
24
- end
25
-
26
- def parameters
27
- @parameters ||= OpenStruct.new(header_attributes)
28
- end
29
-
30
- def relation_types
31
- @relation_types ||= relations&.split(' ') || nil
32
- end
33
-
34
- # https://tools.ietf.org/html/rfc8288#section-3.3
35
- def relations
36
- @relations ||= parameters.rel || nil
37
- end
38
-
39
- # https://tools.ietf.org/html/rfc8288#section-3.1
40
- def target
41
- @target ||= header_match_data[:target]
42
- end
43
-
44
- def target_uri
45
- @target_uri ||= Absolutely.to_abs(base: @base, relative: target)
46
- end
47
-
48
- def to_h
49
- {
50
- target: target,
51
- target_uri: target_uri,
52
- context: context,
53
- context_uri: context_uri,
54
- relations: relations,
55
- relation_types: relation_types,
56
- parameters: parameters.to_h
57
- }
58
- end
59
-
60
- private
61
-
62
- def header_attributes
63
- @header_attributes ||= header_match_data[:attributes].tr('"', '').split(';').map { |tuple| tuple.split('=').map(&:strip) }.sort.to_h
64
- end
65
-
66
- def header_match_data
67
- @header_match_data ||= header.match(/^<\s*(?<target>[^>]+)\s*>\s*;\s*(?<attributes>.*)$/)
68
- end
69
- end
70
- end
@@ -1,58 +0,0 @@
1
- module LinkHeaderParser
2
- class ParsedHeaderCollection
3
- include Enumerable
4
-
5
- attr_reader :headers
6
-
7
- def initialize(*headers, base:)
8
- @headers = headers.flatten
9
- @base = base
10
- end
11
-
12
- def by_relation_type
13
- @by_relation_type ||= OpenStruct.new(mapped_relation_types)
14
- end
15
-
16
- def each
17
- return to_enum unless block_given?
18
-
19
- parsed_headers.each { |parsed_header| yield parsed_header }
20
-
21
- self
22
- end
23
-
24
- def inspect
25
- format(%(#<#{self.class.name}:%#0x @headers=#{headers}>), object_id)
26
- end
27
-
28
- def last
29
- @last ||= parsed_headers[-1]
30
- end
31
-
32
- def length
33
- @length ||= parsed_headers.length
34
- end
35
-
36
- def relation_types
37
- @relation_types ||= parsed_headers.map(&:relation_types).flatten.uniq.sort
38
- end
39
-
40
- private
41
-
42
- def find_all_by_relation_type(relation_type)
43
- find_all { |parsed_header| parsed_header.relation_types.include?(relation_type) }
44
- end
45
-
46
- def mapped_relation_types
47
- @mapped_relation_types ||= relation_types.map { |relation_type| [relation_type, find_all_by_relation_type(relation_type)] }.to_h
48
- end
49
-
50
- def parsed_headers
51
- @parsed_headers ||= uniq_headers.map { |header| ParsedHeader.new(header, base: @base) }
52
- end
53
-
54
- def uniq_headers
55
- @uniq_headers ||= headers.map { |header| header.split(/,(?=[\s|<])/) }.flatten.map(&:strip)
56
- end
57
- end
58
- end