Skip to content

Commit 96fe929

Browse files
authored
Make use of generics instead of a method (#2)
* Allows Crystal to more accurately know the return type of `ordered_elements` * Add example of `ordered_elements` usage
1 parent 83eda12 commit 96fe929

File tree

6 files changed

+28
-32
lines changed

6 files changed

+28
-32
lines changed

spec/negotiator_spec.cr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ struct NegotiatorTest < NegotiatorTestCase
112112
def test_ordered_elements(header : String, expected : Indexable(String)) : Nil
113113
elements = @negotiator.ordered_elements header
114114

115+
elements.should be_a Array(ANG::Accept)
116+
115117
expected.each_with_index do |element, idx|
116118
elements[idx].should be_a ANG::Accept
117119
element.should eq elements[idx].header

src/abstract_negotiator.cr

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Base negotiator type. Implements logic common to all negotiators.
2-
abstract class Athena::Negotiation::AbstractNegotiator
2+
abstract class Athena::Negotiation::AbstractNegotiator(HeaderType)
33
private record OrderKey, quality : Float32, index : Int32, value : String do
44
include Comparable(self)
55

@@ -9,24 +9,22 @@ abstract class Athena::Negotiation::AbstractNegotiator
99
end
1010
end
1111

12-
private abstract def create_header(header : String) : ANG::BaseAccept
13-
14-
# Returns the best `ANG::BaseAccept` type based on the provided *header* value and *priorities*.
12+
# Returns the best `HeaderType` type based on the provided *header* value and *priorities*.
1513
#
1614
# See `Athena::Negotiation` for examples.
17-
def best(header : String, priorities : Indexable(String), strict : Bool = false) : ANG::BaseAccept?
15+
def best(header : String, priorities : Indexable(String), strict : Bool = false) : HeaderType?
1816
raise ArgumentError.new "priorities should not be empty." if priorities.empty?
1917
raise ArgumentError.new "The header string should not be empty." if header.blank?
2018

21-
accepted_headers = Array(ANG::BaseAccept).new
19+
accepted_headers = Array(HeaderType).new
2220

2321
self.parse_header(header) do |h|
24-
accepted_headers << self.create_header h
22+
accepted_headers << HeaderType.new h
2523
rescue ex
2624
raise ex if strict
2725
end
2826

29-
accepted_priorties = priorities.map &->create_header(String)
27+
accepted_priorties = priorities.map { |p| HeaderType.new p }
3028

3129
matches = self.find_matches accepted_headers, accepted_priorties
3230

@@ -41,18 +39,28 @@ abstract class Athena::Negotiation::AbstractNegotiator
4139
match.nil? ? nil : accepted_priorties[match.index]
4240
end
4341

44-
# Returns an array of `ANG::BaseAccept` types that the provided *header* allows, ordered so that the `#best` match is first.
42+
# Returns an array of `HeaderType` types that the provided *header* allows, ordered so that the `#best` match is first.
4543
#
46-
# See `Athena::Negotiation` for examples.
47-
def ordered_elements(header : String) : Array(ANG::BaseAccept)
44+
# ```
45+
# header = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"
46+
#
47+
# ordered_elements = ANG.negotiator.ordered_elements header
48+
#
49+
# ordered_elements[0].media_range # => "text/html"
50+
# ordered_elements[1].media_range # => "text/html"
51+
# ordered_elements[2].media_range # => "*/*"
52+
# ordered_elements[3].media_range # => "text/html"
53+
# ordered_elements[4].media_range # => "text/*"
54+
# ```
55+
def ordered_elements(header : String) : Array(HeaderType)
4856
raise ArgumentError.new "The header string should not be empty." if header.blank?
4957

50-
elements = Array(ANG::BaseAccept).new
58+
elements = Array(HeaderType).new
5159
order_keys = Array(OrderKey).new
5260

5361
idx = 0
5462
self.parse_header(header) do |h|
55-
element = self.create_header h
63+
element = HeaderType.new h
5664
elements << element
5765
order_keys << OrderKey.new element.quality, idx, element.header
5866
rescue ex
@@ -85,7 +93,7 @@ abstract class Athena::Negotiation::AbstractNegotiator
8593
end
8694
end
8795

88-
private def find_matches(headers : Array(ANG::BaseAccept), priorities : Indexable(ANG::BaseAccept)) : Array(ANG::AcceptMatch)
96+
private def find_matches(headers : Array(HeaderType), priorities : Indexable(HeaderType)) : Array(ANG::AcceptMatch)
8997
matches = [] of ANG::AcceptMatch
9098

9199
priorities.each_with_index do |priority, idx|

src/charset_negotiator.cr

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
require "./abstract_negotiator"
22

33
# A `ANG::AbstractNegotiator` implementation to negotiate `ANG::AcceptCharset` headers.
4-
class Athena::Negotiation::CharsetNegotiator < Athena::Negotiation::AbstractNegotiator
5-
private def create_header(header : String) : ANG::BaseAccept
6-
ANG::AcceptCharset.new header
7-
end
4+
class Athena::Negotiation::CharsetNegotiator < Athena::Negotiation::AbstractNegotiator(Athena::Negotiation::AcceptCharset)
85
end

src/encoding_negotiator.cr

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
require "./abstract_negotiator"
22

33
# A `ANG::AbstractNegotiator` implementation to negotiate `ANG::AcceptEncoding` headers.
4-
class Athena::Negotiation::EncodingNegotiator < Athena::Negotiation::AbstractNegotiator
5-
private def create_header(header : String) : ANG::BaseAccept
6-
ANG::AcceptEncoding.new header
7-
end
4+
class Athena::Negotiation::EncodingNegotiator < Athena::Negotiation::AbstractNegotiator(Athena::Negotiation::AcceptEncoding)
85
end

src/language_negotiator.cr

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require "./abstract_negotiator"
22

33
# A `ANG::AbstractNegotiator` implementation to negotiate `ANG::AcceptLanguage` headers.
4-
class Athena::Negotiation::LanguageNegotiator < Athena::Negotiation::AbstractNegotiator
4+
class Athena::Negotiation::LanguageNegotiator < Athena::Negotiation::AbstractNegotiator(Athena::Negotiation::AcceptLanguage)
55
protected def match(accept : ANG::AcceptLanguage, priority : ANG::AcceptLanguage, index : Int32) : ANG::AcceptMatch?
66
accept_base = accept.language
77
priority_base = priority.language
@@ -20,8 +20,4 @@ class Athena::Negotiation::LanguageNegotiator < Athena::Negotiation::AbstractNeg
2020

2121
nil
2222
end
23-
24-
private def create_header(header : String) : ANG::BaseAccept
25-
ANG::AcceptLanguage.new header
26-
end
2723
end

src/negotiator.cr

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require "./abstract_negotiator"
22

33
# A `ANG::AbstractNegotiator` implementation to negotiate `ANG::Accept` headers.
4-
class Athena::Negotiation::Negotiator < Athena::Negotiation::AbstractNegotiator
4+
class Athena::Negotiation::Negotiator < Athena::Negotiation::AbstractNegotiator(Athena::Negotiation::Accept)
55
# TODO: Make this method less complex.
66
#
77
# ameba:disable Metrics/CyclomaticComplexity
@@ -63,8 +63,4 @@ class Athena::Negotiation::Negotiator < Athena::Negotiation::AbstractNegotiator
6363

6464
sub_type.split '+', limit: 2
6565
end
66-
67-
private def create_header(header : String) : ANG::BaseAccept
68-
ANG::Accept.new header
69-
end
7066
end

0 commit comments

Comments
 (0)