xenon 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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