functions_framework 0.4.1 → 0.5.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/docs/overview.md +1 -1
- data/docs/writing-functions.md +4 -4
- data/lib/functions_framework.rb +10 -6
- data/lib/functions_framework/function.rb +3 -2
- data/lib/functions_framework/legacy_event_converter.rb +10 -11
- data/lib/functions_framework/registry.rb +2 -1
- data/lib/functions_framework/server.rb +7 -7
- data/lib/functions_framework/testing.rb +20 -8
- data/lib/functions_framework/version.rb +1 -1
- metadata +17 -12
- data/lib/functions_framework/cloud_events.rb +0 -45
- data/lib/functions_framework/cloud_events/content_type.rb +0 -222
- data/lib/functions_framework/cloud_events/errors.rb +0 -42
- data/lib/functions_framework/cloud_events/event.rb +0 -84
- data/lib/functions_framework/cloud_events/event/field_interpreter.rb +0 -150
- data/lib/functions_framework/cloud_events/event/v0.rb +0 -236
- data/lib/functions_framework/cloud_events/event/v1.rb +0 -223
- data/lib/functions_framework/cloud_events/http_binding.rb +0 -310
- data/lib/functions_framework/cloud_events/json_format.rb +0 -173
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 370556a72cd31e4d24499b0715ca7f96a0598cb856fb28fed3d3eaabee0a80ae
|
4
|
+
data.tar.gz: 04a24cf1e2f205fe124eb0bda891f647177b93c43ba1c2286ed52ffbad18cb1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34d91fa0417632a253ddba1db7aa85f54697eb645caaaf96c9253a36fca241bca80bf17a40796dd5c8521d2c7bbe04ff23353764491d28b5e4335e0ee022f05a
|
7
|
+
data.tar.gz: 0d8d59ef3e45d3980136d7015e8066052bffe3942c442f715368dcf6ac5dd98b0cb282c313fa1a387ac70514747701e75c879c549faa9802444fe6b4d56115c7
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v0.5.0 / 2020-07-09
|
4
|
+
|
5
|
+
* Removed embedded CloudEvents classes and added the official CloudEvents SDK as a dependency. A `FunctionsFramework::CloudEvents` alias provides backward compatibility.
|
6
|
+
|
3
7
|
### v0.4.1 / 2020-07-08
|
4
8
|
|
5
9
|
* Fixed unsupported signal error on Windows.
|
data/README.md
CHANGED
@@ -60,7 +60,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
|
|
60
60
|
```ruby
|
61
61
|
# Gemfile
|
62
62
|
source "https://rubygems.org"
|
63
|
-
gem "functions_framework", "~> 0.
|
63
|
+
gem "functions_framework", "~> 0.5"
|
64
64
|
```
|
65
65
|
|
66
66
|
Create a file called `app.rb` and include the following code. This defines a
|
data/docs/overview.md
CHANGED
@@ -64,7 +64,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
|
|
64
64
|
```ruby
|
65
65
|
# Gemfile
|
66
66
|
source "https://rubygems.org"
|
67
|
-
gem "functions_framework", "~> 0.
|
67
|
+
gem "functions_framework", "~> 0.5"
|
68
68
|
```
|
69
69
|
|
70
70
|
Create a file called `app.rb` and include the following code. This defines a
|
data/docs/writing-functions.md
CHANGED
@@ -112,7 +112,7 @@ dependency on Sinatra in your `Gemfile`:
|
|
112
112
|
```ruby
|
113
113
|
# Gemfile
|
114
114
|
source "https://rubygems.org"
|
115
|
-
gem "functions_framework", "~> 0.
|
115
|
+
gem "functions_framework", "~> 0.5"
|
116
116
|
gem "sinatra", "~> 2.0"
|
117
117
|
```
|
118
118
|
|
@@ -158,9 +158,9 @@ end
|
|
158
158
|
```
|
159
159
|
|
160
160
|
The event parameter will be either a
|
161
|
-
[CloudEvents V0.3 Event](https://rubydoc.info/gems/
|
161
|
+
[CloudEvents V0.3 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V0)
|
162
162
|
object ([see spec](https://github.com/cloudevents/spec/blob/v0.3/spec.md)) or a
|
163
|
-
[CloudEvents V1.0 Event](https://rubydoc.info/gems/
|
163
|
+
[CloudEvents V1.0 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V1)
|
164
164
|
object ([see spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md)).
|
165
165
|
|
166
166
|
Some Google Cloud services send events in a legacy event format that was defined
|
@@ -236,7 +236,7 @@ A simple project might look like this:
|
|
236
236
|
```ruby
|
237
237
|
# Gemfile
|
238
238
|
source "https://rubygems.org"
|
239
|
-
gem "functions_framework", "~> 0.
|
239
|
+
gem "functions_framework", "~> 0.5"
|
240
240
|
```
|
241
241
|
|
242
242
|
```ruby
|
data/lib/functions_framework.rb
CHANGED
@@ -14,7 +14,8 @@
|
|
14
14
|
|
15
15
|
require "logger"
|
16
16
|
|
17
|
-
require "
|
17
|
+
require "cloud_events"
|
18
|
+
|
18
19
|
require "functions_framework/function"
|
19
20
|
require "functions_framework/legacy_event_converter"
|
20
21
|
require "functions_framework/registry"
|
@@ -44,10 +45,6 @@ require "functions_framework/version"
|
|
44
45
|
#
|
45
46
|
# Here is a roadmap to the internal modules in the Ruby functions framework.
|
46
47
|
#
|
47
|
-
# * {FunctionsFramework::CloudEvents} provides an implementation of the
|
48
|
-
# [CloudEvents](https://cloudevents.io) specification. In particular, if
|
49
|
-
# you define an event function, you will receive the event as a
|
50
|
-
# {FunctionsFramework::CloudEvents::Event} object.
|
51
48
|
# * {FunctionsFramework::CLI} is the implementation of the
|
52
49
|
# `functions-framework-ruby` executable. Most apps will not need to interact
|
53
50
|
# with this class directly.
|
@@ -94,6 +91,12 @@ module FunctionsFramework
|
|
94
91
|
#
|
95
92
|
DEFAULT_SOURCE = "./app.rb".freeze
|
96
93
|
|
94
|
+
##
|
95
|
+
# The CloudEvents implementation was extracted to become the official
|
96
|
+
# CloudEvents SDK. This alias is left here for backward compatibility.
|
97
|
+
#
|
98
|
+
CloudEvents = ::CloudEvents
|
99
|
+
|
97
100
|
class << self
|
98
101
|
##
|
99
102
|
# The "global" registry that holds events defined by the
|
@@ -144,7 +147,8 @@ module FunctionsFramework
|
|
144
147
|
#
|
145
148
|
# You must provide a name for the function, and a block that implemets the
|
146
149
|
# function. The block should take one argument: the event object of type
|
147
|
-
#
|
150
|
+
# [`CloudEvents::Event`](https://rubydoc.info/gems/cloud_events/CloudEvents/Event).
|
151
|
+
# Any return value is ignored.
|
148
152
|
#
|
149
153
|
# ## Example
|
150
154
|
#
|
@@ -23,8 +23,9 @@ module FunctionsFramework
|
|
23
23
|
# `Rack::Request` argument and returns one of various HTTP response types.
|
24
24
|
# See {FunctionsFramework::Registry.add_http}. For a function of type
|
25
25
|
# `:cloud_event`, the `call` method takes a single
|
26
|
-
#
|
27
|
-
# return a value.
|
26
|
+
# [CloudEvent](https://rubydoc.info/gems/cloud_events/CloudEvents/Event)
|
27
|
+
# argument, and does not return a value.
|
28
|
+
# See {FunctionsFramework::Registry.add_cloud_event}.
|
28
29
|
#
|
29
30
|
# If a callable object is provided directly, its `call` method is invoked for
|
30
31
|
# every function execution. Note that this means it may be called multiple
|
@@ -23,12 +23,11 @@ module FunctionsFramework
|
|
23
23
|
# Decode an event from the given Rack environment hash.
|
24
24
|
#
|
25
25
|
# @param env [Hash] The Rack environment
|
26
|
-
# @return [
|
27
|
-
# be converted
|
26
|
+
# @return [::CloudEvents::Event] if the request could be converted
|
28
27
|
# @return [nil] if the event format was not recognized.
|
29
28
|
#
|
30
29
|
def decode_rack_env env
|
31
|
-
content_type = CloudEvents::ContentType.new env["CONTENT_TYPE"]
|
30
|
+
content_type = ::CloudEvents::ContentType.new env["CONTENT_TYPE"]
|
32
31
|
return nil unless content_type.media_type == "application" && content_type.subtype_base == "json"
|
33
32
|
input = read_input_json env["rack.input"], content_type.charset
|
34
33
|
return nil unless input
|
@@ -85,14 +84,14 @@ module FunctionsFramework
|
|
85
84
|
return nil unless type && source
|
86
85
|
ce_data = convert_data context[:service], data
|
87
86
|
content_type = "application/json; charset=#{charset}"
|
88
|
-
CloudEvents::Event.new id: context[:id],
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
87
|
+
::CloudEvents::Event.new id: context[:id],
|
88
|
+
source: source,
|
89
|
+
type: type,
|
90
|
+
spec_version: "1.0",
|
91
|
+
data_content_type: content_type,
|
92
|
+
data: ce_data,
|
93
|
+
subject: subject,
|
94
|
+
time: context[:timestamp]
|
96
95
|
end
|
97
96
|
|
98
97
|
def convert_source service, resource
|
@@ -80,7 +80,8 @@ module FunctionsFramework
|
|
80
80
|
#
|
81
81
|
# You must provide a name for the function, and a block that implemets the
|
82
82
|
# function. The block should take _one_ argument: the event object of type
|
83
|
-
#
|
83
|
+
# [`CloudEvents::Event`](https://rubydoc.info/gems/cloud_events/CloudEvents/Event).
|
84
|
+
# Any return value is ignored.
|
84
85
|
#
|
85
86
|
# @param name [String] The function name
|
86
87
|
# @param block [Proc] The function code as a proc
|
@@ -344,7 +344,7 @@ module FunctionsFramework
|
|
344
344
|
string_response response, "text/plain", 200
|
345
345
|
when ::Hash
|
346
346
|
string_response ::JSON.dump(response), "application/json", 200
|
347
|
-
when CloudEvents::CloudEventsError
|
347
|
+
when ::CloudEvents::CloudEventsError
|
348
348
|
cloud_events_error_response response
|
349
349
|
when ::StandardError
|
350
350
|
error_response "#{response.class}: #{response.message}\n#{response.backtrace}\n"
|
@@ -405,7 +405,7 @@ module FunctionsFramework
|
|
405
405
|
def initialize function, config
|
406
406
|
super config
|
407
407
|
@function = function
|
408
|
-
@cloud_events = CloudEvents::HttpBinding.default
|
408
|
+
@cloud_events = ::CloudEvents::HttpBinding.default
|
409
409
|
@legacy_events = LegacyEventConverter.new
|
410
410
|
end
|
411
411
|
|
@@ -415,11 +415,11 @@ module FunctionsFramework
|
|
415
415
|
event = decode_event env
|
416
416
|
response =
|
417
417
|
case event
|
418
|
-
when CloudEvents::Event
|
418
|
+
when ::CloudEvents::Event
|
419
419
|
handle_cloud_event event, logger
|
420
420
|
when ::Array
|
421
|
-
CloudEvents::HttpContentError.new "Batched CloudEvents are not supported"
|
422
|
-
when CloudEvents::CloudEventsError
|
421
|
+
::CloudEvents::HttpContentError.new "Batched CloudEvents are not supported"
|
422
|
+
when ::CloudEvents::CloudEventsError
|
423
423
|
event
|
424
424
|
else
|
425
425
|
raise "Unexpected event type: #{event.class}"
|
@@ -432,8 +432,8 @@ module FunctionsFramework
|
|
432
432
|
def decode_event env
|
433
433
|
@cloud_events.decode_rack_env(env) ||
|
434
434
|
@legacy_events.decode_rack_env(env) ||
|
435
|
-
raise(CloudEvents::HttpContentError, "Unrecognized event format")
|
436
|
-
rescue CloudEvents::CloudEventsError => e
|
435
|
+
raise(::CloudEvents::HttpContentError, "Unrecognized event format")
|
436
|
+
rescue ::CloudEvents::CloudEventsError => e
|
437
437
|
e
|
438
438
|
end
|
439
439
|
|
@@ -100,7 +100,7 @@ module FunctionsFramework
|
|
100
100
|
# be of type :cloud_event`.
|
101
101
|
#
|
102
102
|
# @param name [String] The name of the function to call
|
103
|
-
# @param event [
|
103
|
+
# @param event [::CloudEvents::Event] The event to send
|
104
104
|
# @return [nil]
|
105
105
|
#
|
106
106
|
def call_event name, event
|
@@ -174,23 +174,35 @@ module FunctionsFramework
|
|
174
174
|
# @param source [String,URI] Event source (optional)
|
175
175
|
# @param type [String] Event type (optional)
|
176
176
|
# @param spec_version [String] Spec version (optional)
|
177
|
-
# @param data_content_type [String
|
177
|
+
# @param data_content_type [String,::CloudEvents::ContentType]
|
178
178
|
# Content type for the data (optional)
|
179
179
|
# @param data_schema [String,URI] Data schema (optional)
|
180
180
|
# @param subject [String] Subject (optional)
|
181
181
|
# @param time [String,DateTime] Event timestamp (optional)
|
182
|
-
# @return [
|
182
|
+
# @return [::CloudEvents::Event]
|
183
183
|
#
|
184
184
|
def make_cloud_event data,
|
185
|
-
id: nil,
|
186
|
-
|
185
|
+
id: nil,
|
186
|
+
source: nil,
|
187
|
+
type: nil,
|
188
|
+
spec_version: nil,
|
189
|
+
data_content_type: nil,
|
190
|
+
data_schema: nil,
|
191
|
+
subject: nil,
|
192
|
+
time: nil
|
187
193
|
id ||= "random-id-#{rand 100_000_000}"
|
188
194
|
source ||= "functions-framework-testing"
|
189
195
|
type ||= "com.example.test"
|
190
196
|
spec_version ||= "1.0"
|
191
|
-
CloudEvents::Event.new id:
|
192
|
-
|
193
|
-
|
197
|
+
::CloudEvents::Event.new id: id,
|
198
|
+
source: source,
|
199
|
+
type: type,
|
200
|
+
spec_version: spec_version,
|
201
|
+
data_content_type: data_content_type,
|
202
|
+
data_schema: data_schema,
|
203
|
+
subject: subject,
|
204
|
+
time: time,
|
205
|
+
data: data
|
194
206
|
end
|
195
207
|
|
196
208
|
extend self
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: functions_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Azuma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cloud_events
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.1'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: puma
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -60,15 +74,6 @@ files:
|
|
60
74
|
- docs/writing-functions.md
|
61
75
|
- lib/functions_framework.rb
|
62
76
|
- lib/functions_framework/cli.rb
|
63
|
-
- lib/functions_framework/cloud_events.rb
|
64
|
-
- lib/functions_framework/cloud_events/content_type.rb
|
65
|
-
- lib/functions_framework/cloud_events/errors.rb
|
66
|
-
- lib/functions_framework/cloud_events/event.rb
|
67
|
-
- lib/functions_framework/cloud_events/event/field_interpreter.rb
|
68
|
-
- lib/functions_framework/cloud_events/event/v0.rb
|
69
|
-
- lib/functions_framework/cloud_events/event/v1.rb
|
70
|
-
- lib/functions_framework/cloud_events/http_binding.rb
|
71
|
-
- lib/functions_framework/cloud_events/json_format.rb
|
72
77
|
- lib/functions_framework/function.rb
|
73
78
|
- lib/functions_framework/legacy_event_converter.rb
|
74
79
|
- lib/functions_framework/registry.rb
|
@@ -82,7 +87,7 @@ metadata:
|
|
82
87
|
changelog_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/CHANGELOG.md
|
83
88
|
source_code_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby
|
84
89
|
bug_tracker_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues
|
85
|
-
documentation_uri: https://rubydoc.info/gems/functions_framework/0.
|
90
|
+
documentation_uri: https://rubydoc.info/gems/functions_framework/0.5.0
|
86
91
|
post_install_message:
|
87
92
|
rdoc_options: []
|
88
93
|
require_paths:
|
@@ -1,45 +0,0 @@
|
|
1
|
-
# Copyright 2020 Google LLC
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
require "functions_framework/cloud_events/content_type"
|
16
|
-
require "functions_framework/cloud_events/errors"
|
17
|
-
require "functions_framework/cloud_events/event"
|
18
|
-
require "functions_framework/cloud_events/http_binding"
|
19
|
-
require "functions_framework/cloud_events/json_format"
|
20
|
-
|
21
|
-
module FunctionsFramework
|
22
|
-
##
|
23
|
-
# CloudEvents implementation.
|
24
|
-
#
|
25
|
-
# This is a Ruby implementation of the [CloudEvents](https://cloudevents.io)
|
26
|
-
# specification. It supports both
|
27
|
-
# [CloudEvents 0.3](https://github.com/cloudevents/spec/blob/v0.3/spec.md) and
|
28
|
-
# [CloudEvents 1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md).
|
29
|
-
#
|
30
|
-
module CloudEvents
|
31
|
-
# @private
|
32
|
-
SUPPORTED_SPEC_VERSIONS = ["0.3", "1.0"].freeze
|
33
|
-
|
34
|
-
class << self
|
35
|
-
##
|
36
|
-
# The spec versions supported by this implementation.
|
37
|
-
#
|
38
|
-
# @return [Array<String>]
|
39
|
-
#
|
40
|
-
def supported_spec_versions
|
41
|
-
SUPPORTED_SPEC_VERSIONS
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,222 +0,0 @@
|
|
1
|
-
# Copyright 2020 Google LLC
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
module FunctionsFramework
|
16
|
-
module CloudEvents
|
17
|
-
##
|
18
|
-
# A parsed content-type header.
|
19
|
-
#
|
20
|
-
# This object represents the information contained in a Content-Type,
|
21
|
-
# obtained by parsing the header according to RFC 2045.
|
22
|
-
#
|
23
|
-
# Case-insensitive fields, such as media_type and subtype, are normalized
|
24
|
-
# to lower case.
|
25
|
-
#
|
26
|
-
# If parsing fails, this class will try to get as much information as it
|
27
|
-
# can, and fill the rest with defaults as recommended in RFC 2045 sec 5.2.
|
28
|
-
# In case of a parsing error, the {#error_message} field will be set.
|
29
|
-
#
|
30
|
-
class ContentType
|
31
|
-
##
|
32
|
-
# Parse the given header value.
|
33
|
-
#
|
34
|
-
# @param string [String] Content-Type header value in RFC 2045 format
|
35
|
-
#
|
36
|
-
def initialize string
|
37
|
-
@string = string
|
38
|
-
@media_type = "text"
|
39
|
-
@subtype_base = @subtype = "plain"
|
40
|
-
@subtype_format = nil
|
41
|
-
@params = []
|
42
|
-
@charset = "us-ascii"
|
43
|
-
@error_message = nil
|
44
|
-
parse consume_comments string.strip
|
45
|
-
@canonical_string = "#{@media_type}/#{@subtype}" +
|
46
|
-
@params.map { |k, v| "; #{k}=#{maybe_quote v}" }.join
|
47
|
-
end
|
48
|
-
|
49
|
-
##
|
50
|
-
# The original header content string.
|
51
|
-
# @return [String]
|
52
|
-
#
|
53
|
-
attr_reader :string
|
54
|
-
alias to_s string
|
55
|
-
|
56
|
-
##
|
57
|
-
# A "canonical" header content string with spacing and capitalization
|
58
|
-
# normalized.
|
59
|
-
# @return [String]
|
60
|
-
#
|
61
|
-
attr_reader :canonical_string
|
62
|
-
|
63
|
-
##
|
64
|
-
# The media type.
|
65
|
-
# @return [String]
|
66
|
-
#
|
67
|
-
attr_reader :media_type
|
68
|
-
|
69
|
-
##
|
70
|
-
# The entire content subtype (which could include an extension delimited
|
71
|
-
# by a plus sign).
|
72
|
-
# @return [String]
|
73
|
-
#
|
74
|
-
attr_reader :subtype
|
75
|
-
|
76
|
-
##
|
77
|
-
# The portion of the content subtype before any plus sign.
|
78
|
-
# @return [String]
|
79
|
-
#
|
80
|
-
attr_reader :subtype_base
|
81
|
-
|
82
|
-
##
|
83
|
-
# The portion of the content subtype after any plus sign, or nil if there
|
84
|
-
# is no plus sign in the subtype.
|
85
|
-
# @return [String,nil]
|
86
|
-
#
|
87
|
-
attr_reader :subtype_format
|
88
|
-
|
89
|
-
##
|
90
|
-
# An array of parameters, each element as a two-element array of the
|
91
|
-
# parameter name and value.
|
92
|
-
# @return [Array<Array(String,String)>]
|
93
|
-
#
|
94
|
-
attr_reader :params
|
95
|
-
|
96
|
-
##
|
97
|
-
# The charset, defaulting to "us-ascii" if none is explicitly set.
|
98
|
-
# @return [String]
|
99
|
-
#
|
100
|
-
attr_reader :charset
|
101
|
-
|
102
|
-
##
|
103
|
-
# The error message when parsing, or `nil` if there was no error message.
|
104
|
-
# @return [String,nil]
|
105
|
-
#
|
106
|
-
attr_reader :error_message
|
107
|
-
|
108
|
-
##
|
109
|
-
# An array of values for the given parameter name
|
110
|
-
# @param key [String]
|
111
|
-
# @return [Array<String>]
|
112
|
-
#
|
113
|
-
def param_values key
|
114
|
-
key = key.downcase
|
115
|
-
@params.inject([]) { |a, (k, v)| key == k ? a << v : a }
|
116
|
-
end
|
117
|
-
|
118
|
-
## @private
|
119
|
-
def == other
|
120
|
-
other.is_a?(ContentType) && canonical_string == other.canonical_string
|
121
|
-
end
|
122
|
-
alias eql? ==
|
123
|
-
|
124
|
-
## @private
|
125
|
-
def hash
|
126
|
-
canonical_string.hash
|
127
|
-
end
|
128
|
-
|
129
|
-
## @private
|
130
|
-
class ParseError < ::StandardError
|
131
|
-
end
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def parse str
|
136
|
-
@media_type, str = consume_token str, downcase: true, error_message: "Failed to parse media type"
|
137
|
-
str = consume_special str, "/"
|
138
|
-
@subtype, str = consume_token str, downcase: true, error_message: "Failed to parse subtype"
|
139
|
-
@subtype_base, @subtype_format = @subtype.split "+", 2
|
140
|
-
until str.empty?
|
141
|
-
str = consume_special str, ";"
|
142
|
-
name, str = consume_token str, downcase: true, error_message: "Faled to parse attribute name"
|
143
|
-
str = consume_special str, "=", error_message: "Failed to find value for attribute #{name}"
|
144
|
-
val, str = consume_token_or_quoted str, error_message: "Failed to parse value for attribute #{name}"
|
145
|
-
@params << [name, val]
|
146
|
-
@charset = val if name == "charset"
|
147
|
-
end
|
148
|
-
rescue ParseError => e
|
149
|
-
@error_message = e.message
|
150
|
-
end
|
151
|
-
|
152
|
-
def consume_token str, downcase: false, error_message: nil
|
153
|
-
match = /^([\w!#\$%&'\*\+\.\^`\{\|\}-]+)(.*)$/.match str
|
154
|
-
raise ParseError, error_message || "Expected token" unless match
|
155
|
-
token = match[1]
|
156
|
-
token.downcase! if downcase
|
157
|
-
str = consume_comments match[2].strip
|
158
|
-
[token, str]
|
159
|
-
end
|
160
|
-
|
161
|
-
def consume_special str, expected, error_message: nil
|
162
|
-
raise ParseError, error_message || "Expected #{expected.inspect}" unless str.start_with? expected
|
163
|
-
consume_comments str[1..-1].strip
|
164
|
-
end
|
165
|
-
|
166
|
-
def consume_token_or_quoted str, error_message: nil
|
167
|
-
return consume_token str unless str.start_with? '"'
|
168
|
-
arr = []
|
169
|
-
index = 1
|
170
|
-
loop do
|
171
|
-
char = str[index]
|
172
|
-
case char
|
173
|
-
when nil
|
174
|
-
raise ParseError, error_message || "Quoted-string never finished"
|
175
|
-
when "\""
|
176
|
-
break
|
177
|
-
when "\\"
|
178
|
-
char = str[index + 1]
|
179
|
-
raise ParseError, error_message || "Quoted-string never finished" unless char
|
180
|
-
arr << char
|
181
|
-
index += 2
|
182
|
-
else
|
183
|
-
arr << char
|
184
|
-
index += 1
|
185
|
-
end
|
186
|
-
end
|
187
|
-
index += 1
|
188
|
-
str = consume_comments str[index..-1].strip
|
189
|
-
[arr.join, str]
|
190
|
-
end
|
191
|
-
|
192
|
-
def consume_comments str
|
193
|
-
return str unless str.start_with? "("
|
194
|
-
index = 1
|
195
|
-
loop do
|
196
|
-
char = str[index]
|
197
|
-
case char
|
198
|
-
when nil
|
199
|
-
raise ParseError, "Comment never finished"
|
200
|
-
when ")"
|
201
|
-
break
|
202
|
-
when "\\"
|
203
|
-
index += 2
|
204
|
-
when "("
|
205
|
-
str = consume_comments str[index..-1]
|
206
|
-
index = 0
|
207
|
-
else
|
208
|
-
index += 1
|
209
|
-
end
|
210
|
-
end
|
211
|
-
index += 1
|
212
|
-
consume_comments str[index..-1].strip
|
213
|
-
end
|
214
|
-
|
215
|
-
def maybe_quote str
|
216
|
-
return str if /^[\w!#\$%&'\*\+\.\^`\{\|\}-]+$/ =~ str
|
217
|
-
str = str.gsub("\\", "\\\\\\\\").gsub("\"", "\\\\\"")
|
218
|
-
"\"#{str}\""
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|