@@ -14,10 +14,29 @@ module Event
1414 # This object represents a complete CloudEvent, including the event data
1515 # and context attributes. It supports the standard required and optional
1616 # attributes defined in CloudEvents V1.0, and arbitrary extension
17- # attributes. All attribute values can be obtained (in their string form)
17+ # attributes.
18+ #
19+ # Values for most attributes can be obtained in their encoded string form
1820 # via the {Event::V1#[]} method. Additionally, standard attributes have
19- # their own accessor methods that may return typed objects (such as
20- # `DateTime` for the `time` attribute).
21+ # their own accessor methods that may return decoded Ruby objects (such as
22+ # a `DateTime` object for the `time` attribute).
23+ #
24+ # The `data` attribute is treated specially because it is subject to
25+ # arbitrary encoding governed by the `datacontenttype` attribute. Data is
26+ # expressed in two related fields: {Event::V1#data} and
27+ # {Event::V1#data_encoded}. The former, `data`, _may_ be an arbitrary Ruby
28+ # object representing the decoded form of the data (for example, a Hash for
29+ # most JSON-formatted data.) The latter, `data_encoded`, _must_, if
30+ # present, be a Ruby String object representing the encoded string or
31+ # byte array form of the data.
32+ #
33+ # When the CloudEvents Ruby SDK encodes an event for transmission, it will
34+ # use the `data_encoded` field if present. Otherwise, it will attempt to
35+ # encode the `data` field using any available encoder that recognizes the
36+ # content-type. Currently, text and JSON types are supported. If the type
37+ # is not supported, event encoding may fail. It is thus recommended that
38+ # applications provide a `data_encoded` string, if the `data` object is
39+ # nontrivially encoded.
2140 #
2241 # This object is immutable, and Ractor-shareable on Ruby 3. The data and
2342 # attribute values can be retrieved but not modified. To obtain an event
@@ -33,8 +52,13 @@ class V1
3352 ##
3453 # Create a new cloud event object with the given data and attributes.
3554 #
55+ # ### Specifying event attributes
56+ #
3657 # Event attributes may be presented as keyword arguments, or as a Hash
37- # passed in via the `attributes` argument (but not both).
58+ # passed in via the special `:set_attributes` keyword argument (but not
59+ # both). The `:set_attributes` keyword argument is useful for passing in
60+ # attributes whose keys are strings rather than symbols, which some
61+ # versions of Ruby will not accept as keyword arguments.
3862 #
3963 # The following standard attributes are supported and exposed as
4064 # attribute methods on the object.
@@ -45,11 +69,15 @@ class V1
4569 # * **:source** [`String`, `URI`] - _required_ - The event `source`
4670 # field.
4771 # * **:type** [`String`] - _required_ - The event `type` field.
48- # * **:data** [`Object`] - _optional_ - The data associated with the
49- # event (i.e. the `data` field).
72+ # * **:data** [`Object`] - _optional_ - The "decoded" Ruby object form
73+ # of the event `data` field, if known. (e.g. a Hash representing a
74+ # JSON document)
75+ # * **:data_encoded** [`String`] - _optional_ - The "encoded" string
76+ # form of the event `data` field, if known. This should be set along
77+ # with the `data_content_type`.
5078 # * **:data_content_type** (or **:datacontenttype**) [`String`,
51- # {ContentType}] - _optional_ - The content-type for the data, if
52- # the data is a string (i.e. the event `datacontenttype` field.)
79+ # {ContentType}] - _optional_ - The content-type for the encoded data
80+ # (i.e. the event `datacontenttype` field.)
5381 # * **:data_schema** (or **:dataschema**) [`String`, `URI`] -
5482 # _optional_ - The event `dataschema` field.
5583 # * **:subject** [`String`] - _optional_ - The event `subject` field.
@@ -65,16 +93,63 @@ class V1
6593 # `:data` field, for example if you pass a structured hash. If this is an
6694 # issue, make a deep copy of objects before passing to this constructor.
6795 #
68- # @param attributes [Hash] The data and attributes, as a hash.
96+ # ### Specifying payload data
97+ #
98+ # Typically you should provide _both_ the `:data` and `:data_encoded`
99+ # fields, the former representing the decoded (Ruby object) form of the
100+ # data, and the second providing a hint to formatters and protocol
101+ # bindings for how to seralize the data. In this case, the {#data} and
102+ # {#data_encoded} methods will return the corresponding values, and
103+ # {#data_decoded?} will return true to indicate that {#data} represents
104+ # the decoded form.
105+ #
106+ # If you provide _only_ the `:data` field, omitting `:data_encoded`, then
107+ # the value is expected to represent the decoded (Ruby object) form of
108+ # the data. The {#data} method will return this decoded value, and
109+ # {#data_decoded?} will return true. The {#data_encoded} method will
110+ # return nil.
111+ # When serializing such an event, it will be up to the formatter or
112+ # protocol binding to encode the data. This means serialization _could_
113+ # fail if the formatter does not understand the data's content type.
114+ # Omitting `:data_encoded` is common if the content type is JSON related
115+ # (e.g. `application/json`) and the event is being encoded in JSON
116+ # structured format, because the data encoding is trivial. This form can
117+ # also be used when the content type is `text/*`, for which encoding is
118+ # also trivial.
119+ #
120+ # If you provide _only_ the `:data_encoded` field, omitting `:data`, then
121+ # the value is expected to represent the encoded (string) form of the
122+ # data. The {#data_encoded} method will return this value. Additionally,
123+ # the {#data} method will return the same _encoded_ value, and
124+ # {#data_decoded?} will return false.
125+ # Event objects of this form may be returned from a protocol binding when
126+ # it decodes an event with a `datacontenttype` that it does not know how
127+ # to interpret. Applications should query {#data_decoded?} to determine
128+ # whether the {#data} method returns encoded or decoded data.
129+ #
130+ # If you provide _neither_ `:data` nor `:data_encoded`, the event will
131+ # have no payload data. Both {#data} and {#data_encoded} will return nil,
132+ # and {#data_decoded?} will return false. (Additionally, {#data?} will
133+ # return false to signal the absence of any data.)
134+ #
135+ # @param set_attributes [Hash] The data and attributes, as a hash.
136+ # (Also available as `attributes` but this usage is deprecated.)
69137 # @param args [keywords] The data and attributes, as keyword arguments.
70138 #
71- def initialize attributes : nil , **args
72- interpreter = FieldInterpreter . new attributes || args
139+ def initialize set_attributes : nil , attributes : nil , **args
140+ interpreter = FieldInterpreter . new set_attributes || attributes || args
73141 @spec_version = interpreter . spec_version [ "specversion" , "spec_version" ] , accept : /^1(\. |$)/
74142 @id = interpreter . string [ "id" ] , required : true
75143 @source = interpreter . uri [ "source" ] , required : true
76144 @type = interpreter . string [ "type" ] , required : true
145+ @data_encoded = interpreter . string [ "data_encoded" ] , allow_empty : true
77146 @data = interpreter . data_object [ "data" ]
147+ if @data == FieldInterpreter ::UNDEFINED
148+ @data = @data_encoded
149+ @data_decoded = false
150+ else
151+ @data_decoded = true
152+ end
78153 @data_content_type = interpreter . content_type [ "datacontenttype" , "data_content_type" ]
79154 @data_schema = interpreter . uri [ "dataschema" , "data_schema" ]
80155 @subject = interpreter . string [ "subject" ]
@@ -93,8 +168,14 @@ def initialize attributes: nil, **args
93168 # @return [FunctionFramework::CloudEvents::Event]
94169 #
95170 def with **changes
96- attributes = @attributes . merge changes
97- V1 . new attributes : attributes
171+ changes = Utils . keys_to_strings changes
172+ attributes = @attributes . dup
173+ if changes . key? ( "data" ) || changes . key? ( "data_encoded" )
174+ attributes . delete "data"
175+ attributes . delete "data_encoded"
176+ end
177+ attributes . merge! changes
178+ V1 . new set_attributes : attributes
98179 end
99180
100181 ##
@@ -160,19 +241,61 @@ def to_h
160241 alias specversion spec_version
161242
162243 ##
163- # The event-specific data, or `nil` if there is no data.
244+ # The event `data` field, or `nil` if there is no data.
245+ #
246+ # This may return the data as an encoded string _or_ as a decoded Ruby
247+ # object. The {#data_decoded?} method specifies whether the `data` value
248+ # is decoded or encoded.
249+ #
250+ # In most cases, {#data} returns a decoded value, unless the event was
251+ # received from a source that could not decode the content. For example,
252+ # most protocol bindings understand how to decode JSON, so an event
253+ # received with a {#data_content_type} of `application/json` will usually
254+ # return a decoded object (usually a Hash) from {#data}.
164255 #
165- # Data may be one of the following types:
166- # * Binary data, represented by a `String` using the `ASCII-8BIT`
167- # encoding.
168- # * A string in some other encoding such as `UTF-8` or `US-ASCII`.
169- # * Any JSON data type, such as a Boolean, Integer, Array, Hash, or
170- # `nil`.
256+ # See also {#data_encoded} and {#data_decoded?}.
171257 #
172- # @return [Object]
258+ # @return [Object] if containing decoded data
259+ # @return [String] if containing encoded data
260+ # @return [nil] if there is no data
173261 #
174262 attr_reader :data
175263
264+ ##
265+ # The encoded string representation of the data, i.e. its raw form used
266+ # when encoding an event for transmission. This may be `nil` if there is
267+ # no data, or if the encoded form is not known.
268+ #
269+ # See also {#data}.
270+ #
271+ # @return [String,nil]
272+ #
273+ attr_reader :data_encoded
274+
275+ ##
276+ # Indicates whether the {#data} field returns decoded data.
277+ #
278+ # @return [true] if {#data} returns a decoded Ruby object
279+ # @return [false] if {#data} returns an encoded string or if the event
280+ # has no data.
281+ #
282+ def data_decoded?
283+ @data_decoded
284+ end
285+
286+ ##
287+ # Indicates whether the data field is present. If there is no data,
288+ # {#data} will return `nil`, and {#data_decoded?} will return false.
289+ #
290+ # Generally, if there is no data, the {#data_content_type} field should
291+ # also be absent, but this is not enforced.
292+ #
293+ # @return [boolean]
294+ #
295+ def data?
296+ !@data . nil? || @data_decoded
297+ end
298+
176299 ##
177300 # The optional `datacontenttype` field as a {CloudEvents::ContentType}
178301 # object, or `nil` if the field is absent.
0 commit comments