Umami::Models

A Ruby gem that provides read-only ActiveRecord models for accessing Umami Analytics data directly from Rails applications. This gem allows you to query Umami's database directly for analytics data, reports, and user information.

Features

  • Read-only ActiveRecord models for all Umami database tables
  • Support for PostgreSQL connections
  • Built-in query scopes for common analytics queries
  • Association mappings between models

Installation

Add this line to your application's Gemfile:

gem 'umami-read-models'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install umami-read-models

Configuration

Setup with Rails Multi-Database Support

This gem is designed to work with Rails 6+ multiple database support. Configure your database in config/database.yml:

# config/database.yml
production:
  primary:
    # Your main Rails database configuration
  umami:
    adapter: postgresql
    host: <%= ENV['UMAMI_DB_HOST'] %>
    port: <%= ENV['UMAMI_DB_PORT'] || 5432 %>
    database: <%= ENV['UMAMI_DB_NAME'] %>
    username: <%= ENV['UMAMI_DB_USER'] %>
    password: <%= ENV['UMAMI_DB_PASSWORD'] %>

Configure the gem in an initializer (e.g., config/initializers/umami_read_models.rb):

# For a simple setup with one database
Umami::Models.configure do |config|
  config.database = :umami
end

# Or for read replicas
Umami::Models.configure do |config|
  config.database = { writing: :umami, reading: :umami_replica }
end

Important: The configuration must be set during application initialization, not in an after_initialize block.

Note about read replicas: Even though the models are read-only, Rails still requires a :writing connection to be defined. Both connections can point to the same database if you don't have a read replica.

Usage

Available Models

  • Umami::Models::User - Umami users
  • Umami::Models::Website - Tracked websites
  • Umami::Models::Session - Visitor sessions
  • Umami::Models::WebsiteEvent - Page views and custom events
  • Umami::Models::EventData - Custom event data
  • Umami::Models::SessionData - Session metadata
  • Umami::Models::Team - Teams
  • Umami::Models::TeamUser - Team memberships
  • Umami::Models::Report - Saved reports

Basic Queries

# Get all websites
websites = Umami::Models::Website.all

# Get active websites for a user
user_websites = Umami::Models::Website
  .active
  .by_user(user_id)

# Get recent sessions for a website
recent_sessions = Umami::Models::Session
  .by_website(website_id)
  .recent
  .limit(100)

# Get page views for the last 7 days
page_views = Umami::Models::WebsiteEvent
  .by_website(website_id)
  .page_views
  .by_date_range(7.days.ago, Time.current)

Working with Sessions

# Get sessions by browser
chrome_sessions = Umami::Models::Session
  .by_website(website_id)
  .by_browser('Chrome')
  .by_date_range(start_date, end_date)

# Get sessions by country
us_sessions = Umami::Models::Session
  .by_website(website_id)
  .by_country('US')

# Get session with events
session = Umami::Models::Session.find(session_id)
events = session.website_events.page_views

Working with Events

# Get custom events
custom_events = Umami::Models::WebsiteEvent
  .by_website(website_id)
  .custom_events
  .by_event_name('button_click')

# Get events with UTM parameters
campaign_events = Umami::Models::WebsiteEvent
  .by_website(website_id)
  .with_utm_campaign('summer_sale')

# Get event data
event = Umami::Models::WebsiteEvent.find(event_id)
event_data = event.event_data

Analytics Queries

# Get unique visitors (sessions) by day
daily_visitors = Umami::Models::Session
  .by_website(website_id)
  .group("DATE(created_at)")
  .count

# Get top pages
top_pages = Umami::Models::WebsiteEvent
  .by_website(website_id)
  .page_views
  .group(:url_path)
  .order('count_all DESC')
  .limit(10)
  .count

# Get referrer domains
referrers = Umami::Models::WebsiteEvent
  .by_website(website_id)
  .where.not(referrer_domain: nil)
  .group(:referrer_domain)
  .order('count_all DESC')
  .count

# Get browser statistics
browser_stats = Umami::Models::Session
  .by_website(website_id)
  .group(:browser)
  .count

Working with Reports

# Get user reports
user_reports = Umami::Models::Report
  .by_user(user_id)
  .recent

# Get report with parsed parameters
report = Umami::Models::Report.find(report_id)
params = report.parsed_parameters

Read-Only Protection

All models are read-only. Any attempt to create, update, or delete records will raise an error:

# Creating records will raise an error
website = Umami::Models::Website.new(name: "Test")
website.save # => raises ActiveRecord::ReadOnlyRecord

# Updating records will raise an error
website = Umami::Models::Website.find(id)
website.update(name: "New Name") # => raises ActiveRecord::ReadOnlyRecord

# Deleting records will raise an error
website.destroy # => raises ActiveRecord::ReadOnlyRecord

Advanced Usage

Custom Queries

You can use all ActiveRecord query methods:

# Complex query example
Umami::Models::WebsiteEvent
  .joins(:session)
  .where(website_id: website_id)
  .where(sessions: { country: 'US' })
  .where(created_at: 30.days.ago..Time.current)
  .group(:url_path)
  .having('COUNT(*) > ?', 100)
  .pluck(:url_path, 'COUNT(*)')

Raw SQL

For complex analytics queries, you can use raw SQL with proper parameterization:

sql = <<-SQL
  SELECT 
    DATE(created_at) as date,
    COUNT(DISTINCT session_id) as visitors,
    COUNT(*) as page_views
  FROM website_event
  WHERE website_id = ?
    AND created_at >= ?
  GROUP BY DATE(created_at)
  ORDER BY date DESC
SQL

results = Umami::Models::Base.connection.exec_query(
  sql, 
  'SQL', 
  [[nil, website_id], [nil, 30.days.ago]]
)

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

Releasing a New Version

  1. Update the version number in lib/umami/models/version.rb
  2. Update the CHANGELOG.md with the new version and changes
  3. Commit the changes: git commit -am "Release version X.Y.Z"
  4. Create a tag: git tag vX.Y.Z
  5. Push the changes and tag: git push origin main --tags

The GitHub Action will automatically:

  • Run the test suite
  • Build the gem
  • Publish to RubyGems.org
  • Create a GitHub release

Note: You need to set up the RUBYGEMS_API_KEY secret in your GitHub repository settings for automatic publishing to work.

Manual Release

If you need to release manually:

gem build umami-read-models.gemspec
gem push umami-read-models-*.gem

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/azeitler/umami-read-models.