functions_framework 0.2.0 → 0.4.1

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.
@@ -17,7 +17,7 @@ module FunctionsFramework
17
17
  ##
18
18
  # Base class for all CloudEvents errors.
19
19
  #
20
- class CloudEventsError < ::RuntimeError
20
+ class CloudEventsError < ::StandardError
21
21
  end
22
22
 
23
23
  ##
@@ -15,15 +15,15 @@
15
15
  require "date"
16
16
  require "uri"
17
17
 
18
+ require "functions_framework/cloud_events/event/field_interpreter"
19
+ require "functions_framework/cloud_events/event/v0"
18
20
  require "functions_framework/cloud_events/event/v1"
19
21
 
20
22
  module FunctionsFramework
21
23
  module CloudEvents
22
24
  ##
23
- # CloudEvent object.
24
- #
25
- # An Event object represents a complete event, including both its data and
26
- # its context attributes. The following are true of all event objects:
25
+ # An Event object represents a complete CloudEvent, including both data and
26
+ # context attributes. The following are true of all event objects:
27
27
  #
28
28
  # * Event classes are defined within this module. For example, events
29
29
  # conforming to the CloudEvents 1.0 specification are of type
@@ -49,8 +49,11 @@ module FunctionsFramework
49
49
  # {CloudEvents::JsonFormat} to decode an event from JSON, or use
50
50
  # {CloudEvents::HttpBinding} to decode an event from an HTTP request.
51
51
  #
52
- # See https://github.com/cloudevents/spec/blob/master/spec.md for more
53
- # information about CloudEvents.
52
+ # See https://github.com/cloudevents/spec for more information about
53
+ # CloudEvents. The documentation for the individual event classes
54
+ # {FunctionsFramework::CloudEvents::Event::V0} and
55
+ # {FunctionsFramework::CloudEvents::Event::V1} also include links to their
56
+ # respective specifications.
54
57
  #
55
58
  module Event
56
59
  class << self
@@ -66,6 +69,8 @@ module FunctionsFramework
66
69
  #
67
70
  def create spec_version:, **kwargs
68
71
  case spec_version
72
+ when "0.3"
73
+ V0.new spec_version: spec_version, **kwargs
69
74
  when /^1(\.|$)/
70
75
  V1.new spec_version: spec_version, **kwargs
71
76
  else
@@ -0,0 +1,150 @@
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
+ module Event
18
+ ##
19
+ # A helper that extracts and interprets event fields from an input hash.
20
+ #
21
+ # @private
22
+ #
23
+ class FieldInterpreter
24
+ def initialize args
25
+ @args = keys_to_strings args
26
+ @attributes = {}
27
+ end
28
+
29
+ def finish_attributes
30
+ @attributes.merge! @args
31
+ @args = {}
32
+ @attributes
33
+ end
34
+
35
+ def string keys, required: false
36
+ object keys, required: required do |value|
37
+ case value
38
+ when ::String
39
+ raise AttributeError, "The #{keys.first} field cannot be empty" if value.empty?
40
+ [value, value]
41
+ else
42
+ raise AttributeError, "Illegal type for #{keys.first}:" \
43
+ " String expected but #{value.class} found"
44
+ end
45
+ end
46
+ end
47
+
48
+ def uri keys, required: false
49
+ object keys, required: required do |value|
50
+ case value
51
+ when ::String
52
+ raise AttributeError, "The #{keys.first} field cannot be empty" if value.empty?
53
+ begin
54
+ [::URI.parse(value), value]
55
+ rescue ::URI::InvalidURIError => e
56
+ raise AttributeError, "Illegal format for #{keys.first}: #{e.message}"
57
+ end
58
+ when ::URI::Generic
59
+ [value, value.to_s]
60
+ else
61
+ raise AttributeError, "Illegal type for #{keys.first}:" \
62
+ " String or URI expected but #{value.class} found"
63
+ end
64
+ end
65
+ end
66
+
67
+ def rfc3339_date_time keys, required: false
68
+ object keys, required: required do |value|
69
+ case value
70
+ when ::String
71
+ begin
72
+ [::DateTime.rfc3339(value), value]
73
+ rescue ::Date::Error => e
74
+ raise AttributeError, "Illegal format for #{keys.first}: #{e.message}"
75
+ end
76
+ when ::DateTime
77
+ [value, value.rfc3339]
78
+ when ::Time
79
+ value = value.to_datetime
80
+ [value, value.rfc3339]
81
+ else
82
+ raise AttributeError, "Illegal type for #{keys.first}:" \
83
+ " String, Time, or DateTime expected but #{value.class} found"
84
+ end
85
+ end
86
+ end
87
+
88
+ def content_type keys, required: false
89
+ object keys, required: required do |value|
90
+ case value
91
+ when ::String
92
+ raise AttributeError, "The #{keys.first} field cannot be empty" if value.empty?
93
+ [ContentType.new(value), value]
94
+ when ContentType
95
+ [value, value.to_s]
96
+ else
97
+ raise AttributeError, "Illegal type for #{keys.first}:" \
98
+ " String, or ContentType expected but #{value.class} found"
99
+ end
100
+ end
101
+ end
102
+
103
+ def spec_version keys, accept:
104
+ object keys, required: true do |value|
105
+ case value
106
+ when ::String
107
+ raise SpecVersionError, "Unrecognized specversion: #{value}" unless accept =~ value
108
+ [value, value]
109
+ else
110
+ raise AttributeError, "Illegal type for #{keys.first}:" \
111
+ " String expected but #{value.class} found"
112
+ end
113
+ end
114
+ end
115
+
116
+ UNDEFINED = Object.new
117
+
118
+ def object keys, required: false, allow_nil: false
119
+ value = UNDEFINED
120
+ keys.each do |key|
121
+ key_present = @args.key? key
122
+ val = @args.delete key
123
+ value = val if allow_nil && key_present || !allow_nil && !val.nil?
124
+ end
125
+ if value == UNDEFINED
126
+ raise AttributeError, "The #{keys.first} field is required" if required
127
+ return nil
128
+ end
129
+ if block_given?
130
+ converted, raw = yield value
131
+ else
132
+ converted = raw = value
133
+ end
134
+ @attributes[keys.first] = raw
135
+ converted
136
+ end
137
+
138
+ private
139
+
140
+ def keys_to_strings hash
141
+ result = {}
142
+ hash.each do |key, val|
143
+ result[key.to_s] = val
144
+ end
145
+ result
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,236 @@
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 "date"
16
+ require "uri"
17
+
18
+ module FunctionsFramework
19
+ module CloudEvents
20
+ module Event
21
+ ##
22
+ # A CloudEvents V0 data type.
23
+ #
24
+ # This object represents a complete CloudEvent, including the event data
25
+ # and context attributes. It supports the standard required and optional
26
+ # attributes defined in CloudEvents V0.3, and arbitrary extension
27
+ # attributes. All attribute values can be obtained (in their string form)
28
+ # via the {Event::V0#[]} method. Additionally, standard attributes have
29
+ # their own accessor methods that may return typed objects (such as
30
+ # `DateTime` for the `time` attribute).
31
+ #
32
+ # This object is immutable. The data and attribute values can be
33
+ # retrieved but not modified. To obtain an event with modifications, use
34
+ # the {#with} method to create a copy with the desired changes.
35
+ #
36
+ # See https://github.com/cloudevents/spec/blob/v0.3/spec.md for
37
+ # descriptions of the standard attributes.
38
+ #
39
+ class V0
40
+ include Event
41
+
42
+ ##
43
+ # Create a new cloud event object with the given data and attributes.
44
+ #
45
+ # Event attributes may be presented as keyword arguments, or as a Hash
46
+ # passed in via the `attributes` argument (but not both).
47
+ #
48
+ # The following standard attributes are supported and exposed as
49
+ # attribute methods on the object.
50
+ #
51
+ # * **:spec_version** (or **:specversion**) [`String`] - _required_ -
52
+ # The CloudEvents spec version (i.e. the `specversion` field.)
53
+ # * **:id** [`String`] - _required_ - The event `id` field.
54
+ # * **:source** [`String`, `URI`] - _required_ - The event `source`
55
+ # field.
56
+ # * **:type** [`String`] - _required_ - The event `type` field.
57
+ # * **:data** [`Object`] - _optional_ - The data associated with the
58
+ # event (i.e. the `data` field.)
59
+ # * **:data_content_encoding** (or **:datacontentencoding**)
60
+ # [`String`] - _optional_ - The content-encoding for the data (i.e.
61
+ # the `datacontentencoding` field.)
62
+ # * **:data_content_type** (or **:datacontenttype**) [`String`,
63
+ # {ContentType}] - _optional_ - The content-type for the data, if
64
+ # the data is a string (i.e. the event `datacontenttype` field.)
65
+ # * **:schema_url** (or **:schemaurl**) [`String`, `URI`] -
66
+ # _optional_ - The event `schemaurl` field.
67
+ # * **:subject** [`String`] - _optional_ - The event `subject` field.
68
+ # * **:time** [`String`, `DateTime`, `Time`] - _optional_ - The
69
+ # event `time` field.
70
+ #
71
+ # Any additional attributes are assumed to be extension attributes.
72
+ # They are not available as separate methods, but can be accessed via
73
+ # the {Event::V1#[]} operator.
74
+ #
75
+ # @param attributes [Hash] The data and attributes, as a hash.
76
+ # @param args [keywords] The data and attributes, as keyword arguments.
77
+ #
78
+ def initialize attributes: nil, **args
79
+ interpreter = FieldInterpreter.new attributes || args
80
+ @spec_version = interpreter.spec_version ["specversion", "spec_version"], accept: /^0\.3$/
81
+ @id = interpreter.string ["id"], required: true
82
+ @source = interpreter.uri ["source"], required: true
83
+ @type = interpreter.string ["type"], required: true
84
+ @data = interpreter.object ["data"], allow_nil: true
85
+ @data_content_encoding = interpreter.string ["datacontentencoding", "data_content_encoding"]
86
+ @data_content_type = interpreter.content_type ["datacontenttype", "data_content_type"]
87
+ @schema_url = interpreter.uri ["schemaurl", "schema_url"]
88
+ @subject = interpreter.string ["subject"]
89
+ @time = interpreter.rfc3339_date_time ["time"]
90
+ @attributes = interpreter.finish_attributes
91
+ end
92
+
93
+ ##
94
+ # Create and return a copy of this event with the given changes. See
95
+ # the constructor for the parameters that can be passed. In general,
96
+ # you can pass a new value for any attribute, or pass `nil` to remove
97
+ # an optional attribute.
98
+ #
99
+ # @param changes [keywords] See {#initialize} for a list of arguments.
100
+ # @return [FunctionFramework::CloudEvents::Event]
101
+ #
102
+ def with **changes
103
+ attributes = @attributes.merge changes
104
+ V0.new attributes: attributes
105
+ end
106
+
107
+ ##
108
+ # Return the value of the given named attribute. Both standard and
109
+ # extension attributes are supported.
110
+ #
111
+ # Attribute names must be given as defined in the standard CloudEvents
112
+ # specification. For example `specversion` rather than `spec_version`.
113
+ #
114
+ # Results are given in their "raw" form, generally a string. This may
115
+ # be different from the Ruby object returned from corresponding
116
+ # attribute methods. For example:
117
+ #
118
+ # event["time"] # => String rfc3339 representation
119
+ # event.time # => DateTime object
120
+ #
121
+ # @param key [String,Symbol] The attribute name.
122
+ # @return [String,nil]
123
+ #
124
+ def [] key
125
+ @attributes[key.to_s]
126
+ end
127
+
128
+ ##
129
+ # Return a hash representation of this event.
130
+ #
131
+ # @return [Hash]
132
+ #
133
+ def to_h
134
+ @attributes.dup
135
+ end
136
+
137
+ ##
138
+ # The `id` field. Required.
139
+ #
140
+ # @return [String]
141
+ #
142
+ attr_reader :id
143
+
144
+ ##
145
+ # The `source` field as a `URI` object. Required.
146
+ #
147
+ # @return [URI]
148
+ #
149
+ attr_reader :source
150
+
151
+ ##
152
+ # The `type` field. Required.
153
+ #
154
+ # @return [String]
155
+ #
156
+ attr_reader :type
157
+
158
+ ##
159
+ # The `specversion` field. Required.
160
+ #
161
+ # @return [String]
162
+ #
163
+ attr_reader :spec_version
164
+ alias specversion spec_version
165
+
166
+ ##
167
+ # The event-specific data, or `nil` if there is no data.
168
+ #
169
+ # Data may be one of the following types:
170
+ # * Binary data, represented by a `String` using the `ASCII-8BIT`
171
+ # encoding.
172
+ # * A string in some other encoding such as `UTF-8` or `US-ASCII`.
173
+ # * Any JSON data type, such as a Boolean, Integer, Array, Hash, or
174
+ # `nil`.
175
+ #
176
+ # @return [Object]
177
+ #
178
+ attr_reader :data
179
+
180
+ ##
181
+ # The optional `datacontentencoding` field as a `String` object, or
182
+ # `nil` if the field is absent.
183
+ #
184
+ # @return [String,nil]
185
+ #
186
+ attr_reader :data_content_encoding
187
+ alias datacontentencoding data_content_encoding
188
+
189
+ ##
190
+ # The optional `datacontenttype` field as a
191
+ # {FunctionsFramework::CloudEvents::ContentType} object, or `nil` if
192
+ # the field is absent.
193
+ #
194
+ # @return [FunctionsFramework::CloudEvents::ContentType,nil]
195
+ #
196
+ attr_reader :data_content_type
197
+ alias datacontenttype data_content_type
198
+
199
+ ##
200
+ # The optional `schemaurl` field as a `URI` object, or `nil` if the
201
+ # field is absent.
202
+ #
203
+ # @return [URI,nil]
204
+ #
205
+ attr_reader :schema_url
206
+ alias schemaurl schema_url
207
+
208
+ ##
209
+ # The optional `subject` field, or `nil` if the field is absent.
210
+ #
211
+ # @return [String,nil]
212
+ #
213
+ attr_reader :subject
214
+
215
+ ##
216
+ # The optional `time` field as a `DateTime` object, or `nil` if the
217
+ # field is absent.
218
+ #
219
+ # @return [DateTime,nil]
220
+ #
221
+ attr_reader :time
222
+
223
+ ## @private
224
+ def == other
225
+ other.is_a?(V1) && @attributes == other.instance_variable_get(:@attributes)
226
+ end
227
+ alias eql? ==
228
+
229
+ ## @private
230
+ def hash
231
+ @hash ||= @attributes.hash
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
@@ -21,9 +21,9 @@ module FunctionsFramework
21
21
  ##
22
22
  # A CloudEvents V1 data type.
23
23
  #
24
- # This object a complete CloudEvent, including the event data and its
25
- # context attributes. It supports the standard required and optional
26
- # attributes defined in CloudEvents V1, and arbitrary extension
24
+ # This object represents a complete CloudEvent, including the event data
25
+ # and context attributes. It supports the standard required and optional
26
+ # attributes defined in CloudEvents V1.0, and arbitrary extension
27
27
  # attributes. All attribute values can be obtained (in their string form)
28
28
  # via the {Event::V1#[]} method. Additionally, standard attributes have
29
29
  # their own accessor methods that may return typed objects (such as
@@ -33,7 +33,7 @@ module FunctionsFramework
33
33
  # retrieved but not modified. To obtain an event with modifications, use
34
34
  # the {#with} method to create a copy with the desired changes.
35
35
  #
36
- # See https://github.com/cloudevents/spec/blob/master/spec.md for
36
+ # See https://github.com/cloudevents/spec/blob/v1.0/spec.md for
37
37
  # descriptions of the standard attributes.
38
38
  #
39
39
  class V1
@@ -72,21 +72,18 @@ module FunctionsFramework
72
72
  # @param attributes [Hash] The data and attributes, as a hash.
73
73
  # @param args [keywords] The data and attributes, as keyword arguments.
74
74
  #
75
- def initialize attributes: nil, **args # rubocop:disable Metrics/AbcSize
76
- args = keys_to_strings(attributes || args)
77
- @attributes = {}
78
- @spec_version, _unused = interpret_string args, ["specversion", "spec_version"], required: true
79
- raise SpecVersionError, "Unrecognized specversion: #{@spec_version}" unless /^1(\.|$)/ =~ @spec_version
80
- @id, _unused = interpret_string args, ["id"], required: true
81
- @source, @source_string = interpret_uri args, ["source"], required: true
82
- @type, _unused = interpret_string args, ["type"], required: true
83
- @data, _unused = interpret_value args, ["data"], allow_nil: true
84
- @data_content_type, @data_content_type_string =
85
- interpret_content_type args, ["datacontenttype", "data_content_type"]
86
- @data_schema, @data_schema_string = interpret_uri args, ["dataschema", "data_schema"]
87
- @subject, _unused = interpret_string args, ["subject"]
88
- @time, @time_string = interpret_date_time args, ["time"]
89
- @attributes.merge! args
75
+ def initialize attributes: nil, **args
76
+ interpreter = FieldInterpreter.new attributes || args
77
+ @spec_version = interpreter.spec_version ["specversion", "spec_version"], accept: /^1(\.|$)/
78
+ @id = interpreter.string ["id"], required: true
79
+ @source = interpreter.uri ["source"], required: true
80
+ @type = interpreter.string ["type"], required: true
81
+ @data = interpreter.object ["data"], allow_nil: true
82
+ @data_content_type = interpreter.content_type ["datacontenttype", "data_content_type"]
83
+ @data_schema = interpreter.uri ["dataschema", "data_schema"]
84
+ @subject = interpreter.string ["subject"]
85
+ @time = interpreter.rfc3339_date_time ["time"]
86
+ @attributes = interpreter.finish_attributes
90
87
  end
91
88
 
92
89
  ##
@@ -99,7 +96,7 @@ module FunctionsFramework
99
96
  # @return [FunctionFramework::CloudEvents::Event]
100
97
  #
101
98
  def with **changes
102
- attributes = @attributes.merge keys_to_strings changes
99
+ attributes = @attributes.merge changes
103
100
  V1.new attributes: attributes
104
101
  end
105
102
 
@@ -111,12 +108,11 @@ module FunctionsFramework
111
108
  # specification. For example `specversion` rather than `spec_version`.
112
109
  #
113
110
  # Results are given in their "raw" form, generally a string. This may
114
- # be different from what is returned from corresponding attribute
115
- # methods. For example:
111
+ # be different from the Ruby object returned from corresponding
112
+ # attribute methods. For example:
116
113
  #
117
114
  # event["time"] # => String rfc3339 representation
118
115
  # event.time # => DateTime object
119
- # event.time_string # => String rfc3339 representation
120
116
  #
121
117
  # @param key [String,Symbol] The attribute name.
122
118
  # @return [String,nil]
@@ -148,13 +144,6 @@ module FunctionsFramework
148
144
  #
149
145
  attr_reader :source
150
146
 
151
- ##
152
- # The string representation of the `source` field. Required.
153
- #
154
- # @return [String]
155
- #
156
- attr_reader :source_string
157
-
158
147
  ##
159
148
  # The `type` field. Required.
160
149
  #
@@ -177,8 +166,8 @@ module FunctionsFramework
177
166
  # * Binary data, represented by a `String` using the `ASCII-8BIT`
178
167
  # encoding.
179
168
  # * A string in some other encoding such as `UTF-8` or `US-ASCII`.
180
- # * Any JSON data type, such as String, boolean, Integer, Array, or
181
- # Hash
169
+ # * Any JSON data type, such as a Boolean, Integer, Array, Hash, or
170
+ # `nil`.
182
171
  #
183
172
  # @return [Object]
184
173
  #
@@ -194,15 +183,6 @@ module FunctionsFramework
194
183
  attr_reader :data_content_type
195
184
  alias datacontenttype data_content_type
196
185
 
197
- ##
198
- # The string representation of the optional `datacontenttype` field, or
199
- # `nil` if the field is absent.
200
- #
201
- # @return [String,nil]
202
- #
203
- attr_reader :data_content_type_string
204
- alias datacontenttype_string data_content_type_string
205
-
206
186
  ##
207
187
  # The optional `dataschema` field as a `URI` object, or `nil` if the
208
188
  # field is absent.
@@ -212,15 +192,6 @@ module FunctionsFramework
212
192
  attr_reader :data_schema
213
193
  alias dataschema data_schema
214
194
 
215
- ##
216
- # The string representation of the optional `dataschema` field, or
217
- # `nil` if the field is absent.
218
- #
219
- # @return [String,nil]
220
- #
221
- attr_reader :data_schema_string
222
- alias dataschema_string data_schema_string
223
-
224
195
  ##
225
196
  # The optional `subject` field, or `nil` if the field is absent.
226
197
  #
@@ -236,14 +207,6 @@ module FunctionsFramework
236
207
  #
237
208
  attr_reader :time
238
209
 
239
- ##
240
- # The rfc3339 string representation of the optional `time` field, or
241
- # `nil` if the field is absent.
242
- #
243
- # @return [String,nil]
244
- #
245
- attr_reader :time_string
246
-
247
210
  ## @private
248
211
  def == other
249
212
  other.is_a?(V1) && @attributes == other.instance_variable_get(:@attributes)
@@ -254,109 +217,6 @@ module FunctionsFramework
254
217
  def hash
255
218
  @hash ||= @attributes.hash
256
219
  end
257
-
258
- private
259
-
260
- def keys_to_strings hash
261
- result = {}
262
- hash.each do |key, val|
263
- result[key.to_s] = val
264
- end
265
- result
266
- end
267
-
268
- def interpret_string args, keys, required: false
269
- interpret_value args, keys, required: required do |value|
270
- case value
271
- when ::String
272
- raise AttributeError, "The #{keys.last} field cannot be empty" if value.empty?
273
- [value, value]
274
- else
275
- raise AttributeError, "Illegal type for #{keys.last}:" \
276
- " String expected but #{value.class} found"
277
- end
278
- end
279
- end
280
-
281
- def interpret_uri args, keys, required: false
282
- interpret_value args, keys, required: required do |value|
283
- case value
284
- when ::String
285
- raise AttributeError, "The #{keys.last} field cannot be empty" if value.empty?
286
- begin
287
- [::URI.parse(value), value]
288
- rescue ::URI::InvalidURIError => e
289
- raise AttributeError, "Illegal format for #{keys.last}: #{e.message}"
290
- end
291
- when ::URI::Generic
292
- [value, value.to_s]
293
- else
294
- raise AttributeError, "Illegal type for #{keys.last}:" \
295
- " String or URI expected but #{value.class} found"
296
- end
297
- end
298
- end
299
-
300
- def interpret_date_time args, keys, required: false
301
- interpret_value args, keys, required: required do |value|
302
- case value
303
- when ::String
304
- begin
305
- [::DateTime.rfc3339(value), value]
306
- rescue ::Date::Error => e
307
- raise AttributeError, "Illegal format for #{keys.last}: #{e.message}"
308
- end
309
- when ::DateTime
310
- [value, value.rfc3339]
311
- when ::Time
312
- value = value.to_datetime
313
- [value, value.rfc3339]
314
- else
315
- raise AttributeError, "Illegal type for #{keys.last}:" \
316
- " String, Time, or DateTime expected but #{value.class} found"
317
- end
318
- end
319
- end
320
-
321
- def interpret_content_type args, keys, required: false
322
- interpret_value args, keys, required: required do |value|
323
- case value
324
- when ::String
325
- raise AttributeError, "The #{keys.last} field cannot be empty" if value.empty?
326
- [ContentType.new(value), value]
327
- when ContentType
328
- [value, value.to_s]
329
- else
330
- raise AttributeError, "Illegal type for #{keys.last}:" \
331
- " String, or ContentType expected but #{value.class} found"
332
- end
333
- end
334
- end
335
-
336
- def interpret_value args, keys, required: false, allow_nil: false
337
- value = nil
338
- found = false
339
- keys.each do |key|
340
- key_present = args.key? key
341
- val = args.delete key
342
- if allow_nil && key_present || !allow_nil && !val.nil?
343
- value = val
344
- found = true
345
- end
346
- end
347
- if found
348
- if block_given?
349
- converted, raw = yield value
350
- else
351
- converted = raw = value
352
- end
353
- @attributes[keys.first] = raw
354
- [converted, raw]
355
- else
356
- raise AttributeError, "The #{keys.last} field is required" if required
357
- [nil, nil]
358
- end
359
- end
360
220
  end
361
221
  end
362
222
  end