link-header-parser 1.0.0 → 2.0.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 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