functions_framework 0.2.0 → 0.4.1

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