Class: Happn::Event

Inherits:
Object
  • Object
show all
Defined in:
lib/happn/event.rb

Overview

A single CREPE event, as it was consumed from the exchange.

An event is built from the parsed payload of a message. That payload carries two entries: meta, describing the event itself, and data, describing what happened. A projector handler receives an instance of this class.

Key conversion

Every key of the payload, at every depth, is converted into an underscored Symbol when the event is built. A payload emitted in camel case is therefore read in snake case, and always through symbols:

# {"meta" => {…}, "data" => {"requestMetadata" => {"controllerName" => "countries"}}}
event.data[:request_metadata][:controller_name]  # => "countries"
event.data["request_metadata"]                   # => nil

Dashes become underscores, :: becomes /, and the capitals of an acronym are kept together: "HTTPResponseCode" is read as :http_response_code.

Changes

An entity change carries a changes entry, mapping an attribute to the pair of values it went through:

event.changes  # => { name: ["France", "Belgium"] }

Events of another shape carry no such entry at all, a request for instance. #changes then returns nil, and the five methods reading through it raise a NoMethodError: guard them with #changes when a handler may be reached by events of several shapes.

Examples:

Reading an event in a projector

on kind: "entity_change", name: "update country" do |event|
  Rails.logger.info("#{event.emitter} renamed a country at #{event.timestamp}")
  Rails.logger.info("from #{event.change_before(:name)} to #{event.change_after(:name)}")
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ Event

Builds an event from a parsed payload.

Parameters:

  • args (Hash)

    the parsed payload, with its "meta" and "data" entries, both keyed by String

Raises:

  • (KeyError)

    if the payload carries no "meta" or no "data" entry



55
56
57
58
# File 'lib/happn/event.rb', line 55

def initialize(args)
  @meta = deep_underscore_keys(args.fetch("meta"))
  @data = deep_underscore_keys(args.fetch("data"))
end

Instance Attribute Details

#dataHash (readonly)

The whole data entry of the payload, keys converted.

This is the hash the event holds, not a copy: #changes= and #add_change write into it, and so does anything the caller does to it.

Returns:

  • (Hash)

    the payload data, or whatever data held if it was no hash



48
49
50
# File 'lib/happn/event.rb', line 48

def data
  @data
end

Instance Method Details

#add_change(name, value) ⇒ Array

Records a change on an attribute.

The "before" value is always nil: the method describes a value that was set, not a transition. An empty String is normalized into nil, so that a blank emitted value and an absent one are recorded alike.

Examples:

event.add_change(:name, "Belgium")  # => [nil, "Belgium"]
event.add_change(:name, "")         # => [nil, nil]

Parameters:

  • name (Symbol, String)

    the attribute the change bears on

  • value (Object)

    the value the attribute was set to

Returns:

  • (Array)

    the pair of values recorded

Raises:

  • (NoMethodError)

    if the payload carries no changes entry



109
110
111
112
# File 'lib/happn/event.rb', line 109

def add_change(name, value)
  new_value            = value == "" ? nil : value
  changes[name.to_sym] = [nil, new_value]
end

#associationsHash?

The entities the event relates to.

Returns:

  • (Hash, nil)

    nil when the payload carries no associations



117
118
119
# File 'lib/happn/event.rb', line 117

def associations
  @data[:associations]
end

#change_after(attribute_name) ⇒ Object?

The value an attribute was changed to.

Parameters:

  • attribute_name (Symbol, String)

    the attribute to read

Returns:

  • (Object, nil)

    nil when the attribute did not change

Raises:

  • (NoMethodError)

    if the payload carries no changes entry



178
179
180
# File 'lib/happn/event.rb', line 178

def change_after(attribute_name)
  changes[attribute_name.to_sym]&.last
end

#change_before(attribute_name) ⇒ Object?

The value an attribute was changed from.

Parameters:

  • attribute_name (Symbol, String)

    the attribute to read

Returns:

  • (Object, nil)

    nil when the attribute did not change

Raises:

  • (NoMethodError)

    if the payload carries no changes entry



187
188
189
# File 'lib/happn/event.rb', line 187

def change_before(attribute_name)
  changes[attribute_name.to_sym]&.first
end

#changesHash{Symbol => Array}?

The attributes the event changed, each mapped to its before and after values.

Returns:

  • (Hash{Symbol => Array}, nil)

    nil when the payload carries no changes entry, which is the case of every event that is not an entity change



80
81
82
# File 'lib/happn/event.rb', line 80

def changes
  @data[:changes]
end

#changes=(new_changes) ⇒ Hash{Symbol => Array}

Replaces the whole set of changes.

Keys are taken as they are given: unlike the ones read from the payload, they go through no conversion.

Parameters:

  • new_changes (Hash{Symbol => Array})

    the changes to substitute

Returns:

  • (Hash{Symbol => Array})

    the changes that were set



91
92
93
# File 'lib/happn/event.rb', line 91

def changes=(new_changes)
  @data[:changes] = new_changes
end

#delete_change(attribute_name) ⇒ Array?

Drops the change recorded on an attribute.

Parameters:

  • attribute_name (Symbol, String)

    the attribute to drop

Returns:

  • (Array, nil)

    the pair of values that was dropped, nil when the attribute did not change

Raises:

  • (NoMethodError)

    if the payload carries no changes entry



206
207
208
# File 'lib/happn/event.rb', line 206

def delete_change(attribute_name)
  changes.delete(attribute_name.to_sym)
end

#emitterString?

The application the event comes from, matched by the emitter of a query.

Returns:

  • (String, nil)

    nil when the payload carries no emitter



169
170
171
# File 'lib/happn/event.rb', line 169

def emitter
  @meta[:emitter]
end

#has_change?(attribute_name) ⇒ Boolean

Whether an attribute changed.

Parameters:

  • attribute_name (Symbol, String)

    the attribute to look for

Returns:

  • (Boolean)

Raises:

  • (NoMethodError)

    if the payload carries no changes entry



196
197
198
# File 'lib/happn/event.rb', line 196

def has_change?(attribute_name)
  !changes[attribute_name.to_sym].nil?
end

#idString?

The identifier the emitter gave the event.

Returns:

  • (String, nil)

    nil when the payload carries no id



141
142
143
# File 'lib/happn/event.rb', line 141

def id
  @meta[:id]
end

#kindString?

The category of the event, matched by the kind of a query.

Returns:

  • (String, nil)

    nil when the payload carries no kind



162
163
164
# File 'lib/happn/event.rb', line 162

def kind
  @meta[:kind]
end

#nameString?

What the event says happened, matched by the name of a query.

Returns:

  • (String, nil)

    nil when the payload carries no name



148
149
150
# File 'lib/happn/event.rb', line 148

def name
  @meta[:name]
end

#request_metadataHash?

The metadata the emitter attached to the request behind the event.

Returns:

  • (Hash, nil)

    nil when the payload carries no request_metadata



70
71
72
# File 'lib/happn/event.rb', line 70

def 
  @data[:request_metadata]
end

#statusString?

The state of the event, matched by the status of a query.

Returns:

  • (String, nil)

    nil when the payload carries no status



155
156
157
# File 'lib/happn/event.rb', line 155

def status
  @meta[:status]
end

#timestampDateTime?

When the event was emitted.

The raw value is parsed on every call, and its offset is kept as it was emitted rather than being normalized.

Returns:

  • (DateTime, nil)

    nil when the payload carries no timestamp, or an empty one

Raises:

  • (Date::Error)

    if the timestamp cannot be parsed



129
130
131
132
133
134
135
136
# File 'lib/happn/event.rb', line 129

def timestamp
  raw_timestamp = @meta[:timestamp]
  if raw_timestamp.nil? || raw_timestamp.to_s.strip.empty?
    nil
  else
    DateTime.parse(raw_timestamp)
  end
end

#user_metadataHash?

The metadata the emitter attached to the user behind the event.

Returns:

  • (Hash, nil)

    nil when the payload carries no user_metadata



63
64
65
# File 'lib/happn/event.rb', line 63

def 
  @data[:user_metadata]
end