xenon 0.0.3 → 0.0.4

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. metadata +13 -145
  3. data/.codeclimate.yml +0 -18
  4. data/.gitignore +0 -25
  5. data/.rspec +0 -3
  6. data/.travis.yml +0 -6
  7. data/Gemfile +0 -13
  8. data/Guardfile +0 -16
  9. data/LICENSE +0 -22
  10. data/README.md +0 -86
  11. data/Rakefile +0 -8
  12. data/examples/hello_world/config.ru +0 -3
  13. data/examples/hello_world/hello_world.rb +0 -17
  14. data/lib/xenon.rb +0 -253
  15. data/lib/xenon/auth.rb +0 -63
  16. data/lib/xenon/errors.rb +0 -5
  17. data/lib/xenon/etag.rb +0 -48
  18. data/lib/xenon/headers.rb +0 -112
  19. data/lib/xenon/headers/accept.rb +0 -34
  20. data/lib/xenon/headers/accept_charset.rb +0 -59
  21. data/lib/xenon/headers/accept_encoding.rb +0 -63
  22. data/lib/xenon/headers/accept_language.rb +0 -59
  23. data/lib/xenon/headers/authorization.rb +0 -50
  24. data/lib/xenon/headers/cache_control.rb +0 -56
  25. data/lib/xenon/headers/content_type.rb +0 -23
  26. data/lib/xenon/headers/if_match.rb +0 -53
  27. data/lib/xenon/headers/if_modified_since.rb +0 -22
  28. data/lib/xenon/headers/if_none_match.rb +0 -53
  29. data/lib/xenon/headers/if_range.rb +0 -45
  30. data/lib/xenon/headers/if_unmodified_since.rb +0 -22
  31. data/lib/xenon/headers/user_agent.rb +0 -65
  32. data/lib/xenon/headers/www_authenticate.rb +0 -70
  33. data/lib/xenon/media_type.rb +0 -162
  34. data/lib/xenon/parsers/basic_rules.rb +0 -86
  35. data/lib/xenon/parsers/header_rules.rb +0 -60
  36. data/lib/xenon/parsers/media_type.rb +0 -53
  37. data/lib/xenon/quoted_string.rb +0 -20
  38. data/lib/xenon/routing/directives.rb +0 -14
  39. data/lib/xenon/routing/header_directives.rb +0 -32
  40. data/lib/xenon/routing/method_directives.rb +0 -26
  41. data/lib/xenon/routing/param_directives.rb +0 -22
  42. data/lib/xenon/routing/path_directives.rb +0 -37
  43. data/lib/xenon/routing/route_directives.rb +0 -51
  44. data/lib/xenon/routing/security_directives.rb +0 -20
  45. data/lib/xenon/version.rb +0 -3
  46. data/spec/spec_helper.rb +0 -94
  47. data/spec/xenon/etag_spec.rb +0 -19
  48. data/spec/xenon/headers/accept_charset_spec.rb +0 -31
  49. data/spec/xenon/headers/accept_encoding_spec.rb +0 -40
  50. data/spec/xenon/headers/accept_language_spec.rb +0 -33
  51. data/spec/xenon/headers/accept_spec.rb +0 -54
  52. data/spec/xenon/headers/authorization_spec.rb +0 -47
  53. data/spec/xenon/headers/cache_control_spec.rb +0 -64
  54. data/spec/xenon/headers/if_match_spec.rb +0 -73
  55. data/spec/xenon/headers/if_modified_since_spec.rb +0 -19
  56. data/spec/xenon/headers/if_none_match_spec.rb +0 -79
  57. data/spec/xenon/headers/if_range_spec.rb +0 -45
  58. data/spec/xenon/headers/if_unmodified_since_spec.rb +0 -19
  59. data/spec/xenon/headers/user_agent_spec.rb +0 -67
  60. data/spec/xenon/headers/www_authenticate_spec.rb +0 -43
  61. data/spec/xenon/media_type_spec.rb +0 -267
  62. data/xenon.gemspec +0 -30
@@ -1,162 +0,0 @@
1
- require 'xenon/errors'
2
- require 'xenon/parsers/media_type'
3
-
4
- module Xenon
5
-
6
- # A media type.
7
- #
8
- # @see ContentType
9
- # @see MediaRange
10
- class MediaType
11
- attr_reader :type, :subtype, :params
12
-
13
- # Initializes a new instance of MediaType.
14
- #
15
- # @param type [String] The main type, e.g. 'application'.
16
- # @param subtype [String] The subtype, e.g. 'json'.
17
- # @param params [Hash] Any params for the media type; don't use 'q' or 'charset'.
18
- def initialize(type, subtype, params = {})
19
- @type = type
20
- @subtype = subtype
21
- @params = params
22
- end
23
-
24
- # Parses a media type.
25
- #
26
- # @param s [String] The media type string.
27
- # @return [MediaType] The media type.
28
- def self.parse(s)
29
- tree = Parsers::MediaType.new.parse(s)
30
- Parsers::MediaTypeTransform.new.apply(tree)
31
- rescue Parslet::ParseFailed
32
- raise Xenon::ParseError.new("Invalid media type (#{s}).")
33
- end
34
-
35
- %w(application audio image message multipart text video).each do |type|
36
- define_method "#{type}?" do
37
- @type == type
38
- end
39
- end
40
-
41
- def experimental?
42
- @subtype.start_with?('x.') # not x- see http://tools.ietf.org/html/rfc6838#section-3.4
43
- end
44
-
45
- def personal?
46
- @subtype.start_with?('prs.')
47
- end
48
-
49
- def vendor?
50
- @subtype.start_with?('vnd.')
51
- end
52
-
53
- %w(ber der fastinfoset json wbxml xml zip).each do |format|
54
- define_method "#{format}?" do
55
- @subtype == format || @subtype.end_with?("+#{format}")
56
- end
57
- end
58
-
59
- # Creates a {MediaRange} using this media type with a quality factor.
60
- #
61
- # @param q [Numeric] A value between 1.0 (most desirable) and 0.0 (not acceptable).
62
- # @return [MediaRange] The media range.
63
- def with_q(q)
64
- MediaRange.new(self, q)
65
- end
66
-
67
- # Creates a {ContentType} using this media type with a charset.
68
- #
69
- # @param charset [String] The desired charset, e.g. 'utf-8'.
70
- # @return [ContentType] The content type.
71
- def with_charset(charset)
72
- ContentType.new(self, charset)
73
- end
74
-
75
- def to_s
76
- "#{@type}/#{@subtype}" << @params.map { |n, v| v ? "; #{n}=#{v}" : "; #{n}" }.join
77
- end
78
-
79
- JSON = MediaType.new('application', 'json')
80
- XML = MediaType.new('application', 'xml')
81
- end
82
-
83
- # A content type.
84
- class ContentType
85
- attr_reader :media_type, :charset
86
-
87
- DEFAULT_CHARSET = 'utf-8' # historically iso-8859-1 but see http://tools.ietf.org/html/rfc7231#appendix-B
88
-
89
- def initialize(media_type, charset = DEFAULT_CHARSET)
90
- @media_type = media_type
91
- @charset = charset
92
- end
93
-
94
- def self.parse(s)
95
- media_type = MediaType.parse(s)
96
- charset = media_type.params.delete('charset') || DEFAULT_CHARSET
97
- ContentType.new(media_type, charset)
98
- end
99
-
100
- def to_s
101
- "#{@media_type}; charset=#{@charset}"
102
- end
103
- end
104
-
105
- class MediaRange
106
- include Comparable
107
-
108
- DEFAULT_Q = 1.0
109
-
110
- attr_reader :type, :subtype, :q, :params
111
-
112
- def initialize(type, subtype, params = {})
113
- @type = type
114
- @subtype = subtype
115
- @q = Float(params.delete('q')) rescue DEFAULT_Q
116
- @params = params
117
- end
118
-
119
- def self.parse(s)
120
- tree = Parsers::MediaRange.new.parse(s)
121
- Parsers::MediaTypeTransform.new.apply(tree)
122
- rescue Parslet::ParseFailed
123
- raise Xenon::ParseError.new("Invalid media range (#{s})")
124
- end
125
-
126
- def <=>(other)
127
- dt = compare_types(@type, other.type)
128
- return dt if dt != 0
129
- ds = compare_types(@subtype, other.subtype)
130
- return ds if ds != 0
131
- dp = params.size <=> other.params.size
132
- return dp if dp != 0
133
- @q <=> other.q
134
- end
135
-
136
- def =~(media_type)
137
- (type == '*' || type == media_type.type) &&
138
- (subtype == '*' || subtype == media_type.subtype) &&
139
- params.all? { |n, v| media_type.params[n] == v }
140
- end
141
-
142
- alias_method :===, :=~
143
-
144
- def to_s
145
- s = "#{@type}/#{@subtype}"
146
- s << @params.map { |n, v| v ? "; #{n}=#{v}" : "; #{n}" }.join
147
- s << "; q=#{@q}" if @q != DEFAULT_Q
148
- s
149
- end
150
-
151
- private
152
-
153
- def compare_types(a, b)
154
- if a == b then 0
155
- elsif a == '*' then -1
156
- elsif b == '*' then 1
157
- else 0
158
- end
159
- end
160
- end
161
-
162
- end
@@ -1,86 +0,0 @@
1
- require 'parslet'
2
-
3
- module Xenon
4
- module Parsers
5
-
6
- # Parslet doesn't match sequence of sequences (i.e. [['foo', 'bar']]) as a sequence(:v) in transform
7
- # rules so this is a little wrapper class that allows smuggling an array through the matcher rules,
8
- # for example above would be [Tuple.new('foo', 'bar')], when no 'proper' class is required.
9
- class Tuple
10
- def initialize(*values)
11
- @values = values
12
- end
13
-
14
- def to_a
15
- @values
16
- end
17
- end
18
-
19
- module BasicRules
20
- include Parslet
21
-
22
- # http://tools.ietf.org/html/rfc5234#appendix-B.1
23
- rule(:alpha) { match(/[a-z]/i) }
24
- rule(:bit) { match(/[01]/) }
25
- rule(:char) { match(/[\u0001-\u007f]/) }
26
- rule(:digit) { match(/[0-9]/) }
27
- rule(:hexdig) { match(/[a-f0-9]/i)}
28
- rule(:vchar) { match(/[\u0021-\u007e]/) }
29
- rule(:alphanum) { alpha | digit }
30
-
31
- rule(:sp) { str(' ') }
32
- rule(:sp?) { sp.repeat }
33
- rule(:htab) { str("\t") }
34
- rule(:wsp) { sp | htab }
35
- rule(:lwsp) { (crlf.maybe >> wsp).repeat }
36
-
37
- rule(:cr) { str("\r") }
38
- rule(:lf) { str("\n") }
39
- rule(:crlf) { cr >> lf }
40
- rule(:dquote) { str('"') }
41
-
42
- # http://tools.ietf.org/html/rfc7230#section-3.2.3
43
- rule(:ows) { wsp.repeat }
44
- rule(:rws) { wsp.repeat(1) }
45
- rule(:bws) { wsp.repeat }
46
-
47
- # http://tools.ietf.org/html/rfc7230#section-3.2.6
48
- rule(:tchar) { alpha | digit | match(/[!#\$%&'\*\+\-\.\^_`\|~]/) }
49
- rule(:token) { tchar.repeat(1) }
50
-
51
- # http://tools.ietf.org/html/rfc7231#section-7.1.1.1
52
- rule(:day_name) { str('Mon') | str('Tue') | str('Wed') | str('Thu') | str('Fri') | str('Sat') | str('Sun') }
53
- rule(:day) { digit.repeat(2) }
54
- rule(:month) { (str('Jan') | str('Feb') | str('Mar') | str('Apr') | str('May') | str('Jun') | str('Jul') | str('Aug') | str('Sep') | str('Oct') | str('Nov') | str('Dec')) }
55
- rule(:year) { digit.repeat(4) }
56
- rule(:date1) { day >> sp >> month >> sp >> year }
57
- rule(:gmt) { str('GMT') }
58
- rule(:hour) { digit.repeat(2) }
59
- rule(:minute) { digit.repeat(2) }
60
- rule(:second) { digit.repeat(2) }
61
- rule(:time_of_day) { hour >> str(':') >> minute >> str(':') >> second }
62
- rule(:imf_fixdate) { day_name >> str(',') >> sp >> date1 >> sp >> time_of_day >> sp >> gmt }
63
- rule(:day_name_l) { str('Monday') | str('Tuesday') | str('Wednesday') | str('Thursday') | str('Friday') | str('Saturday') | str('Sunday') }
64
- rule(:year2) { digit.repeat(2) }
65
- rule(:date2) { day >> str('-') >> month >> str('-') >> year2 }
66
- rule(:rfc850_date) { day_name_l >> str(',') >> sp >> date2 >> sp >> time_of_day >> sp >> gmt }
67
- rule(:day1) { sp >> digit }
68
- rule(:date3) { month >> sp >> (day | day1) }
69
- rule(:asctime_date) { day_name >> sp >> date3 >> sp >> time_of_day >> sp >> year }
70
- rule(:obs_date) { rfc850_date | asctime_date }
71
- rule(:http_date) { (imf_fixdate | obs_date).as(:http_date) }
72
-
73
- # extras -- TODO: move these into header rules?
74
- rule(:comma) { str(',') >> sp? }
75
- rule(:semicolon) { str(';') >> sp? }
76
- end
77
-
78
- class BasicTransform < Parslet::Transform
79
- rule(simple(:v)) { v.respond_to?(:str) ? v.str : v }
80
-
81
- rule(quoted_string: simple(:qs)) { qs[1..-2].gsub(/\\(.)/, '\1') }
82
- rule(http_date: simple(:str)) { Time.httpdate(str) }
83
- end
84
-
85
- end
86
- end
@@ -1,60 +0,0 @@
1
- require 'xenon/quoted_string'
2
- require 'xenon/parsers/basic_rules'
3
-
4
- module Xenon
5
- module Parsers
6
-
7
- module HeaderRules
8
- include Parslet, BasicRules
9
-
10
- # http://tools.ietf.org/html/rfc7230#section-3.2.6
11
- rule(:list_sep) { str(',') >> sp? }
12
- rule(:param_sep) { str(';') >> sp? }
13
- rule(:obs_text) { match(/[\u0080-\u00ff]/)}
14
- rule(:qdtext) { htab | sp | match(/[\u0021\u0023-\u005b\u005d-\u007e]/) | obs_text }
15
- rule(:quoted_pair) { str('\\') >> (htab | sp | vchar | obs_text) }
16
- rule(:quoted_string) { (dquote >> (qdtext | quoted_pair).repeat >> dquote).as(:quoted_string) }
17
- rule(:ctext) { htab | sp | match(/[\u0021-\u0027\u002a-\u005b\u005d-\u007e]/) | obs_text }
18
- rule(:comment) { (str('(') >> (ctext | quoted_pair | comment).repeat >> str(')')).as(:comment) }
19
-
20
- # http://tools.ietf.org/html/rfc7231#section-5.3.1
21
- rule(:weight_value) { (digit >> (str('.') >> digit.repeat(0, 3)).maybe).as(:q) }
22
- rule(:weight) { param_sep >> str('q') >> sp? >> str('=') >> sp? >> weight_value >> sp? }
23
- end
24
-
25
- module AuthHeaderRules
26
- include Parslet, HeaderRules
27
-
28
- rule(:token68) { ((alpha | digit | match(/[\-\._~\+\/]/)) >> str('=').repeat).repeat(1).as(:token) }
29
- rule(:auth_scheme) { token.as(:auth_scheme) }
30
- rule(:name) { token.as(:name) }
31
- rule(:value) { token.as(:value) }
32
- rule(:auth_param) { (name >> bws >> str('=') >> bws >> (token | quoted_string).as(:value)).as(:auth_param) }
33
- rule(:auth_params) { (auth_param.maybe >> (ows >> comma >> ows >> auth_param).repeat).as(:auth_params) }
34
- end
35
-
36
- module ETagHeaderRules
37
- include Parslet, HeaderRules
38
-
39
- # http://tools.ietf.org/html/rfc7232#section-2.3
40
- rule(:wildcard) { str('*').as(:wildcard) }
41
- rule(:weak) { str('W/').as(:weak) }
42
- rule(:etagc) { str('!') | match(/[\u0023-\u007e#-~]/) | obs_text }
43
- rule(:opaque_tag) { dquote >> etagc.repeat.as(:opaque_tag) >> dquote }
44
- rule(:etag) { (weak.maybe >> opaque_tag).as(:etag) }
45
- end
46
-
47
- class HeaderTransform < BasicTransform
48
- using QuotedString
49
-
50
- rule(quoted_string: simple(:qs)) { qs.unquote }
51
- rule(comment: simple(:c)) { c.uncomment }
52
- end
53
-
54
- class ETagHeaderTransform < HeaderTransform
55
- rule(etag: { opaque_tag: simple(:t), weak: simple(:w) }) { Xenon::ETag.new(t, weak: true) }
56
- rule(etag: { opaque_tag: simple(:t) }) { Xenon::ETag.new(t) }
57
- end
58
-
59
- end
60
- end
@@ -1,53 +0,0 @@
1
- require 'xenon/parsers/basic_rules'
2
-
3
- module Xenon
4
- module Parsers
5
-
6
- # note this uses the rules from https://tools.ietf.org/html/rfc6838 not http://tools.ietf.org/html/rfc7231
7
- # because the latter is slightly ambiguous, e.g. a token can include * so */json would parse correctly
8
- module MediaTypeRules
9
- include Parslet, BasicRules
10
-
11
- rule(:restricted_name_first) { match(/[a-zA-Z0-9]/) }
12
- rule(:restricted_name_chars) { match(/[a-zA-Z0-9!#\$&\-\^_\.\+]/).repeat(0, 126) }
13
- rule(:restricted_name) { restricted_name_first >> restricted_name_chars }
14
-
15
- rule(:type) { restricted_name.as(:type) }
16
- rule(:slash) { str('/') }
17
- rule(:subtype) { restricted_name.as(:subtype) >> sp? }
18
-
19
- rule(:param_sep) { str(';') >> sp? }
20
- rule(:param_name) { restricted_name.as(:param_name) >> sp? }
21
- rule(:equals) { str('=') >> sp? }
22
- rule(:param_value) { token.as(:param_value) >> sp? } # not quite correct but probably correct enough
23
- rule(:param) { param_sep >> param_name >> (equals >> param_value).maybe >> sp? }
24
- rule(:params) { param.repeat.as(:params) }
25
-
26
- rule(:media_type) { (type >> slash >> subtype >> params).as(:media_type) >> sp? }
27
-
28
- rule(:wildcard) { str('*') }
29
- rule(:wild_media_range) { wildcard.as(:type) >> slash >> wildcard.as(:subtype) >> params }
30
- rule(:root_media_range) { type >> slash >> (wildcard.as(:subtype) | subtype) >> params }
31
- rule(:media_range) { (wild_media_range | root_media_range).as(:media_range) >> sp? }
32
- end
33
-
34
- class MediaType < Parslet::Parser
35
- include MediaTypeRules
36
- root(:media_type)
37
- end
38
-
39
- class MediaRange < Parslet::Parser
40
- include MediaTypeRules
41
- root(:media_range)
42
- end
43
-
44
- class MediaTypeTransform < Parslet::Transform
45
- rule(param_name: simple(:n), param_value: simple(:v)) { [n.str, v.str] }
46
- rule(param_name: simple(:n)) { [n.str, nil] }
47
- rule(type: simple(:t), subtype: simple(:s), params: subtree(:p)) { { type: t.str, subtype: s.str, params: Hash[p] } }
48
- rule(media_type: subtree(:mt)) { Xenon::MediaType.new(mt[:type], mt[:subtype], mt[:params])}
49
- rule(media_range: subtree(:mr)) { Xenon::MediaRange.new(mr[:type], mr[:subtype], mr[:params])}
50
- end
51
-
52
- end
53
- end
@@ -1,20 +0,0 @@
1
- module Xenon
2
- module QuotedString
3
- refine String do
4
- def quote
5
- qs = gsub(/([\\"])/, '\\\\\1')
6
- self == qs ? self : %{"#{qs}"}
7
- end
8
-
9
- def unquote
10
- qs = start_with?('"') && end_with?('"') ? self[1..-2] : self
11
- qs.gsub(/\\(.)/, '\1')
12
- end
13
-
14
- def uncomment
15
- qs = start_with?('(') && end_with?(')') ? self[1..-2] : self
16
- qs.gsub(/\\(.)/, '\1')
17
- end
18
- end
19
- end
20
- end
@@ -1,14 +0,0 @@
1
- Dir[File.join(__dir__, '*_directives.rb')].each { |f| require f }
2
-
3
- module Xenon
4
- module Routing
5
- module Directives
6
- include RouteDirectives
7
- include HeaderDirectives
8
- include MethodDirectives
9
- include ParamDirectives
10
- include PathDirectives
11
- include SecurityDirectives
12
- end
13
- end
14
- end
@@ -1,32 +0,0 @@
1
- require 'xenon/routing/route_directives'
2
-
3
- module Xenon
4
- module Routing
5
- module HeaderDirectives
6
- include RouteDirectives
7
-
8
- def optional_header(name)
9
- extract_request do |request|
10
- yield request.header(name)
11
- end
12
- end
13
-
14
- def header(name)
15
- optional_header(name) do |value|
16
- if value
17
- yield value
18
- else
19
- reject Rejection.new(:header, { required: name })
20
- end
21
- end
22
- end
23
-
24
- def respond_with_header(header)
25
- map_response -> r { r.copy(headers: r.headers.add(header)) } do
26
- yield
27
- end
28
- end
29
-
30
- end
31
- end
32
- end
@@ -1,26 +0,0 @@
1
- require 'xenon/routing/route_directives'
2
-
3
- module Xenon
4
- module Routing
5
- module MethodDirectives
6
- include RouteDirectives
7
-
8
- def request_method(method)
9
- extract_request do |request|
10
- if request.request_method == method
11
- yield
12
- else
13
- reject Rejection.new(:method, { supported: method })
14
- end
15
- end
16
- end
17
-
18
- %i(delete get head options patch post put).each do |method|
19
- define_method(method) do |&inner|
20
- request_method(method, &inner)
21
- end
22
- end
23
-
24
- end
25
- end
26
- end