activerecord-materialized 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 +7 -0
- data/CHANGELOG.md +37 -0
- data/LICENSE +21 -0
- data/README.md +526 -0
- data/lib/activerecord/materialized/aggregate_analysis.rb +132 -0
- data/lib/activerecord/materialized/async_refresher.rb +105 -0
- data/lib/activerecord/materialized/cache_table_schema.rb +67 -0
- data/lib/activerecord/materialized/cold_read.rb +60 -0
- data/lib/activerecord/materialized/configuration.rb +80 -0
- data/lib/activerecord/materialized/delta_maintainer.rb +74 -0
- data/lib/activerecord/materialized/dependency_registry.rb +107 -0
- data/lib/activerecord/materialized/dependency_trackable.rb +48 -0
- data/lib/activerecord/materialized/incremental_maintainer.rb +58 -0
- data/lib/activerecord/materialized/maintenance_delta.rb +82 -0
- data/lib/activerecord/materialized/maintenance_delta_builder.rb +62 -0
- data/lib/activerecord/materialized/maintenance_store.rb +82 -0
- data/lib/activerecord/materialized/metadata/maintenance_payload.rb +33 -0
- data/lib/activerecord/materialized/metadata/schema.rb +84 -0
- data/lib/activerecord/materialized/metadata/timestamps.rb +31 -0
- data/lib/activerecord/materialized/metadata.rb +138 -0
- data/lib/activerecord/materialized/metadata_record.rb +28 -0
- data/lib/activerecord/materialized/migration_builder.rb +38 -0
- data/lib/activerecord/materialized/module_api.rb +82 -0
- data/lib/activerecord/materialized/partition_record.rb +27 -0
- data/lib/activerecord/materialized/partition_state.rb +127 -0
- data/lib/activerecord/materialized/query_expressions.rb +83 -0
- data/lib/activerecord/materialized/railtie.rb +16 -0
- data/lib/activerecord/materialized/refresh_callbacks.rb +62 -0
- data/lib/activerecord/materialized/refresh_job.rb +22 -0
- data/lib/activerecord/materialized/refresh_result.rb +40 -0
- data/lib/activerecord/materialized/refresh_scheduler.rb +54 -0
- data/lib/activerecord/materialized/refresher.rb +139 -0
- data/lib/activerecord/materialized/registry.rb +74 -0
- data/lib/activerecord/materialized/relation_cache_writer.rb +137 -0
- data/lib/activerecord/materialized/schema_verifier.rb +64 -0
- data/lib/activerecord/materialized/summary_delta.rb +76 -0
- data/lib/activerecord/materialized/summary_delta_builder.rb +58 -0
- data/lib/activerecord/materialized/table_model_registry.rb +43 -0
- data/lib/activerecord/materialized/tasks.rb +79 -0
- data/lib/activerecord/materialized/type_reexports.rb +14 -0
- data/lib/activerecord/materialized/version.rb +9 -0
- data/lib/activerecord/materialized/view.rb +79 -0
- data/lib/activerecord/materialized/view_class.rb +8 -0
- data/lib/activerecord/materialized/view_configuration_class_methods.rb +103 -0
- data/lib/activerecord/materialized/view_definition.rb +133 -0
- data/lib/activerecord/materialized/view_incremental_class_methods.rb +142 -0
- data/lib/activerecord/materialized/view_query_access_class_methods.rb +160 -0
- data/lib/activerecord/materialized/view_refresh_policy_class_methods.rb +109 -0
- data/lib/activerecord/materialized/write_change.rb +69 -0
- data/lib/activerecord/materialized.rb +55 -0
- data/lib/activerecord_materialized_types.rb +18 -0
- data/lib/generators/activerecord_materialized/install/templates/README +55 -0
- data/lib/generators/activerecord_materialized/install/templates/create_ar_materialized_view_metadata.rb.erb +30 -0
- data/lib/generators/activerecord_materialized/install_generator.rb +32 -0
- data/lib/generators/activerecord_materialized/migration_generator.rb +51 -0
- data/lib/generators/activerecord_materialized/templates/materialized_view.rb.erb +17 -0
- data/lib/generators/activerecord_materialized/templates/materialized_view_migration.rb.erb +11 -0
- data/lib/generators/activerecord_materialized/view_generator.rb +18 -0
- metadata +162 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
module Materialized
|
|
6
|
+
# A committed write to a dependency table, captured as full before/after
|
|
7
|
+
# attribute snapshots (string-keyed). Snapshots are complete — not just the
|
|
8
|
+
# changed columns — so maintenance can compute deltas even when the GROUP BY
|
|
9
|
+
# key or a summed column did not change.
|
|
10
|
+
class WriteChange
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
13
|
+
Operation = T.type_alias { T.any(Symbol, String) }
|
|
14
|
+
Attributes = T.type_alias { T::Hash[String, T.untyped] }
|
|
15
|
+
|
|
16
|
+
sig { returns(String) }
|
|
17
|
+
attr_reader :table_name
|
|
18
|
+
|
|
19
|
+
sig { returns(Operation) }
|
|
20
|
+
attr_reader :operation
|
|
21
|
+
|
|
22
|
+
sig { returns(Attributes) }
|
|
23
|
+
attr_reader :before
|
|
24
|
+
|
|
25
|
+
sig { returns(Attributes) }
|
|
26
|
+
attr_reader :after
|
|
27
|
+
|
|
28
|
+
sig { params(table_name: String, operation: Operation, before: Attributes, after: Attributes).void }
|
|
29
|
+
def initialize(table_name:, operation:, before:, after:)
|
|
30
|
+
@table_name = table_name
|
|
31
|
+
@operation = operation
|
|
32
|
+
@before = before
|
|
33
|
+
@after = after
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
sig { params(record: ::ActiveRecord::Base, operation: Operation).returns(WriteChange) }
|
|
37
|
+
def self.from_record(record, operation)
|
|
38
|
+
table_name = record.class.table_name
|
|
39
|
+
case operation.to_sym
|
|
40
|
+
when :create
|
|
41
|
+
new(table_name: table_name, operation: :create, before: {}, after: stringify_keys(record.attributes))
|
|
42
|
+
when :update
|
|
43
|
+
new(table_name: table_name, operation: :update, before: old_attributes(record),
|
|
44
|
+
after: stringify_keys(record.attributes))
|
|
45
|
+
when :destroy
|
|
46
|
+
# attributes_in_database is emptied once the row is gone; use in-memory attributes.
|
|
47
|
+
new(table_name: table_name, operation: :destroy, before: stringify_keys(record.attributes), after: {})
|
|
48
|
+
else
|
|
49
|
+
raise ArgumentError, "unsupported write operation: #{operation}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Full pre-update attributes: current values with changed columns reverted.
|
|
54
|
+
sig { params(record: ::ActiveRecord::Base).returns(Attributes) }
|
|
55
|
+
def self.old_attributes(record)
|
|
56
|
+
attributes = stringify_keys(record.attributes)
|
|
57
|
+
record.saved_changes.each_pair { |column, (old_value, _new_value)| attributes[column.to_s] = old_value }
|
|
58
|
+
attributes
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
sig { params(values: T::Hash[T.any(String, Symbol), T.untyped]).returns(Attributes) }
|
|
62
|
+
def self.stringify_keys(values)
|
|
63
|
+
values.each_with_object({}) { |(key, value), result| result[key.to_s] = value }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private_class_method :old_attributes, :stringify_keys
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "active_record"
|
|
6
|
+
require "active_support/concern"
|
|
7
|
+
require "active_support/core_ext/string/inflections"
|
|
8
|
+
require "active_support/core_ext/module/delegation"
|
|
9
|
+
require "active_support/core_ext/integer/time"
|
|
10
|
+
require "active_support/core_ext/object/blank"
|
|
11
|
+
require "securerandom"
|
|
12
|
+
|
|
13
|
+
require_relative "../activerecord_materialized_types"
|
|
14
|
+
require_relative "materialized/type_reexports"
|
|
15
|
+
require_relative "materialized/configuration"
|
|
16
|
+
require_relative "materialized/module_api"
|
|
17
|
+
require_relative "materialized/refresh_callbacks"
|
|
18
|
+
require_relative "materialized/write_change"
|
|
19
|
+
require_relative "materialized/maintenance_delta_builder"
|
|
20
|
+
require_relative "materialized/table_model_registry"
|
|
21
|
+
require_relative "materialized/dependency_trackable"
|
|
22
|
+
require_relative "materialized/dependency_registry"
|
|
23
|
+
require_relative "materialized/async_refresher"
|
|
24
|
+
require_relative "materialized/refresh_scheduler"
|
|
25
|
+
require_relative "materialized/registry"
|
|
26
|
+
require_relative "materialized/metadata_record"
|
|
27
|
+
require_relative "materialized/view_incremental_class_methods"
|
|
28
|
+
require_relative "materialized/view_refresh_policy_class_methods"
|
|
29
|
+
require_relative "materialized/view_configuration_class_methods"
|
|
30
|
+
require_relative "materialized/view_query_access_class_methods"
|
|
31
|
+
require_relative "materialized/view"
|
|
32
|
+
require_relative "materialized/view_class"
|
|
33
|
+
require_relative "materialized/refresh_result"
|
|
34
|
+
require_relative "materialized/refresher"
|
|
35
|
+
require_relative "materialized/maintenance_delta"
|
|
36
|
+
require_relative "materialized/view_definition"
|
|
37
|
+
require_relative "materialized/aggregate_analysis"
|
|
38
|
+
require_relative "materialized/summary_delta"
|
|
39
|
+
require_relative "materialized/summary_delta_builder"
|
|
40
|
+
require_relative "materialized/query_expressions"
|
|
41
|
+
require_relative "materialized/cache_table_schema"
|
|
42
|
+
require_relative "materialized/migration_builder"
|
|
43
|
+
require_relative "materialized/schema_verifier"
|
|
44
|
+
require_relative "materialized/cold_read"
|
|
45
|
+
require_relative "materialized/relation_cache_writer"
|
|
46
|
+
require_relative "materialized/maintenance_store"
|
|
47
|
+
require_relative "materialized/incremental_maintainer"
|
|
48
|
+
require_relative "materialized/delta_maintainer"
|
|
49
|
+
require_relative "materialized/metadata"
|
|
50
|
+
require_relative "materialized/partition_record"
|
|
51
|
+
require_relative "materialized/partition_state"
|
|
52
|
+
|
|
53
|
+
require_relative "materialized/refresh_job" if defined?(ActiveJob::Base)
|
|
54
|
+
|
|
55
|
+
require_relative "materialized/railtie" if defined?(Rails::Railtie)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module ActiveRecordMaterializedTypes
|
|
5
|
+
DebounceInterval = T.type_alias { T.any(Integer, Float, ::ActiveSupport::Duration) }
|
|
6
|
+
StalenessDuration = T.type_alias { T.any(Integer, ::ActiveSupport::Duration) }
|
|
7
|
+
SourceDefinition = T.type_alias do
|
|
8
|
+
T.any(
|
|
9
|
+
::ActiveRecord::Relation,
|
|
10
|
+
Proc,
|
|
11
|
+
T.nilable(T.proc.returns(::ActiveRecord::Relation))
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
RefreshMode = T.type_alias { Symbol }
|
|
15
|
+
RefreshCallbackName = T.type_alias { T.any(Symbol, Proc) }
|
|
16
|
+
Connection = T.type_alias { ::ActiveRecord::ConnectionAdapters::AbstractAdapter }
|
|
17
|
+
Timestamp = T.type_alias { T.any(::Time, ::ActiveSupport::TimeWithZone) }
|
|
18
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
After installing activerecord-materialized, create materialized views as standard
|
|
4
|
+
ActiveRecord models inheriting from ActiveRecord::Materialized::View.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
|
|
8
|
+
class SalesSummary < ActiveRecord::Materialized::View
|
|
9
|
+
self.table_name = "mv_sales_summary"
|
|
10
|
+
|
|
11
|
+
materialized_from do
|
|
12
|
+
LineItem.group(:category).select(
|
|
13
|
+
LineItem.arel_table[:category],
|
|
14
|
+
ActiveRecord::Materialized::QueryExpressions.sum_as(LineItem.arel_table[:amount], as: :total)
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
depends_on :line_items
|
|
19
|
+
max_staleness 12.hours
|
|
20
|
+
# cold_read :read_through # default; also :serve_stale or :raise
|
|
21
|
+
# warm_up { [where(region: "us")] } # materialize hot partitions at deploy
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Provision the (empty) cache table with a migration generated from the
|
|
25
|
+
relation, so the table exists at deploy time:
|
|
26
|
+
|
|
27
|
+
bin/rails generate activerecord_materialized:migration SalesSummary
|
|
28
|
+
bin/rails db:migrate
|
|
29
|
+
|
|
30
|
+
Verify the cache tables still match their relations (e.g. in CI / on deploy):
|
|
31
|
+
|
|
32
|
+
bin/rails materialized:verify
|
|
33
|
+
|
|
34
|
+
Build the view once (the only full-scan path — never happens implicitly; runs
|
|
35
|
+
in the database via INSERT ... SELECT):
|
|
36
|
+
|
|
37
|
+
SalesSummary.rebuild!(confirm: true)
|
|
38
|
+
|
|
39
|
+
Or warm just the hot partitions at deploy time (leaving the rest to read
|
|
40
|
+
through on demand):
|
|
41
|
+
|
|
42
|
+
bin/rails materialized:warm_up
|
|
43
|
+
|
|
44
|
+
Until a view is built, reads transparently fall through to the source query
|
|
45
|
+
(cold_read :read_through). After it is built, writes to depends_on tables
|
|
46
|
+
incrementally maintain it; refresh! applies that maintenance (it never rebuilds):
|
|
47
|
+
|
|
48
|
+
SalesSummary.refresh! # incremental maintenance only
|
|
49
|
+
SalesSummary.refresh_if_stale!
|
|
50
|
+
|
|
51
|
+
Or via Rake:
|
|
52
|
+
|
|
53
|
+
bin/rails materialized:rebuild # intentional full materialization
|
|
54
|
+
bin/rails materialized:refresh_all # incremental maintenance pass
|
|
55
|
+
bin/rails materialized:refresh_stale
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateArMaterializedViewMetadata < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ar_materialized_view_metadata do |t|
|
|
6
|
+
t.string :view_name, null: false
|
|
7
|
+
t.datetime :last_refreshed_at
|
|
8
|
+
t.boolean :refreshing, null: false, default: false
|
|
9
|
+
t.boolean :dirty, null: false, default: true
|
|
10
|
+
t.boolean :warm, null: false, default: false
|
|
11
|
+
t.integer :row_count
|
|
12
|
+
t.integer :refresh_duration_ms
|
|
13
|
+
t.text :last_error
|
|
14
|
+
t.text :maintenance_payload
|
|
15
|
+
|
|
16
|
+
t.timestamps
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
add_index :ar_materialized_view_metadata, :view_name, unique: true
|
|
20
|
+
|
|
21
|
+
create_table :ar_materialized_view_partitions do |t|
|
|
22
|
+
t.string :view_name, null: false
|
|
23
|
+
t.string :partition_key, null: false
|
|
24
|
+
|
|
25
|
+
t.datetime :created_at, null: false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
add_index :ar_materialized_view_partitions, %i[view_name partition_key], unique: true
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rails/generators"
|
|
5
|
+
require "rails/generators/migration"
|
|
6
|
+
|
|
7
|
+
module ActiverecordMaterialized
|
|
8
|
+
# `rails generate activerecord_materialized:install` — installs the metadata-table migration.
|
|
9
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
10
|
+
extend T::Sig
|
|
11
|
+
include Rails::Generators::Migration
|
|
12
|
+
|
|
13
|
+
source_root File.expand_path("install", __dir__)
|
|
14
|
+
|
|
15
|
+
sig { params(dirname: String).returns(String) }
|
|
16
|
+
def self.next_migration_number(dirname)
|
|
17
|
+
next_migration_number = T.unsafe(self).current_migration_number(dirname) + 1
|
|
18
|
+
::ActiveRecord::Migration.next_migration_number(next_migration_number)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
sig { void }
|
|
22
|
+
def copy_migration
|
|
23
|
+
migration_template "create_ar_materialized_view_metadata.rb.erb",
|
|
24
|
+
"db/migrate/create_ar_materialized_view_metadata.rb"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { void }
|
|
28
|
+
def show_readme
|
|
29
|
+
readme "README" if behavior == :invoke
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rails/generators"
|
|
5
|
+
require "rails/generators/migration"
|
|
6
|
+
|
|
7
|
+
module ActiverecordMaterialized
|
|
8
|
+
# Generates a migration that provisions a materialized view's (empty) cache
|
|
9
|
+
# table, with columns inferred from the view's source relation:
|
|
10
|
+
#
|
|
11
|
+
# bin/rails generate activerecord_materialized:migration SalesSummary
|
|
12
|
+
#
|
|
13
|
+
# Run after the view's `materialized_from` is defined, then `db:migrate`.
|
|
14
|
+
class MigrationGenerator < ::Rails::Generators::NamedBase
|
|
15
|
+
extend T::Sig
|
|
16
|
+
include ::Rails::Generators::Migration
|
|
17
|
+
|
|
18
|
+
source_root File.expand_path("templates", __dir__)
|
|
19
|
+
|
|
20
|
+
sig { params(dirname: String).returns(String) }
|
|
21
|
+
def self.next_migration_number(dirname)
|
|
22
|
+
::ActiveRecord::Migration.next_migration_number(T.unsafe(self).current_migration_number(dirname) + 1)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
sig { void }
|
|
26
|
+
def create_migration_file
|
|
27
|
+
migration_template "materialized_view_migration.rb.erb",
|
|
28
|
+
File.join("db", "migrate", "create_#{builder.table_name}.rb")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
sig { returns(::ActiveRecord::Materialized::MigrationBuilder) }
|
|
34
|
+
def builder
|
|
35
|
+
@builder ||= T.let(build_builder, T.nilable(::ActiveRecord::Materialized::MigrationBuilder))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Resolve via the registry (after eager-load) rather than constantize.
|
|
39
|
+
sig { returns(::ActiveRecord::Materialized::MigrationBuilder) }
|
|
40
|
+
def build_builder
|
|
41
|
+
T.unsafe(::Rails).application.eager_load!
|
|
42
|
+
view_class = ::ActiveRecord::Materialized::Registry.for_class_name(class_name)
|
|
43
|
+
if view_class.nil?
|
|
44
|
+
Kernel.raise ::Thor::Error,
|
|
45
|
+
"Unknown materialized view: #{class_name}. Define it before generating its migration."
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
::ActiveRecord::Materialized::MigrationBuilder.new(view_class)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= class_name %> < ActiveRecord::Materialized::View
|
|
4
|
+
self.table_name = "mv_<%= file_name %>"
|
|
5
|
+
|
|
6
|
+
# Replace ApplicationRecord.none with your ActiveRecord::Relation source, e.g.:
|
|
7
|
+
# Order.group(:status).select(
|
|
8
|
+
# Order.arel_table[:status],
|
|
9
|
+
# ActiveRecord::Materialized::QueryExpressions.count_as(Order.arel_table[:id], as: :order_count)
|
|
10
|
+
# )
|
|
11
|
+
materialized_from { ApplicationRecord.none }
|
|
12
|
+
|
|
13
|
+
depends_on :application_record
|
|
14
|
+
refresh_on_change :async
|
|
15
|
+
refresh_debounce 2.seconds
|
|
16
|
+
max_staleness 12.hours
|
|
17
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= builder.migration_class_name %> < ActiveRecord::Migration[<%= builder.migration_version %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :<%= builder.table_name %> do |t|
|
|
6
|
+
<% builder.column_definitions.each do |column| -%>
|
|
7
|
+
t.<%= column.type %> :<%= column.name %>
|
|
8
|
+
<% end -%>
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rails/generators"
|
|
5
|
+
|
|
6
|
+
module ActiverecordMaterialized
|
|
7
|
+
# `rails generate activerecord_materialized:view NAME` — scaffolds a materialized view class.
|
|
8
|
+
class ViewGenerator < ::Rails::Generators::NamedBase
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
sig { void }
|
|
14
|
+
def create_model
|
|
15
|
+
template "materialized_view.rb.erb", File.join("app/models", class_path, "#{file_name}.rb")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: activerecord-materialized
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michael Avrukin
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activerecord
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: activesupport
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '8.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: railties
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '8.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '8.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: sorbet-runtime
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.5'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.5'
|
|
68
|
+
description: |
|
|
69
|
+
Provides transparent materialized view semantics for Rails applications
|
|
70
|
+
on databases that do not support native materialized views. Precomputes
|
|
71
|
+
expensive analytical queries into cache tables with scheduled or on-demand refresh.
|
|
72
|
+
email:
|
|
73
|
+
- michael@avrukin.com
|
|
74
|
+
executables: []
|
|
75
|
+
extensions: []
|
|
76
|
+
extra_rdoc_files: []
|
|
77
|
+
files:
|
|
78
|
+
- CHANGELOG.md
|
|
79
|
+
- LICENSE
|
|
80
|
+
- README.md
|
|
81
|
+
- lib/activerecord/materialized.rb
|
|
82
|
+
- lib/activerecord/materialized/aggregate_analysis.rb
|
|
83
|
+
- lib/activerecord/materialized/async_refresher.rb
|
|
84
|
+
- lib/activerecord/materialized/cache_table_schema.rb
|
|
85
|
+
- lib/activerecord/materialized/cold_read.rb
|
|
86
|
+
- lib/activerecord/materialized/configuration.rb
|
|
87
|
+
- lib/activerecord/materialized/delta_maintainer.rb
|
|
88
|
+
- lib/activerecord/materialized/dependency_registry.rb
|
|
89
|
+
- lib/activerecord/materialized/dependency_trackable.rb
|
|
90
|
+
- lib/activerecord/materialized/incremental_maintainer.rb
|
|
91
|
+
- lib/activerecord/materialized/maintenance_delta.rb
|
|
92
|
+
- lib/activerecord/materialized/maintenance_delta_builder.rb
|
|
93
|
+
- lib/activerecord/materialized/maintenance_store.rb
|
|
94
|
+
- lib/activerecord/materialized/metadata.rb
|
|
95
|
+
- lib/activerecord/materialized/metadata/maintenance_payload.rb
|
|
96
|
+
- lib/activerecord/materialized/metadata/schema.rb
|
|
97
|
+
- lib/activerecord/materialized/metadata/timestamps.rb
|
|
98
|
+
- lib/activerecord/materialized/metadata_record.rb
|
|
99
|
+
- lib/activerecord/materialized/migration_builder.rb
|
|
100
|
+
- lib/activerecord/materialized/module_api.rb
|
|
101
|
+
- lib/activerecord/materialized/partition_record.rb
|
|
102
|
+
- lib/activerecord/materialized/partition_state.rb
|
|
103
|
+
- lib/activerecord/materialized/query_expressions.rb
|
|
104
|
+
- lib/activerecord/materialized/railtie.rb
|
|
105
|
+
- lib/activerecord/materialized/refresh_callbacks.rb
|
|
106
|
+
- lib/activerecord/materialized/refresh_job.rb
|
|
107
|
+
- lib/activerecord/materialized/refresh_result.rb
|
|
108
|
+
- lib/activerecord/materialized/refresh_scheduler.rb
|
|
109
|
+
- lib/activerecord/materialized/refresher.rb
|
|
110
|
+
- lib/activerecord/materialized/registry.rb
|
|
111
|
+
- lib/activerecord/materialized/relation_cache_writer.rb
|
|
112
|
+
- lib/activerecord/materialized/schema_verifier.rb
|
|
113
|
+
- lib/activerecord/materialized/summary_delta.rb
|
|
114
|
+
- lib/activerecord/materialized/summary_delta_builder.rb
|
|
115
|
+
- lib/activerecord/materialized/table_model_registry.rb
|
|
116
|
+
- lib/activerecord/materialized/tasks.rb
|
|
117
|
+
- lib/activerecord/materialized/type_reexports.rb
|
|
118
|
+
- lib/activerecord/materialized/version.rb
|
|
119
|
+
- lib/activerecord/materialized/view.rb
|
|
120
|
+
- lib/activerecord/materialized/view_class.rb
|
|
121
|
+
- lib/activerecord/materialized/view_configuration_class_methods.rb
|
|
122
|
+
- lib/activerecord/materialized/view_definition.rb
|
|
123
|
+
- lib/activerecord/materialized/view_incremental_class_methods.rb
|
|
124
|
+
- lib/activerecord/materialized/view_query_access_class_methods.rb
|
|
125
|
+
- lib/activerecord/materialized/view_refresh_policy_class_methods.rb
|
|
126
|
+
- lib/activerecord/materialized/write_change.rb
|
|
127
|
+
- lib/activerecord_materialized_types.rb
|
|
128
|
+
- lib/generators/activerecord_materialized/install/templates/README
|
|
129
|
+
- lib/generators/activerecord_materialized/install/templates/create_ar_materialized_view_metadata.rb.erb
|
|
130
|
+
- lib/generators/activerecord_materialized/install_generator.rb
|
|
131
|
+
- lib/generators/activerecord_materialized/migration_generator.rb
|
|
132
|
+
- lib/generators/activerecord_materialized/templates/materialized_view.rb.erb
|
|
133
|
+
- lib/generators/activerecord_materialized/templates/materialized_view_migration.rb.erb
|
|
134
|
+
- lib/generators/activerecord_materialized/view_generator.rb
|
|
135
|
+
homepage: https://github.com/mavrukin/activerecord-materialized
|
|
136
|
+
licenses:
|
|
137
|
+
- MIT
|
|
138
|
+
metadata:
|
|
139
|
+
allowed_push_host: https://rubygems.org
|
|
140
|
+
source_code_uri: https://github.com/mavrukin/activerecord-materialized
|
|
141
|
+
changelog_uri: https://github.com/mavrukin/activerecord-materialized/blob/main/CHANGELOG.md
|
|
142
|
+
bug_tracker_uri: https://github.com/mavrukin/activerecord-materialized/issues
|
|
143
|
+
documentation_uri: https://rubydoc.info/gems/activerecord-materialized
|
|
144
|
+
rubygems_mfa_required: 'true'
|
|
145
|
+
rdoc_options: []
|
|
146
|
+
require_paths:
|
|
147
|
+
- lib
|
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: 3.4.0
|
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
|
+
requirements:
|
|
155
|
+
- - ">="
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
version: '0'
|
|
158
|
+
requirements: []
|
|
159
|
+
rubygems_version: 4.0.15
|
|
160
|
+
specification_version: 4
|
|
161
|
+
summary: Application-level materialized views for ActiveRecord
|
|
162
|
+
test_files: []
|