mat_views 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed05279bf4f2812ebeb8cb882f38772636f28c273b0992a878e13fb06b95d17f
4
- data.tar.gz: 8fa0d71481c042d1b763e012b91dfe397373161decf8a68ef13c27bd3349fb33
3
+ metadata.gz: b7120f5d0821cb752001586381ab084a7366aeaa9616b86ef6e648be2b52d152
4
+ data.tar.gz: ab541f3705c974e8272cc07db3c9612add6311cb159467e04055eb980fa648b6
5
5
  SHA512:
6
- metadata.gz: 3233a39ea2a44171bc95d15be3bfccf53f507f49efb946787888f3aeae95b94273126331ac607a83e3d9e23c1f8770a4b0362ee2825b6c7ae1c60aab6f9aff92
7
- data.tar.gz: 05566cd4d75f7ef05465387a39794ca1cae6351375e8b298b7deec0f1503e517d09429f5d1b605042dd6f739c0fa6f868fc839ce600bf201228f206c01dbaec9
6
+ metadata.gz: e3d260f3fcaf3029af874c943ae72b8786e2e86232dbc352a955ae9c4e1add587a0df88507cb86fe5463b308df1ff354b64ebfed00ebe2bcd124924984121797
7
+ data.tar.gz: 235208e771305d1085eeea02f5bd762bfb91942901e2b9d7d127281d8e7d04da878fcbf5eb8df4cdec9434c1e5db243fb90b20c79d77ede4136006449cbe2df8
data/README.md CHANGED
@@ -96,9 +96,9 @@ MatViews::Jobs::Adapter.enqueue(job_class, queue: :default, args: [...])
96
96
 
97
97
  ```bash
98
98
  # Create
99
- bundle exec rake mat_views:create_by_name\[VIEW_NAME,force,--yes]
100
- bundle exec rake mat_views:create_by_id\[ID,force,--yes]
101
- bundle exec rake mat_views:create_all\[force,--yes]
99
+ bundle exec rake mat_views:create_by_name\[VIEW_NAME,force,row_count_strategy,--yes]
100
+ bundle exec rake mat_views:create_by_id\[ID,force,row_count_strategy,--yes]
101
+ bundle exec rake mat_views:create_all\[force,row_count_strategy,--yes]
102
102
 
103
103
  # Refresh
104
104
  bundle exec rake mat_views:refresh_by_name\[VIEW_NAME,row_count_strategy,--yes]
@@ -106,9 +106,9 @@ bundle exec rake mat_views:refresh_by_id\[ID,row_count_strategy,--yes]
106
106
  bundle exec rake mat_views:refresh_all\[row_count_strategy,--yes]
107
107
 
108
108
  # Delete
109
- bundle exec rake mat_views:delete_by_name\[VIEW_NAME,cascade,--yes]
110
- bundle exec rake mat_views:delete_by_id\[ID,cascade,--yes]
111
- bundle exec rake mat_views:delete_all\[cascade,--yes]
109
+ bundle exec rake mat_views:delete_by_name\[VIEW_NAME,cascade,row_count_strategy,--yes]
110
+ bundle exec rake mat_views:delete_by_id\[ID,cascade,row_count_strategy,--yes]
111
+ bundle exec rake mat_views:delete_all\[cascade,row_count_strategy,--yes]
112
112
  ```
113
113
 
114
114
  ---
@@ -35,5 +35,110 @@ module MatViews
35
35
  # end
36
36
  #
37
37
  class ApplicationJob < ActiveJob::Base
38
+ private
39
+
40
+ def record_run(definition, operation, &)
41
+ start = monotime
42
+ run = start_run(definition, operation)
43
+ response = yield
44
+ finalize_run(run, response, elapsed_ms(start))
45
+ response.to_h
46
+ rescue StandardError => e
47
+ fail_run(run, e, elapsed_ms(start))
48
+ raise e
49
+ end
50
+
51
+ ##
52
+ # Begin a {MatViews::MatViewRun} row for lifecycle tracking.
53
+ #
54
+ # @api private
55
+ #
56
+ # @return [MatViews::MatViewRun]
57
+ #
58
+ def start_run(definition, operation)
59
+ MatViews::MatViewRun.create!(
60
+ mat_view_definition: definition,
61
+ status: :running,
62
+ started_at: Time.current,
63
+ operation: operation
64
+ )
65
+ end
66
+
67
+ ##
68
+ # Finalize the run with success/failure, timing, and meta from the response.
69
+ #
70
+ # @api private
71
+ #
72
+ # @param run [MatViews::MatViewRun]
73
+ # @param response [MatViews::ServiceResponse, nil] may be nil if exception raised
74
+ # @param duration_ms [Integer]
75
+ # @return [void]
76
+ #
77
+ def finalize_run(run, response, duration_ms)
78
+ base_attrs = {
79
+ finished_at: Time.current,
80
+ duration_ms: duration_ms,
81
+ meta: { request: response.request, response: response.response }.compact
82
+ }
83
+
84
+ if response.success?
85
+ run.update!(base_attrs.merge(status: :success, error: nil))
86
+ else
87
+ run.update!(base_attrs.merge(status: :failed, error: response.error))
88
+ end
89
+ end
90
+
91
+ ##
92
+ # Mark the run failed due to an exception.
93
+ #
94
+ # @api private
95
+ #
96
+ # @param run [MatViews::MatViewRun]
97
+ # @param exception [Exception]
98
+ # @param duration_ms [Integer]
99
+ # @return [void]
100
+ #
101
+ def fail_run(run, exception, duration_ms)
102
+ run&.update!(
103
+ error: exception.mv_serialize_error,
104
+ finished_at: Time.current,
105
+ duration_ms: duration_ms,
106
+ status: :failed
107
+ )
108
+ end
109
+
110
+ ##
111
+ # Monotonic clock getter (for elapsed-time measurement).
112
+ #
113
+ # @api private
114
+ # @return [Float] seconds
115
+ #
116
+ def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
117
+
118
+ ##
119
+ # Convert monotonic start time to elapsed milliseconds.
120
+ #
121
+ # @api private
122
+ # @param start [Float]
123
+ # @return [Integer] elapsed ms
124
+ #
125
+ def elapsed_ms(start) = ((monotime - start) * 1000).round
126
+
127
+ ##
128
+ # Normalize the strategy argument into a symbol or default.
129
+ #
130
+ # @api private
131
+ #
132
+ # @param arg [Symbol, String, nil]
133
+ # @return [Symbol] One of `:estimated`, `:exact`, or `:none` by default.
134
+ #
135
+ def normalize_strategy(arg)
136
+ case arg
137
+ when String, Symbol
138
+ arg.to_sym
139
+ else
140
+ :none
141
+ end
142
+ end
38
143
  end
39
144
  end
@@ -29,7 +29,7 @@ module MatViews
29
29
  # @example Inline run (test/dev)
30
30
  # MatViews::CreateViewJob.new.perform(definition.id, false)
31
31
  #
32
- class CreateViewJob < ::ActiveJob::Base
32
+ class CreateViewJob < ApplicationJob
33
33
  ##
34
34
  # Queue name for the job.
35
35
  #
@@ -40,37 +40,29 @@ module MatViews
40
40
  queue_as { MatViews.configuration.job_queue || :default }
41
41
 
42
42
  ##
43
- # Perform the job for the given materialized view definition.
43
+ # Perform the create job for the given materialized view definition.
44
44
  #
45
45
  # @api public
46
46
  #
47
47
  # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
48
48
  # @param force_arg [Boolean, Hash, nil] Optional flag or hash (`{ force: true }`)
49
- # to force creation (drop/recreate) when supported by the service.
49
+ # @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
50
50
  #
51
- # @return [Hash] A serialized {MatViews::ServiceResponse#to_h}:
52
- # - `:status` [Symbol] one of `:ok, :created, :updated, :noop, :error`
53
- # - `:payload` [Hash] service-specific payload (also stored in run.meta)
54
- # - `:error` [String, nil] error message if any
55
- # - `:duration_ms` [Integer, nil]
51
+ # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
52
+ # - `:status` [Symbol]
53
+ # - `:payload` [Hash]
54
+ # - `:error` [String, nil]
55
+ # - `:duration_ms` [Integer]
56
56
  # - `:meta` [Hash]
57
57
  #
58
58
  # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
59
59
  #
60
- # @see MatViews::Services::CreateView
61
- #
62
- def perform(definition_id, force_arg = nil)
63
- force = normalize_force(force_arg)
64
-
60
+ def perform(definition_id, force_arg = nil, row_count_strategy_arg = nil)
65
61
  definition = MatViews::MatViewDefinition.find(definition_id)
66
- run = start_run(definition)
67
62
 
68
- response, duration_ms = execute(definition, force: force)
69
- finalize_run!(run, response, duration_ms)
70
- response.to_h
71
- rescue StandardError => e
72
- fail_run!(run, e) if run
73
- raise e
63
+ record_run(definition, :create) do
64
+ MatViews::Services::CreateView.new(definition, force: force?(force_arg), row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
65
+ end
74
66
  end
75
67
 
76
68
  private
@@ -82,107 +74,13 @@ module MatViews
82
74
  #
83
75
  # @api private
84
76
  #
85
- # @param arg [Object] Raw argument; commonly `true/false`, `nil`, or `Hash`.
77
+ # @param arg [Object] Raw argument; commonly `true/false`, `nil`
86
78
  # @return [Boolean] Coerced force flag.
87
79
  #
88
- def normalize_force(arg)
89
- case arg
90
- when Hash
91
- arg[:force] || arg['force'] || false
92
- else
93
- !!arg
94
- end
95
- end
80
+ def force?(arg)
81
+ return false if arg.nil?
96
82
 
97
- ##
98
- # Execute the create service and measure duration.
99
- #
100
- # @api private
101
- #
102
- # @param definition [MatViews::MatViewDefinition]
103
- # @param force [Boolean]
104
- # @return [Array(MatViews::ServiceResponse, Integer)] response and elapsed ms.
105
- #
106
- def execute(definition, force:)
107
- started = monotime
108
- response = MatViews::Services::CreateView.new(definition, force: force).run
109
- [response, elapsed_ms(started)]
83
+ !!arg
110
84
  end
111
-
112
- ##
113
- # Begin a {MatViews::MatViewRun} row for lifecycle tracking.
114
- #
115
- # @api private
116
- #
117
- # @param definition [MatViews::MatViewDefinition]
118
- # @return [MatViews::MatViewRun] newly created run with `status: :running`
119
- #
120
- def start_run(definition)
121
- MatViews::MatViewRun.create!(
122
- mat_view_definition: definition,
123
- status: :running,
124
- started_at: Time.current,
125
- operation: :create
126
- )
127
- end
128
-
129
- ##
130
- # Finalize the run with success/failure, timing, and meta from the response payload.
131
- #
132
- # @api private
133
- #
134
- # @param run [MatViews::MatViewRun]
135
- # @param response [MatViews::ServiceResponse]
136
- # @param duration_ms [Integer]
137
- # @return [void]
138
- #
139
- def finalize_run!(run, response, duration_ms)
140
- base_attrs = {
141
- finished_at: Time.current,
142
- duration_ms: duration_ms,
143
- meta: response.payload || {}
144
- }
145
-
146
- if response.success?
147
- run.update!(base_attrs.merge(status: :success, error: nil))
148
- else
149
- run.update!(base_attrs.merge(status: :failed, error: response.error.to_s.presence))
150
- end
151
- end
152
-
153
- ##
154
- # Mark the run failed due to an exception.
155
- #
156
- # @api private
157
- #
158
- # @param run [MatViews::MatViewRun]
159
- # @param exception [Exception]
160
- # @return [void]
161
- #
162
- def fail_run!(run, exception)
163
- run.update!(
164
- finished_at: Time.current,
165
- duration_ms: run.duration_ms || 0,
166
- error: "#{exception.class}: #{exception.message}",
167
- status: :failed
168
- )
169
- end
170
-
171
- ##
172
- # Monotonic clock getter (for elapsed-time measurement).
173
- #
174
- # @api private
175
- # @return [Float] seconds from a monotonic source.
176
- #
177
- def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
178
-
179
- ##
180
- # Convert a monotonic start time to elapsed milliseconds.
181
- #
182
- # @api private
183
- # @param start [Float] monotonic seconds.
184
- # @return [Integer] elapsed milliseconds.
185
- #
186
- def elapsed_ms(start) = ((monotime - start) * 1000).round
187
85
  end
188
86
  end
@@ -25,7 +25,12 @@ module MatViews
25
25
  # @example Inline run (test/dev)
26
26
  # MatViews::DeleteViewJob.new.perform(definition.id, false)
27
27
  #
28
- class DeleteViewJob < ::ActiveJob::Base
28
+ class DeleteViewJob < ApplicationJob
29
+ ###
30
+ # cascade flag for the service call
31
+ # @return [Boolean]
32
+ attr_reader :cascade
33
+
29
34
  ##
30
35
  # Queue name for the job.
31
36
  #
@@ -40,60 +45,35 @@ module MatViews
40
45
  #
41
46
  # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
42
47
  # @param cascade_arg [Boolean, String, Integer, Hash, nil] Cascade option.
43
- # Accepts:
44
- # - `true/false`
45
- # - `1` (treated as true)
46
- # - `"true"`, `"1"`, `"yes"` (case-insensitive)
47
- # - `{ cascade: true }` or `{ "cascade" => true }`
48
+ # @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
48
49
  #
49
- # @return [Hash] A serialized {MatViews::ServiceResponse#to_h}:
50
- # - `:status` [Symbol] `:success`, `:failed`, etc.
51
- # - `:payload` [Hash] response payload (also stored in run.meta)
50
+ # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
51
+ # - `:status` [Symbol]
52
+ # - `:payload` [Hash]
52
53
  # - `:error` [String, nil]
53
54
  # - `:duration_ms` [Integer]
54
55
  # - `:meta` [Hash]
55
56
  #
56
57
  # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
57
58
  #
58
- def perform(definition_id, cascade_arg = nil)
59
- cascade = normalize_cascade?(cascade_arg)
59
+ def perform(definition_id, cascade_arg = nil, row_count_strategy_arg = nil)
60
60
  definition = MatViews::MatViewDefinition.find(definition_id)
61
- run = start_run(definition)
62
-
63
- response, duration_ms = execute(definition, cascade: cascade)
64
- finalize_run!(run, response, duration_ms)
65
- response.to_h
66
- rescue StandardError => e
67
- fail_run!(run, e) if run
68
- raise e
61
+ record_run(definition, :drop) do
62
+ MatViews::Services::DeleteView.new(definition, cascade: cascade?(cascade_arg),
63
+ row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
64
+ end
69
65
  end
70
66
 
71
67
  private
72
68
 
73
69
  ##
74
- # Normalize cascade argument into a boolean.
75
- #
76
- # @api private
77
- # @param arg [Object] Raw cascade argument.
78
- # @return [Boolean] Whether to cascade drop.
79
- #
80
- def normalize_cascade?(arg)
81
- value = if arg.is_a?(Hash)
82
- arg[:cascade] || arg['cascade']
83
- else
84
- arg
85
- end
86
- cascade_value_trueish?(value)
87
- end
88
-
89
- ##
90
- # Evaluate if a value is "truthy" for cascade purposes.
70
+ # Evaluate if a value is "truthy" for cascade.
91
71
  #
92
72
  # @api private
93
- # @param value [Object]
73
+ # @param value [TrueClass, FalseClass, String, Integer, nil, Object]
94
74
  # @return [Boolean]
95
75
  #
96
- def cascade_value_trueish?(value)
76
+ def cascade?(value)
97
77
  case value
98
78
  when true
99
79
  true
@@ -105,92 +85,5 @@ module MatViews
105
85
  false
106
86
  end
107
87
  end
108
-
109
- ##
110
- # Execute the delete service and measure duration.
111
- #
112
- # @api private
113
- # @param definition [MatViews::MatViewDefinition]
114
- # @param cascade [Boolean]
115
- # @return [Array(MatViews::ServiceResponse, Integer)]
116
- #
117
- def execute(definition, cascade:)
118
- started = monotime
119
- response = MatViews::Services::DeleteView.new(definition, cascade: cascade, if_exists: true).run
120
- [response, elapsed_ms(started)]
121
- end
122
-
123
- ##
124
- # Begin a {MatViews::MatViewRun} row for lifecycle tracking.
125
- #
126
- # @api private
127
- # @param definition [MatViews::MatViewDefinition]
128
- # @return [MatViews::MatViewRun]
129
- #
130
- def start_run(definition)
131
- MatViews::MatViewRun.create!(
132
- mat_view_definition: definition,
133
- status: :running,
134
- started_at: Time.current,
135
- operation: :drop
136
- )
137
- end
138
-
139
- ##
140
- # Finalize the run with success/failure, timing, and meta from the response.
141
- #
142
- # @api private
143
- # @param run [MatViews::MatViewRun]
144
- # @param response [MatViews::ServiceResponse]
145
- # @param duration_ms [Integer]
146
- # @return [void]
147
- #
148
- def finalize_run!(run, response, duration_ms)
149
- base_attrs = {
150
- finished_at: Time.current,
151
- duration_ms: duration_ms,
152
- meta: response.payload || {}
153
- }
154
-
155
- if response.success?
156
- run.update!(base_attrs.merge(status: :success, error: nil))
157
- else
158
- run.update!(base_attrs.merge(status: :failed, error: response.error.to_s.presence))
159
- end
160
- end
161
-
162
- ##
163
- # Mark the run failed due to an exception.
164
- #
165
- # @api private
166
- # @param run [MatViews::MatViewRun]
167
- # @param exception [Exception]
168
- # @return [void]
169
- #
170
- def fail_run!(run, exception)
171
- run.update!(
172
- finished_at: Time.current,
173
- duration_ms: run.duration_ms || 0,
174
- error: "#{exception.class}: #{exception.message}",
175
- status: :failed
176
- )
177
- end
178
-
179
- ##
180
- # Monotonic clock getter (for elapsed-time measurement).
181
- #
182
- # @api private
183
- # @return [Float] seconds
184
- #
185
- def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
186
-
187
- ##
188
- # Convert monotonic start time to elapsed milliseconds.
189
- #
190
- # @api private
191
- # @param start [Float]
192
- # @return [Integer] elapsed ms
193
- #
194
- def elapsed_ms(start) = ((monotime - start) * 1000).round
195
88
  end
196
89
  end
@@ -39,7 +39,7 @@ module MatViews
39
39
  # @example Enqueue using keyword-hash form
40
40
  # MatViews::RefreshViewJob.perform_later(definition.id, row_count_strategy: :estimated)
41
41
  #
42
- class RefreshViewJob < ::ActiveJob::Base
42
+ class RefreshViewJob < ApplicationJob
43
43
  ##
44
44
  # Queue name for the job.
45
45
  #
@@ -48,16 +48,12 @@ module MatViews
48
48
  queue_as { MatViews.configuration.job_queue || :default }
49
49
 
50
50
  ##
51
- # Perform the job for the given materialized view definition.
52
- #
53
- # Accepts either a symbol/string (`:estimated`, `:exact`) or a hash
54
- # (`{ row_count_strategy: :exact }`) for `strategy_arg`.
51
+ # Perform the refresh job for the given materialized view definition.
55
52
  #
56
53
  # @api public
57
54
  #
58
55
  # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
59
- # @param strategy_arg [Symbol, String, Hash, nil] Row count strategy override.
60
- # When a Hash, looks for `:row_count_strategy` / `"row_count_strategy"`.
56
+ # @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
61
57
  #
62
58
  # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
63
59
  # - `:status` [Symbol]
@@ -68,54 +64,14 @@ module MatViews
68
64
  #
69
65
  # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
70
66
  #
71
- def perform(definition_id, strategy_arg = nil)
72
- row_count_strategy = normalize_strategy(strategy_arg)
67
+ def perform(definition_id, row_count_strategy_arg = nil)
73
68
  definition = MatViews::MatViewDefinition.find(definition_id)
74
- run = start_run(definition)
75
-
76
- response, duration_ms = execute(definition, row_count_strategy: row_count_strategy)
77
- finalize_run!(run, response, duration_ms)
78
- response.to_h
79
- rescue StandardError => e
80
- fail_run!(run, e) if run
81
- raise e
82
- end
83
-
84
- private
85
-
86
- ##
87
- # Normalize the strategy argument into a symbol or default.
88
- #
89
- # @api private
90
- #
91
- # @param arg [Symbol, String, Hash, nil]
92
- # @return [Symbol] One of `:estimated`, `:exact`, or `:estimated` by default.
93
- #
94
- def normalize_strategy(arg)
95
- case arg
96
- when Hash
97
- (arg[:row_count_strategy] || arg['row_count_strategy'] || :estimated).to_sym
98
- when String, Symbol
99
- arg.to_sym
100
- else
101
- :estimated
69
+ record_run(definition, :refresh) do
70
+ service(definition).new(definition, row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
102
71
  end
103
72
  end
104
73
 
105
- ##
106
- # Execute the appropriate refresh service and measure duration.
107
- #
108
- # @api private
109
- #
110
- # @param definition [MatViews::MatViewDefinition]
111
- # @param row_count_strategy [Symbol, nil]
112
- # @return [Array(MatViews::ServiceResponse, Integer)] response and elapsed ms.
113
- #
114
- def execute(definition, row_count_strategy:)
115
- started = monotime
116
- response = service(definition).new(definition, row_count_strategy: row_count_strategy).run
117
- [response, elapsed_ms(started)]
118
- end
74
+ private
119
75
 
120
76
  ##
121
77
  # Select the refresh service class based on the definition's strategy.
@@ -135,81 +91,5 @@ module MatViews
135
91
  MatViews::Services::RegularRefresh
136
92
  end
137
93
  end
138
-
139
- ##
140
- # Begin a {MatViews::MatViewRun} row for lifecycle tracking.
141
- #
142
- # @api private
143
- #
144
- # @param definition [MatViews::MatViewDefinition]
145
- # @return [MatViews::MatViewRun]
146
- #
147
- def start_run(definition)
148
- MatViews::MatViewRun.create!(
149
- mat_view_definition: definition,
150
- status: :running,
151
- started_at: Time.current,
152
- operation: :refresh
153
- )
154
- end
155
-
156
- ##
157
- # Finalize the run with success/failure, timing, and meta from the response.
158
- #
159
- # @api private
160
- #
161
- # @param run [MatViews::MatViewRun]
162
- # @param response [MatViews::ServiceResponse]
163
- # @param duration_ms [Integer]
164
- # @return [void]
165
- #
166
- def finalize_run!(run, response, duration_ms)
167
- base_attrs = {
168
- finished_at: Time.current,
169
- duration_ms: duration_ms,
170
- meta: response.payload || {}
171
- }
172
-
173
- if response.success?
174
- run.update!(base_attrs.merge(status: :success, error: nil))
175
- else
176
- run.update!(base_attrs.merge(status: :failed, error: response.error.to_s.presence))
177
- end
178
- end
179
-
180
- ##
181
- # Mark the run failed due to an exception.
182
- #
183
- # @api private
184
- #
185
- # @param run [MatViews::MatViewRun]
186
- # @param exception [Exception]
187
- # @return [void]
188
- #
189
- def fail_run!(run, exception)
190
- run.update!(
191
- finished_at: Time.current,
192
- duration_ms: run.duration_ms || 0,
193
- error: "#{exception.class}: #{exception.message}",
194
- status: :failed
195
- )
196
- end
197
-
198
- ##
199
- # Monotonic clock getter (for elapsed-time measurement).
200
- #
201
- # @api private
202
- # @return [Float] seconds
203
- #
204
- def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
205
-
206
- ##
207
- # Convert monotonic start time to elapsed milliseconds.
208
- #
209
- # @api private
210
- # @param start [Float]
211
- # @return [Integer] elapsed ms
212
- #
213
- def elapsed_ms(start) = ((monotime - start) * 1000).round
214
94
  end
215
95
  end
@@ -77,13 +77,33 @@ module MatViews
77
77
  validates :status, presence: true
78
78
 
79
79
  ##
80
- # Scopes
80
+ # Scope create runs
81
+ # All runs with `operation: :create`.
82
+ # @return [ActiveRecord::Relation<MatViews::MatViewRun>]
81
83
  scope :create_runs, -> { where(operation: :create) }
84
+
85
+ ##
86
+ # Scope refresh runs
87
+ # All runs with `operation: :refresh`.
88
+ # @return [ActiveRecord::Relation<MatViews::MatViewRun>]
82
89
  scope :refresh_runs, -> { where(operation: :refresh) }
90
+
91
+ ##
92
+ # Scope drop runs
93
+ # All runs with `operation: :drop`.
94
+ # @return [ActiveRecord::Relation<MatViews::MatViewRun>]
83
95
  scope :drop_runs, -> { where(operation: :drop) }
84
96
 
85
- def row_count
86
- meta['row_count']
97
+ # row count before the operation, if applicable
98
+ # @return [Integer, nil]
99
+ def row_count_before
100
+ meta.dig('response', 'row_count_before')
101
+ end
102
+
103
+ # row count after the operation, if applicable
104
+ # @return [Integer, nil]
105
+ def row_count_after
106
+ meta.dig('response', 'row_count_after')
87
107
  end
88
108
  end
89
109
  end