Attach meta information to events

Table of contents

  1. Generate the initializer
  2. Define attributes
  3. Add validations
  4. Set values per request

In auditable systems, what happened is only part of the story — you also need to know who did it and in what context. Event metainformation lets you attach that context to every event that you record during a request. That enriches your audit trail with the provenance required for compliance, debugging, and forensic analysis.

Generate the initializer

Funes ships a generator that creates a pre-configured initializer with the metainformation options already commented in:

$ bin/rails generate funes:initializer

This command creates config/initializers/funes.rb with the event_metainformation_attributes and event_metainformation_validations blocks ready to uncomment and fill in. Funes exposes the attributes you declare there on Funes::EventMetainformation, an ActiveSupport::CurrentAttributes subclass that gives you thread-isolated, per-request storage. Rails resets it between requests, and it is safe under any multithreaded server such as Puma.

Define attributes

You configure metainformation in your application’s initializer. Start by declaring the attributes you need:

# config/initializers/funes.rb
Funes.configure do |config|
  config.event_metainformation_attributes = [:user_id, :action, :git_version]
end

These become thread-safe attributes on Funes::EventMetainformation; Funes attaches them automatically to every event that it persists during the same request.

Attributes don’t take a type — they keep whatever Ruby value you assign. Integers stay integers, strings stay strings, nil is allowed. Funes serializes the whole hash into the event’s meta_info JSON column when the event lands, so any JSON-compatible value survives ipsis literis.

Add validations

Use the event_metainformation_validations block to enforce that the required context is always present when you record an event:

# config/initializers/funes.rb
Funes.configure do |config|
  config.event_metainformation_attributes = [:user_id, :action, :git_version]

  config.event_metainformation_validations do
    validates :user_id, presence: true
    validates :action, presence: true, format: { with: /\A\w+#\w+\z/ }
    validates :git_version, presence: true, format: { with: /\A[a-f0-9]{7,40}\z/ }
  end
end

The block DSL supports all standard ActiveModel validators. If metainformation fails validation at append time, Funes raises Funes::InvalidEventMetainformation and rolls back the database transaction.

Set values per request

Funes doesn’t care how you set the values — only that they’re in place before an append runs. Anything that assigns to Funes::EventMetainformation during the request works: a before_action, middleware, a service object called from the controller, even a manual assignment inside the action itself.

What follows is one suggestion that fits most Rails apps: populate the attributes at the start of each request from a before_action, with a concern that keeps the wiring out of ApplicationController. Feel free to adapt it to whatever shape your app already uses.

# app/controllers/concerns/events_metainformation_attachment.rb
module EventsMetainformationAttachment
  extend ActiveSupport::Concern

  included do
    before_action :set_event_metainformation
  end

  private
      def set_event_metainformation
        Funes::EventMetainformation.user_id = current_user&.id
        Funes::EventMetainformation.action = "#{controller_name}##{action_name}"
        Funes::EventMetainformation.git_version = ENV["GIT_VERSION"]
      end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include EventsMetainformationAttachment
end

Every event that you append during that request will carry the values from this before_action.


This site uses Just the Docs, a documentation theme for Jekyll.