umami-read-models 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2c2f6153e25093f6b47ff480dcad0305d3729725541ce61fe814c5551f52ad3f
4
+ data.tar.gz: e81abfd30bf2a212a5f8ca47847720a3760358c58cf5da14c659519e4d068c35
5
+ SHA512:
6
+ metadata.gz: 4867b41518f09623a58584e9130aab9314d89366e9d97d0a5b8826d953156d20004ba3df88087e459bd0757e263331bb71bb14a149d6eb49caf6e8159ef63d15
7
+ data.tar.gz: 30676022f6642b95b55658364d072a1bb282e0df36c5c01e4f158cefac29af0465d7697b813b5b40814cba45c444bd99edfee818e1c3555a456d46b5eaa91b01
data/.rubocop.yml ADDED
@@ -0,0 +1,16 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ EnforcedStyle: double_quotes
10
+
11
+ Style/Documentation:
12
+ Exclude:
13
+ - 'test/**/*'
14
+
15
+ Style/ClassAndModuleChildren:
16
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2024-01-26
11
+
12
+ ### Added
13
+ - Initial release of umami-read-models gem
14
+ - Read-only ActiveRecord models for all Umami database tables
15
+ - Support for PostgreSQL connections
16
+ - Rails multi-database support with external configuration
17
+ - Comprehensive query scopes for analytics queries
18
+ - Full association mappings between models
19
+ - Configurable table prefix support
20
+ - Thread-safe connection management
21
+ - Documentation and usage examples
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Andreas Zeitler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # Umami::Models
2
+
3
+ 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.
4
+
5
+ ## Features
6
+
7
+ - Read-only ActiveRecord models for all Umami database tables
8
+ - Support for PostgreSQL connections
9
+ - Built-in query scopes for common analytics queries
10
+ - Association mappings between models
11
+ - Configurable table prefix support
12
+ - Thread-safe connection management
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'umami-read-models'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle install
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install umami-read-models
29
+
30
+ ## Configuration
31
+
32
+ ### Setup with Rails Multi-Database Support
33
+
34
+ This gem is designed to work with Rails 6+ multiple database support. Configure your database in `config/database.yml`:
35
+
36
+ ```yaml
37
+ # config/database.yml
38
+ production:
39
+ primary:
40
+ # Your main Rails database configuration
41
+ umami:
42
+ adapter: postgresql
43
+ host: <%= ENV['UMAMI_DB_HOST'] %>
44
+ port: <%= ENV['UMAMI_DB_PORT'] || 5432 %>
45
+ database: <%= ENV['UMAMI_DB_NAME'] %>
46
+ username: <%= ENV['UMAMI_DB_USER'] %>
47
+ password: <%= ENV['UMAMI_DB_PASSWORD'] %>
48
+ ```
49
+
50
+ Then configure the gem in an initializer (e.g., `config/initializers/umami_read_models.rb`):
51
+
52
+ ```ruby
53
+ Umami::Models.configure do |config|
54
+ # Specify which database configuration to use
55
+ config.database = :umami
56
+
57
+ # Optional: Set a table prefix if your Umami tables use one
58
+ config.table_prefix = "umami_"
59
+ end
60
+ ```
61
+
62
+ ### Advanced Multi-Database Configuration
63
+
64
+ For read replicas:
65
+
66
+ ```ruby
67
+ Umami::Models.configure do |config|
68
+ config.database = { writing: :umami, reading: :umami_replica }
69
+ end
70
+ ```
71
+
72
+ This uses Rails' built-in database roles. Even though the models are read-only, Rails still requires
73
+ a `:writing` connection to be defined. Both connections can point to the same database, or you can
74
+ use a read replica for the `:reading` connection for better performance.
75
+
76
+ ## Usage
77
+
78
+ ### Available Models
79
+
80
+ - `Umami::Models::User` - Umami users
81
+ - `Umami::Models::Website` - Tracked websites
82
+ - `Umami::Models::Session` - Visitor sessions
83
+ - `Umami::Models::WebsiteEvent` - Page views and custom events
84
+ - `Umami::Models::EventData` - Custom event data
85
+ - `Umami::Models::SessionData` - Session metadata
86
+ - `Umami::Models::Team` - Teams
87
+ - `Umami::Models::TeamUser` - Team memberships
88
+ - `Umami::Models::Report` - Saved reports
89
+
90
+ ### Basic Queries
91
+
92
+ ```ruby
93
+ # Get all websites
94
+ websites = Umami::Models::Website.all
95
+
96
+ # Get active websites for a user
97
+ user_websites = Umami::Models::Website
98
+ .active
99
+ .by_user(user_id)
100
+
101
+ # Get recent sessions for a website
102
+ recent_sessions = Umami::Models::Session
103
+ .by_website(website_id)
104
+ .recent
105
+ .limit(100)
106
+
107
+ # Get page views for the last 7 days
108
+ page_views = Umami::Models::WebsiteEvent
109
+ .by_website(website_id)
110
+ .page_views
111
+ .by_date_range(7.days.ago, Time.current)
112
+ ```
113
+
114
+ ### Working with Sessions
115
+
116
+ ```ruby
117
+ # Get sessions by browser
118
+ chrome_sessions = Umami::Models::Session
119
+ .by_website(website_id)
120
+ .by_browser('Chrome')
121
+ .by_date_range(start_date, end_date)
122
+
123
+ # Get sessions by country
124
+ us_sessions = Umami::Models::Session
125
+ .by_website(website_id)
126
+ .by_country('US')
127
+
128
+ # Get session with events
129
+ session = Umami::Models::Session.find(session_id)
130
+ events = session.website_events.page_views
131
+ ```
132
+
133
+ ### Working with Events
134
+
135
+ ```ruby
136
+ # Get custom events
137
+ custom_events = Umami::Models::WebsiteEvent
138
+ .by_website(website_id)
139
+ .custom_events
140
+ .by_event_name('button_click')
141
+
142
+ # Get events with UTM parameters
143
+ campaign_events = Umami::Models::WebsiteEvent
144
+ .by_website(website_id)
145
+ .with_utm_campaign('summer_sale')
146
+
147
+ # Get event data
148
+ event = Umami::Models::WebsiteEvent.find(event_id)
149
+ event_data = event.event_data
150
+ ```
151
+
152
+ ### Analytics Queries
153
+
154
+ ```ruby
155
+ # Get unique visitors (sessions) by day
156
+ daily_visitors = Umami::Models::Session
157
+ .by_website(website_id)
158
+ .group("DATE(created_at)")
159
+ .count
160
+
161
+ # Get top pages
162
+ top_pages = Umami::Models::WebsiteEvent
163
+ .by_website(website_id)
164
+ .page_views
165
+ .group(:url_path)
166
+ .order('count_all DESC')
167
+ .limit(10)
168
+ .count
169
+
170
+ # Get referrer domains
171
+ referrers = Umami::Models::WebsiteEvent
172
+ .by_website(website_id)
173
+ .where.not(referrer_domain: nil)
174
+ .group(:referrer_domain)
175
+ .order('count_all DESC')
176
+ .count
177
+
178
+ # Get browser statistics
179
+ browser_stats = Umami::Models::Session
180
+ .by_website(website_id)
181
+ .group(:browser)
182
+ .count
183
+ ```
184
+
185
+ ### Working with Reports
186
+
187
+ ```ruby
188
+ # Get user reports
189
+ user_reports = Umami::Models::Report
190
+ .by_user(user_id)
191
+ .recent
192
+
193
+ # Get report with parsed parameters
194
+ report = Umami::Models::Report.find(report_id)
195
+ params = report.parsed_parameters
196
+ ```
197
+
198
+ ## Read-Only Protection
199
+
200
+ All models are read-only by default. Any attempt to create, update, or delete records will fail:
201
+
202
+ ```ruby
203
+ # This will raise an error
204
+ website = Umami::Models::Website.new(name: "Test")
205
+ website.save # => raises ActiveRecord::ReadOnlyRecord
206
+
207
+ # This will also raise an error
208
+ Umami::Models::Website.find(id).update(name: "New Name")
209
+ ```
210
+
211
+ ## Advanced Usage
212
+
213
+ ### Custom Queries
214
+
215
+ You can use all ActiveRecord query methods:
216
+
217
+ ```ruby
218
+ # Complex query example
219
+ Umami::Models::WebsiteEvent
220
+ .joins(:session)
221
+ .where(website_id: website_id)
222
+ .where(sessions: { country: 'US' })
223
+ .where(created_at: 30.days.ago..Time.current)
224
+ .group(:url_path)
225
+ .having('COUNT(*) > ?', 100)
226
+ .pluck(:url_path, 'COUNT(*)')
227
+ ```
228
+
229
+ ### Raw SQL
230
+
231
+ For complex analytics queries, you can use raw SQL:
232
+
233
+ ```ruby
234
+ results = Umami::Models::Base.connection.execute(<<-SQL)
235
+ SELECT
236
+ DATE(created_at) as date,
237
+ COUNT(DISTINCT session_id) as visitors,
238
+ COUNT(*) as page_views
239
+ FROM website_event
240
+ WHERE website_id = '#{website_id}'
241
+ AND created_at >= '#{30.days.ago}'
242
+ GROUP BY DATE(created_at)
243
+ ORDER BY date DESC
244
+ SQL
245
+ ```
246
+
247
+ ## Development
248
+
249
+ 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.
250
+
251
+ ### Releasing a New Version
252
+
253
+ 1. Update the version number in `lib/umami/models/version.rb`
254
+ 2. Update the CHANGELOG.md with the new version and changes
255
+ 3. Commit the changes: `git commit -am "Release version X.Y.Z"`
256
+ 4. Create a tag: `git tag vX.Y.Z`
257
+ 5. Push the changes and tag: `git push origin main --tags`
258
+
259
+ The GitHub Action will automatically:
260
+ - Run the test suite
261
+ - Build the gem
262
+ - Publish to RubyGems.org
263
+ - Create a GitHub release
264
+
265
+ **Note**: You need to set up the `RUBYGEMS_API_KEY` secret in your GitHub repository settings for automatic publishing to work.
266
+
267
+ ### Manual Release
268
+
269
+ If you need to release manually:
270
+
271
+ ```bash
272
+ gem build umami-read-models.gemspec
273
+ gem push umami-read-models-*.gem
274
+ ```
275
+
276
+ ## Contributing
277
+
278
+ Bug reports and pull requests are welcome on GitHub at https://github.com/azeitler/umami-read-models.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents custom data associated with an event
6
+ class EventData < Base
7
+ self.table_name = "event_data"
8
+ self.primary_key = "event_data_id"
9
+ belongs_to :website, foreign_key: "website_id"
10
+ belongs_to :website_event, foreign_key: "website_event_id"
11
+ scope :by_website, ->(website_id) { where(website_id: website_id) }
12
+ scope :by_key, ->(key) { where(data_key: key) }
13
+ scope :string_type, -> { where(data_type: 1) }
14
+ scope :number_type, -> { where(data_type: 2) }
15
+ scope :date_type, -> { where(data_type: 3) }
16
+ scope :by_date_range, ->(start_date, end_date) { where(created_at: start_date..end_date) }
17
+ def value
18
+ case data_type
19
+ when 1 then string_value
20
+ when 2 then number_value
21
+ when 3 then date_value
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents a saved report configuration
6
+ class Report < Base
7
+ self.table_name = "report"
8
+ self.primary_key = "report_id"
9
+ belongs_to :user, foreign_key: "user_id"
10
+ belongs_to :website, foreign_key: "website_id"
11
+ scope :by_type, ->(type) { where(type: type) }
12
+ scope :by_name, ->(name) { where(name: name) }
13
+ scope :by_user, ->(user_id) { where(user_id: user_id) }
14
+ scope :by_website, ->(website_id) { where(website_id: website_id) }
15
+ scope :recent, -> { order(created_at: :desc) }
16
+ def parsed_parameters
17
+ JSON.parse(parameters)
18
+ rescue JSON::ParserError
19
+ {}
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents a visitor session
6
+ class Session < Base
7
+ self.table_name = "session"
8
+ self.primary_key = "session_id"
9
+
10
+ has_many :website_events, class_name: "WebsiteEvent", foreign_key: "session_id"
11
+ has_many :session_data, class_name: "SessionData", foreign_key: "session_id"
12
+
13
+ scope :recent, -> { order(created_at: :desc) }
14
+ scope :by_website, ->(website_id) { where(website_id: website_id) }
15
+ scope :by_browser, ->(browser) { where(browser: browser) }
16
+ scope :by_os, ->(os) { where(os: os) }
17
+ scope :by_device, ->(device) { where(device: device) }
18
+ scope :by_country, ->(country) { where(country: country) }
19
+ scope :by_date_range, ->(start_date, end_date) { where(created_at: start_date..end_date) }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents custom data associated with a session
6
+ class SessionData < Base
7
+ self.table_name = "session_data"
8
+ self.primary_key = "session_data_id"
9
+ belongs_to :website, foreign_key: "website_id"
10
+ belongs_to :session, foreign_key: "session_id"
11
+ scope :by_website, ->(website_id) { where(website_id: website_id) }
12
+ scope :by_session, ->(session_id) { where(session_id: session_id) }
13
+ scope :by_key, ->(key) { where(data_key: key) }
14
+ scope :by_distinct_id, ->(distinct_id) { where(distinct_id: distinct_id) }
15
+ scope :string_type, -> { where(data_type: 1) }
16
+ scope :number_type, -> { where(data_type: 2) }
17
+ scope :date_type, -> { where(data_type: 3) }
18
+ scope :by_date_range, ->(start_date, end_date) { where(created_at: start_date..end_date) }
19
+ def value
20
+ case data_type
21
+ when 1 then string_value
22
+ when 2 then number_value
23
+ when 3 then date_value
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents a team for multi-tenancy
6
+ class Team < Base
7
+ self.table_name = "team"
8
+ self.primary_key = "team_id"
9
+ has_many :websites, foreign_key: "team_id"
10
+ has_many :team_users, class_name: "TeamUser", foreign_key: "team_id"
11
+ has_many :users, through: :team_users
12
+ scope :active, -> { where(deleted_at: nil) }
13
+ scope :by_access_code, ->(code) { where(access_code: code) }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents team membership
6
+ class TeamUser < Base
7
+ self.table_name = "team_user"
8
+ self.primary_key = "team_user_id"
9
+ belongs_to :team, foreign_key: "team_id"
10
+ belongs_to :user, foreign_key: "user_id"
11
+ scope :by_role, ->(role) { where(role: role) }
12
+ scope :admins, -> { where(role: "admin") }
13
+ scope :members, -> { where(role: "member") }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents a user in the Umami analytics system
6
+ class User < Base
7
+ self.table_name = "user"
8
+ self.primary_key = "user_id"
9
+
10
+ has_many :owned_websites, class_name: "Website", foreign_key: "user_id"
11
+ has_many :created_websites, class_name: "Website", foreign_key: "created_by"
12
+ has_many :team_users, class_name: "TeamUser", foreign_key: "user_id"
13
+ has_many :teams, through: :team_users
14
+ has_many :reports, foreign_key: "user_id"
15
+
16
+ scope :active, -> { where(deleted_at: nil) }
17
+ scope :with_role, ->(role) { where(role: role) }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents a website being tracked
6
+ class Website < Base
7
+ self.table_name = "website"
8
+ self.primary_key = "website_id"
9
+ belongs_to :user, foreign_key: "user_id", optional: true
10
+ belongs_to :created_by_user, class_name: "User", foreign_key: "created_by", optional: true
11
+ belongs_to :team, foreign_key: "team_id", optional: true
12
+ has_many :event_data, class_name: "EventData", foreign_key: "website_id"
13
+ has_many :reports, foreign_key: "website_id"
14
+ has_many :session_data, class_name: "SessionData", foreign_key: "website_id"
15
+ scope :active, -> { where(deleted_at: nil) }
16
+ scope :by_user, ->(user_id) { where(user_id: user_id) }
17
+ scope :by_team, ->(team_id) { where(team_id: team_id) }
18
+ scope :public_shares, -> { where.not(share_id: nil) }
19
+ def sessions
20
+ Session.by_website(website_id)
21
+ end
22
+
23
+ def website_events
24
+ WebsiteEvent.by_website(website_id)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umami
4
+ module Models
5
+ # Represents a page view or custom event
6
+ class WebsiteEvent < Base
7
+ self.table_name = "website_event"
8
+ self.primary_key = "event_id"
9
+ belongs_to :session, foreign_key: "session_id"
10
+ has_many :event_data, class_name: "EventData", foreign_key: "website_event_id"
11
+ scope :recent, -> { order(created_at: :desc) }
12
+ scope :by_website, ->(website_id) { where(website_id: website_id) }
13
+ scope :by_session, ->(session_id) { where(session_id: session_id) }
14
+ scope :by_visit, ->(visit_id) { where(visit_id: visit_id) }
15
+ scope :by_date_range, ->(start_date, end_date) { where(created_at: start_date..end_date) }
16
+ scope :page_views, -> { where(event_type: 1) }
17
+ scope :custom_events, -> { where(event_type: 2) }
18
+ scope :by_event_name, ->(name) { where(event_name: name) }
19
+ scope :by_url_path, ->(path) { where(url_path: path) }
20
+ scope :by_hostname, ->(hostname) { where(hostname: hostname) }
21
+ scope :with_utm_source, ->(source) { where(utm_source: source) }
22
+ scope :with_utm_campaign, ->(campaign) { where(utm_campaign: campaign) }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "models/version"
4
+ require "active_record"
5
+
6
+ module Umami
7
+ # Provides read-only ActiveRecord models for accessing Umami Analytics data
8
+ module Models
9
+ class Error < StandardError; end
10
+
11
+ class << self
12
+ attr_accessor :table_prefix, :database
13
+
14
+ def configure
15
+ yield self
16
+ end
17
+ end
18
+
19
+ self.table_prefix = ""
20
+ self.database = nil
21
+
22
+ # Base class for all Umami models with read-only enforcement
23
+ class Base < ActiveRecord::Base
24
+ self.abstract_class = true
25
+
26
+ def self.inherited(subclass)
27
+ super
28
+ # Apply the database configuration when a model inherits from Base
29
+ return unless Umami::Models.database
30
+
31
+ subclass.connects_to database: Umami::Models.database
32
+ end
33
+
34
+ def readonly?
35
+ true
36
+ end
37
+
38
+ def self.table_name
39
+ "#{Umami::Models.table_prefix}#{super}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ require_relative "models/user"
46
+ require_relative "models/session"
47
+ require_relative "models/website"
48
+ require_relative "models/website_event"
49
+ require_relative "models/event_data"
50
+ require_relative "models/session_data"
51
+ require_relative "models/team"
52
+ require_relative "models/team_user"
53
+ require_relative "models/report"
@@ -0,0 +1,6 @@
1
+ module Umami
2
+ module Models
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: umami-read-models
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andreas Zeitler
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-07-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: Provides read-only ActiveRecord models for accessing Umami Analytics
42
+ data directly from Rails applications
43
+ email:
44
+ - me@azeitler.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".rubocop.yml"
50
+ - CHANGELOG.md
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - lib/umami/models.rb
55
+ - lib/umami/models/event_data.rb
56
+ - lib/umami/models/report.rb
57
+ - lib/umami/models/session.rb
58
+ - lib/umami/models/session_data.rb
59
+ - lib/umami/models/team.rb
60
+ - lib/umami/models/team_user.rb
61
+ - lib/umami/models/user.rb
62
+ - lib/umami/models/version.rb
63
+ - lib/umami/models/website.rb
64
+ - lib/umami/models/website_event.rb
65
+ - sig/umami/models.rbs
66
+ homepage: https://github.com/azeitler/umami-read-models
67
+ licenses: []
68
+ metadata:
69
+ homepage_uri: https://github.com/azeitler/umami-read-models
70
+ source_code_uri: https://github.com/azeitler/umami-read-models
71
+ changelog_uri: https://github.com/azeitler/umami-read-models/blob/main/CHANGELOG.md
72
+ rubygems_mfa_required: 'true'
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 3.0.0
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.4.19
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Read-only ActiveRecord models for Umami Analytics database
92
+ test_files: []