mat_views 0.1.2
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/LICENSE +21 -0
- data/README.md +121 -0
- data/Rakefile +15 -0
- data/app/assets/stylesheets/mat_views/application.css +15 -0
- data/app/jobs/mat_views/application_job.rb +39 -0
- data/app/jobs/mat_views/create_view_job.rb +188 -0
- data/app/jobs/mat_views/delete_view_job.rb +196 -0
- data/app/jobs/mat_views/refresh_view_job.rb +215 -0
- data/app/models/mat_views/application_record.rb +34 -0
- data/app/models/mat_views/mat_view_definition.rb +98 -0
- data/app/models/mat_views/mat_view_run.rb +89 -0
- data/config/routes.rb +12 -0
- data/lib/generators/mat_views/install/install_generator.rb +86 -0
- data/lib/generators/mat_views/install/templates/create_mat_view_definitions.rb +29 -0
- data/lib/generators/mat_views/install/templates/create_mat_view_runs.rb +32 -0
- data/lib/generators/mat_views/install/templates/mat_views_initializer.rb +23 -0
- data/lib/mat_views/configuration.rb +49 -0
- data/lib/mat_views/engine.rb +34 -0
- data/lib/mat_views/jobs/adapter.rb +78 -0
- data/lib/mat_views/service_response.rb +60 -0
- data/lib/mat_views/services/base_service.rb +308 -0
- data/lib/mat_views/services/concurrent_refresh.rb +177 -0
- data/lib/mat_views/services/create_view.rb +156 -0
- data/lib/mat_views/services/delete_view.rb +160 -0
- data/lib/mat_views/services/regular_refresh.rb +146 -0
- data/lib/mat_views/services/swap_refresh.rb +221 -0
- data/lib/mat_views/version.rb +21 -0
- data/lib/mat_views.rb +57 -0
- data/lib/tasks/helpers.rb +185 -0
- data/lib/tasks/mat_views_tasks.rake +145 -0
- metadata +95 -0
data/lib/mat_views.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright Codevedas Inc. 2025-present
|
4
|
+
#
|
5
|
+
# This source code is licensed under the MIT license found in the
|
6
|
+
# LICENSE file in the root directory of this source tree.
|
7
|
+
|
8
|
+
require 'mat_views/version'
|
9
|
+
require 'mat_views/engine'
|
10
|
+
require 'mat_views/configuration'
|
11
|
+
require 'mat_views/jobs/adapter'
|
12
|
+
require 'mat_views/service_response'
|
13
|
+
require 'mat_views/services/base_service'
|
14
|
+
require 'mat_views/services/create_view'
|
15
|
+
require 'mat_views/services/regular_refresh'
|
16
|
+
require 'mat_views/services/concurrent_refresh'
|
17
|
+
require 'mat_views/services/swap_refresh'
|
18
|
+
require 'mat_views/services/delete_view'
|
19
|
+
|
20
|
+
##
|
21
|
+
# MatViews is a Rails engine that provides first-class support for
|
22
|
+
# PostgreSQL materialized views in Rails applications.
|
23
|
+
#
|
24
|
+
# Features include:
|
25
|
+
# - Declarative definitions for materialized views
|
26
|
+
# - Safe creation, refresh (regular, concurrent, swap), and deletion
|
27
|
+
# - Background job integration (ActiveJob, Sidekiq, Resque)
|
28
|
+
# - Tracking of run history and metrics
|
29
|
+
# - Rake task helpers for operational workflows
|
30
|
+
#
|
31
|
+
# Usage:
|
32
|
+
# MatViews.configure do |config|
|
33
|
+
# config.job_queue = :low_priority
|
34
|
+
# config.job_adapter = :sidekiq
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Once mounted, Rails apps can leverage MatViews services and jobs
|
38
|
+
# to manage materialized views consistently.
|
39
|
+
module MatViews
|
40
|
+
class << self
|
41
|
+
# Global configuration for MatViews
|
42
|
+
# @return [MatViews::Configuration]
|
43
|
+
attr_accessor :configuration
|
44
|
+
|
45
|
+
# Configure MatViews via block.
|
46
|
+
#
|
47
|
+
# Example:
|
48
|
+
# MatViews.configure do |config|
|
49
|
+
# config.job_adapter = :sidekiq
|
50
|
+
# config.job_queue = :materialized
|
51
|
+
# end
|
52
|
+
def configure
|
53
|
+
self.configuration ||= Configuration.new
|
54
|
+
yield(configuration)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module MatViews
|
6
|
+
module Tasks
|
7
|
+
##
|
8
|
+
# Helpers module provides utility methods for MatViews Rake tasks.
|
9
|
+
#
|
10
|
+
# These helpers support:
|
11
|
+
# - Database connections
|
12
|
+
# - Logging
|
13
|
+
# - Parsing boolean/flag-like arguments
|
14
|
+
# - Confirmation prompts
|
15
|
+
# - Enqueueing background jobs for create, refresh, and delete operations
|
16
|
+
# - Looking up materialized view definitions
|
17
|
+
#
|
18
|
+
# By extracting this logic, Rake tasks can remain clean and declarative.
|
19
|
+
module Helpers
|
20
|
+
module_function
|
21
|
+
|
22
|
+
# Returns the current database connection.
|
23
|
+
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
24
|
+
def mv_conn
|
25
|
+
ActiveRecord::Base.connection
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the Rails logger.
|
29
|
+
# @return [Logger]
|
30
|
+
def logger
|
31
|
+
Rails.logger
|
32
|
+
end
|
33
|
+
|
34
|
+
# Check if a value is a "truthy" boolean-like string.
|
35
|
+
# Recognized: 1, true, yes, y, --yes
|
36
|
+
# @param value [String, Boolean, nil]
|
37
|
+
# @return [Boolean]
|
38
|
+
def booleanish_true?(value)
|
39
|
+
str = value.to_s.strip.downcase
|
40
|
+
%w[1 true yes y --yes].include?(str)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Whether confirmation should be skipped (YES env or arg).
|
44
|
+
def skip_confirm?(arg)
|
45
|
+
booleanish_true?(arg || ENV.fetch('YES', nil))
|
46
|
+
end
|
47
|
+
|
48
|
+
# Parse whether force mode is enabled (FORCE env or arg).
|
49
|
+
def parse_force?(arg)
|
50
|
+
booleanish_true?(arg || ENV.fetch('FORCE', nil))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Parse row count strategy from arg or ROW_COUNT_STRATEGY env.
|
54
|
+
# Defaults to :estimated if blank.
|
55
|
+
def parse_row_count_strategy(arg)
|
56
|
+
s = (arg || ENV.fetch('ROW_COUNT_STRATEGY', nil)).to_s.strip
|
57
|
+
return :estimated if s.empty?
|
58
|
+
|
59
|
+
s.to_sym
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check if a materialized view exists in schema.
|
63
|
+
# @param rel [String] relation name
|
64
|
+
# @param schema [String] schema name
|
65
|
+
# @return [Boolean]
|
66
|
+
def matview_exists?(rel, schema: 'public')
|
67
|
+
mv_conn.select_value(<<~SQL).to_i.positive?
|
68
|
+
SELECT COUNT(*)
|
69
|
+
FROM pg_matviews
|
70
|
+
WHERE schemaname = #{mv_conn.quote(schema)} AND matviewname = #{mv_conn.quote(rel)}
|
71
|
+
SQL
|
72
|
+
end
|
73
|
+
|
74
|
+
# Find a MatViewDefinition by raw name (schema.rel or rel).
|
75
|
+
# Raises if none found or mismatch with DB presence.
|
76
|
+
#
|
77
|
+
# @param raw_name [String] schema-qualified or unqualified view name
|
78
|
+
# @return [MatViews::MatViewDefinition]
|
79
|
+
# @raise [RuntimeError] if no definition found or mismatch with DB
|
80
|
+
def find_definition_by_name!(raw_name)
|
81
|
+
raise 'view_name is required' if raw_name.nil? || raw_name.to_s.strip.empty?
|
82
|
+
|
83
|
+
schema, rel =
|
84
|
+
if raw_name.to_s.include?('.')
|
85
|
+
parts = raw_name.to_s.split('.', 2)
|
86
|
+
[parts[0], parts[1]]
|
87
|
+
else
|
88
|
+
[nil, raw_name.to_s]
|
89
|
+
end
|
90
|
+
|
91
|
+
defn = MatViews::MatViewDefinition.find_by(name: rel)
|
92
|
+
return defn if defn
|
93
|
+
|
94
|
+
if schema && matview_exists?(rel, schema: schema)
|
95
|
+
raise "Materialized view #{schema}.#{rel} exists, but no MatViews::MatViewDefinition record was found for name=#{rel.inspect}"
|
96
|
+
end
|
97
|
+
|
98
|
+
raise "No MatViews::MatViewDefinition found for #{raw_name.inspect}"
|
99
|
+
end
|
100
|
+
|
101
|
+
# Ask user to confirm a destructive action, unless skipped.
|
102
|
+
#
|
103
|
+
# @param message [String] confirmation message
|
104
|
+
# @param skip [Boolean] whether to skip confirmation
|
105
|
+
# @raise [RuntimeError] if user declines confirmation
|
106
|
+
# @return [void]
|
107
|
+
#
|
108
|
+
# If `skip` is true, logs the message and returns without prompting.
|
109
|
+
# Otherwise, prompts user for confirmation and raises if declined.
|
110
|
+
def confirm!(message, skip: false)
|
111
|
+
if skip
|
112
|
+
logger.info("[mat_views] #{message} — confirmation skipped.")
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
logger.info("[mat_views] #{message}")
|
117
|
+
$stdout.print('Proceed? [y/N]: ')
|
118
|
+
$stdout.flush
|
119
|
+
ans = $stdin.gets&.strip&.downcase
|
120
|
+
return if ans&.start_with?('y')
|
121
|
+
|
122
|
+
raise 'Aborted.'
|
123
|
+
end
|
124
|
+
|
125
|
+
# Enqueue a CreateView job for given definition.
|
126
|
+
#
|
127
|
+
# @param definition_id [Integer] MatViewDefinition ID
|
128
|
+
# @param force [Boolean] whether to force creation
|
129
|
+
# @return [void]
|
130
|
+
def enqueue_create!(definition_id, force)
|
131
|
+
q = MatViews.configuration.job_queue || :default
|
132
|
+
MatViews::Jobs::Adapter.enqueue(
|
133
|
+
MatViews::CreateViewJob,
|
134
|
+
queue: q,
|
135
|
+
args: [definition_id, force]
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Enqueue a RefreshView job for given definition.
|
140
|
+
#
|
141
|
+
# @param definition_id [Integer] MatViewDefinition ID
|
142
|
+
# @param row_count_strategy [Symbol] :estimated or :exact
|
143
|
+
# @return [void]
|
144
|
+
#
|
145
|
+
# This method allows scheduling a refresh operation with the specified row count strategy.
|
146
|
+
# It uses the configured job adapter to enqueue the job.
|
147
|
+
def enqueue_refresh!(definition_id, row_count_strategy)
|
148
|
+
q = MatViews.configuration.job_queue || :default
|
149
|
+
MatViews::Jobs::Adapter.enqueue(
|
150
|
+
MatViews::RefreshViewJob,
|
151
|
+
queue: q,
|
152
|
+
args: [definition_id, row_count_strategy]
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Parse cascade option (CASCADE env or arg).
|
157
|
+
#
|
158
|
+
# @param arg [String, Boolean, nil] argument or environment variable value
|
159
|
+
# @return [Boolean] true if cascade is enabled, false otherwise
|
160
|
+
#
|
161
|
+
# This method checks if the CASCADE option is set to true, allowing for cascading drops.
|
162
|
+
# It defaults to the value of the CASCADE environment variable if not provided.
|
163
|
+
def parse_cascade?(arg)
|
164
|
+
booleanish_true?(arg || ENV.fetch('CASCADE', nil))
|
165
|
+
end
|
166
|
+
|
167
|
+
# Enqueue a DeleteView job for given definition.
|
168
|
+
#
|
169
|
+
# @param definition_id [Integer] MatViewDefinition ID
|
170
|
+
# @param cascade [Boolean] whether to drop with CASCADE
|
171
|
+
# @return [void]
|
172
|
+
#
|
173
|
+
# This method schedules a job to delete the materialized view, optionally with CASCADE.
|
174
|
+
# It uses the configured job adapter to enqueue the job.
|
175
|
+
def enqueue_delete!(definition_id, cascade)
|
176
|
+
q = MatViews.configuration.job_queue || :default
|
177
|
+
MatViews::Jobs::Adapter.enqueue(
|
178
|
+
MatViews::DeleteViewJob,
|
179
|
+
queue: q,
|
180
|
+
args: [definition_id, cascade]
|
181
|
+
)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require_relative 'helpers'
|
5
|
+
|
6
|
+
# rubocop:disable Metrics/BlockLength
|
7
|
+
namespace :mat_views do
|
8
|
+
helpers = MatViews::Tasks::Helpers
|
9
|
+
|
10
|
+
# ───────────── CREATE ─────────────
|
11
|
+
|
12
|
+
desc 'Enqueue a CREATE for a specific view by its name (optionally schema-qualified)'
|
13
|
+
task :create_by_name, %i[view_name force yes] => :environment do |_t, args|
|
14
|
+
force = helpers.parse_force?(args[:force])
|
15
|
+
skip = helpers.skip_confirm?(args[:yes])
|
16
|
+
defn = helpers.find_definition_by_name!(args[:view_name])
|
17
|
+
|
18
|
+
helpers.confirm!("Enqueue CREATE for view=#{defn.name} (id=#{defn.id}), force=#{force}", skip: skip)
|
19
|
+
helpers.enqueue_create!(defn.id, force)
|
20
|
+
helpers.logger.info("[mat_views] Enqueued CreateViewJob for definition ##{defn.id} (#{defn.name}), force=#{force}")
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Enqueue a CREATE for a specific view by its definition ID'
|
24
|
+
task :create_by_id, %i[definition_id force yes] => :environment do |_t, args|
|
25
|
+
raise 'mat_views:create_by_id requires a definition_id parameter' if args[:definition_id].to_s.strip.empty?
|
26
|
+
|
27
|
+
force = helpers.parse_force?(args[:force])
|
28
|
+
skip = helpers.skip_confirm?(args[:yes])
|
29
|
+
|
30
|
+
defn = MatViews::MatViewDefinition.find_by(id: args[:definition_id])
|
31
|
+
raise "No MatViews::MatViewDefinition found for id=#{args[:definition_id]}" unless defn
|
32
|
+
|
33
|
+
helpers.confirm!("Enqueue CREATE for id=#{defn.id} (#{defn.name}), force=#{force}", skip: skip)
|
34
|
+
helpers.enqueue_create!(defn.id, force)
|
35
|
+
helpers.logger.info("[mat_views] Enqueued CreateViewJob for definition ##{defn.id} (#{defn.name}), force=#{force}")
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Enqueue CREATE jobs for ALL defined materialized views'
|
39
|
+
task :create_all, %i[force yes] => :environment do |_t, args|
|
40
|
+
force = helpers.parse_force?(args[:force])
|
41
|
+
skip = helpers.skip_confirm?(args[:yes])
|
42
|
+
|
43
|
+
scope = MatViews::MatViewDefinition.all
|
44
|
+
count = scope.count
|
45
|
+
if count.zero?
|
46
|
+
helpers.logger.info('[mat_views] No mat view definitions found.')
|
47
|
+
next
|
48
|
+
end
|
49
|
+
|
50
|
+
helpers.confirm!("Enqueue CREATE for ALL (#{count}) views, force=#{force}", skip: skip)
|
51
|
+
scope.find_each { |defn| helpers.enqueue_create!(defn.id, force) }
|
52
|
+
helpers.logger.info("[mat_views] Enqueued #{count} CreateViewJob(s), force=#{force}.")
|
53
|
+
end
|
54
|
+
|
55
|
+
# ───────────── REFRESH ─────────────
|
56
|
+
|
57
|
+
desc 'Enqueue a REFRESH for a specific view by its name (optionally schema-qualified)'
|
58
|
+
task :refresh_by_name, %i[view_name row_count_strategy yes] => :environment do |_t, args|
|
59
|
+
rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
|
60
|
+
skip = helpers.skip_confirm?(args[:yes])
|
61
|
+
defn = helpers.find_definition_by_name!(args[:view_name])
|
62
|
+
|
63
|
+
helpers.confirm!("Enqueue REFRESH for view=#{defn.name} (id=#{defn.id}), row_count_strategy=#{rcs}", skip: skip)
|
64
|
+
helpers.enqueue_refresh!(defn.id, rcs)
|
65
|
+
helpers.logger.info("[mat_views] Enqueued RefreshViewJob for definition ##{defn.id} (#{defn.name}), row_count_strategy=#{rcs}")
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'Enqueue a REFRESH for a specific view by its definition ID'
|
69
|
+
task :refresh_by_id, %i[definition_id row_count_strategy yes] => :environment do |_t, args|
|
70
|
+
raise 'mat_views:refresh_by_id requires a definition_id parameter' if args[:definition_id].to_s.strip.empty?
|
71
|
+
|
72
|
+
rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
|
73
|
+
skip = helpers.skip_confirm?(args[:yes])
|
74
|
+
|
75
|
+
defn = MatViews::MatViewDefinition.find_by(id: args[:definition_id])
|
76
|
+
raise "No MatViews::MatViewDefinition found for id=#{args[:definition_id]}" unless defn
|
77
|
+
|
78
|
+
helpers.confirm!("Enqueue REFRESH for id=#{defn.id} (#{defn.name}), row_count_strategy=#{rcs}", skip: skip)
|
79
|
+
helpers.enqueue_refresh!(defn.id, rcs)
|
80
|
+
helpers.logger.info("[mat_views] Enqueued RefreshViewJob for definition ##{defn.id} (#{defn.name}), row_count_strategy=#{rcs}")
|
81
|
+
end
|
82
|
+
|
83
|
+
desc 'Enqueue REFRESH jobs for ALL defined materialized views'
|
84
|
+
task :refresh_all, %i[row_count_strategy yes] => :environment do |_t, args|
|
85
|
+
rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
|
86
|
+
skip = helpers.skip_confirm?(args[:yes])
|
87
|
+
|
88
|
+
scope = MatViews::MatViewDefinition.all
|
89
|
+
count = scope.count
|
90
|
+
if count.zero?
|
91
|
+
helpers.logger.info('[mat_views] No mat view definitions found.')
|
92
|
+
next
|
93
|
+
end
|
94
|
+
|
95
|
+
helpers.confirm!("Enqueue REFRESH for ALL (#{count}) views, row_count_strategy=#{rcs}", skip: skip)
|
96
|
+
scope.find_each { |defn| helpers.enqueue_refresh!(defn.id, rcs) }
|
97
|
+
helpers.logger.info("[mat_views] Enqueued #{count} RefreshViewJob(s), row_count_strategy=#{rcs}.")
|
98
|
+
end
|
99
|
+
|
100
|
+
# ───────────── DELETE ─────────────
|
101
|
+
|
102
|
+
desc 'Enqueue a DELETE (DROP MATERIALIZED VIEW) for a specific view by its name (optionally schema-qualified)'
|
103
|
+
task :delete_by_name, %i[view_name cascade yes] => :environment do |_t, args|
|
104
|
+
cascade = helpers.parse_cascade?(args[:cascade])
|
105
|
+
skip = helpers.skip_confirm?(args[:yes])
|
106
|
+
defn = helpers.find_definition_by_name!(args[:view_name])
|
107
|
+
|
108
|
+
helpers.confirm!("Enqueue DELETE for view=#{defn.name} (id=#{defn.id}), cascade=#{cascade}", skip: skip)
|
109
|
+
helpers.enqueue_delete!(defn.id, cascade)
|
110
|
+
helpers.logger.info("[mat_views] Enqueued DeleteViewJob for definition ##{defn.id} (#{defn.name}), cascade=#{cascade}")
|
111
|
+
end
|
112
|
+
|
113
|
+
desc 'Enqueue a DELETE (DROP MATERIALIZED VIEW) for a specific view by its definition ID'
|
114
|
+
task :delete_by_id, %i[definition_id cascade yes] => :environment do |_t, args|
|
115
|
+
raise 'mat_views:delete_by_id requires a definition_id parameter' if args[:definition_id].to_s.strip.empty?
|
116
|
+
|
117
|
+
cascade = helpers.parse_cascade?(args[:cascade])
|
118
|
+
skip = helpers.skip_confirm?(args[:yes])
|
119
|
+
|
120
|
+
defn = MatViews::MatViewDefinition.find_by(id: args[:definition_id])
|
121
|
+
raise "No MatViews::MatViewDefinition found for id=#{args[:definition_id]}" unless defn
|
122
|
+
|
123
|
+
helpers.confirm!("Enqueue DELETE for id=#{defn.id} (#{defn.name}), cascade=#{cascade}", skip: skip)
|
124
|
+
helpers.enqueue_delete!(defn.id, cascade)
|
125
|
+
helpers.logger.info("[mat_views] Enqueued DeleteViewJob for definition ##{defn.id} (#{defn.name}), cascade=#{cascade}")
|
126
|
+
end
|
127
|
+
|
128
|
+
desc 'Enqueue DELETE jobs for ALL defined materialized views'
|
129
|
+
task :delete_all, %i[cascade yes] => :environment do |_t, args|
|
130
|
+
cascade = helpers.parse_cascade?(args[:cascade])
|
131
|
+
skip = helpers.skip_confirm?(args[:yes])
|
132
|
+
|
133
|
+
scope = MatViews::MatViewDefinition.all
|
134
|
+
count = scope.count
|
135
|
+
if count.zero?
|
136
|
+
helpers.logger.info('[mat_views] No mat view definitions found.')
|
137
|
+
next
|
138
|
+
end
|
139
|
+
|
140
|
+
helpers.confirm!("Enqueue DELETE for ALL (#{count}) views, cascade=#{cascade}", skip: skip)
|
141
|
+
scope.find_each { |defn| helpers.enqueue_delete!(defn.id, cascade) }
|
142
|
+
helpers.logger.info("[mat_views] Enqueued #{count} DeleteViewJob(s), cascade=#{cascade}.")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
# rubocop:enable Metrics/BlockLength
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mat_views
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nitesh Purohit
|
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: rails
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '7.1'
|
19
|
+
- - "<"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '9.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '7.1'
|
29
|
+
- - "<"
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '9.0'
|
32
|
+
description: A mountable Rails engine to track, define, refresh, and monitor Postgres
|
33
|
+
materialized views.
|
34
|
+
email:
|
35
|
+
- nitesh.purohit.it@gmail.com
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- LICENSE
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- app/assets/stylesheets/mat_views/application.css
|
44
|
+
- app/jobs/mat_views/application_job.rb
|
45
|
+
- app/jobs/mat_views/create_view_job.rb
|
46
|
+
- app/jobs/mat_views/delete_view_job.rb
|
47
|
+
- app/jobs/mat_views/refresh_view_job.rb
|
48
|
+
- app/models/mat_views/application_record.rb
|
49
|
+
- app/models/mat_views/mat_view_definition.rb
|
50
|
+
- app/models/mat_views/mat_view_run.rb
|
51
|
+
- config/routes.rb
|
52
|
+
- lib/generators/mat_views/install/install_generator.rb
|
53
|
+
- lib/generators/mat_views/install/templates/create_mat_view_definitions.rb
|
54
|
+
- lib/generators/mat_views/install/templates/create_mat_view_runs.rb
|
55
|
+
- lib/generators/mat_views/install/templates/mat_views_initializer.rb
|
56
|
+
- lib/mat_views.rb
|
57
|
+
- lib/mat_views/configuration.rb
|
58
|
+
- lib/mat_views/engine.rb
|
59
|
+
- lib/mat_views/jobs/adapter.rb
|
60
|
+
- lib/mat_views/service_response.rb
|
61
|
+
- lib/mat_views/services/base_service.rb
|
62
|
+
- lib/mat_views/services/concurrent_refresh.rb
|
63
|
+
- lib/mat_views/services/create_view.rb
|
64
|
+
- lib/mat_views/services/delete_view.rb
|
65
|
+
- lib/mat_views/services/regular_refresh.rb
|
66
|
+
- lib/mat_views/services/swap_refresh.rb
|
67
|
+
- lib/mat_views/version.rb
|
68
|
+
- lib/tasks/helpers.rb
|
69
|
+
- lib/tasks/mat_views_tasks.rake
|
70
|
+
homepage: https://github.com/Code-Vedas/rails_materialized_views
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata:
|
74
|
+
homepage_uri: https://github.com/Code-Vedas/rails_materialized_views
|
75
|
+
source_code_uri: https://github.com/Code-Vedas/rails_materialized_views.git
|
76
|
+
changelog_uri: https://github.com/Code-Vedas/rails_materialized_views/blob/main/CHANGELOG.md
|
77
|
+
rubygems_mfa_required: 'true'
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '3.2'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubygems_version: 3.6.8
|
93
|
+
specification_version: 4
|
94
|
+
summary: Manage and refresh PostgreSQL materialized views in Rails
|
95
|
+
test_files: []
|