iknow_view_models 3.2.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +13 -0
- data/Appraisals +6 -6
- data/Rakefile +5 -5
- data/gemfiles/rails_5_2.gemfile +5 -5
- data/gemfiles/rails_6_0.gemfile +5 -5
- data/iknow_view_models.gemspec +40 -39
- data/lib/iknow_view_models.rb +9 -7
- data/lib/iknow_view_models/version.rb +1 -1
- data/lib/view_model.rb +17 -14
- data/lib/view_model/access_control.rb +5 -2
- data/lib/view_model/access_control/composed.rb +10 -9
- data/lib/view_model/access_control/open.rb +2 -0
- data/lib/view_model/access_control/read_only.rb +2 -0
- data/lib/view_model/access_control/tree.rb +11 -6
- data/lib/view_model/access_control_error.rb +4 -1
- data/lib/view_model/active_record.rb +12 -11
- data/lib/view_model/active_record/association_data.rb +2 -1
- data/lib/view_model/active_record/association_manipulation.rb +6 -4
- data/lib/view_model/active_record/cache.rb +4 -2
- data/lib/view_model/active_record/collection_nested_controller.rb +3 -3
- data/lib/view_model/active_record/controller_base.rb +4 -1
- data/lib/view_model/active_record/nested_controller_base.rb +1 -0
- data/lib/view_model/active_record/update_context.rb +8 -6
- data/lib/view_model/active_record/update_data.rb +32 -30
- data/lib/view_model/active_record/update_operation.rb +17 -13
- data/lib/view_model/active_record/visitor.rb +0 -1
- data/lib/view_model/after_transaction_runner.rb +0 -1
- data/lib/view_model/callbacks.rb +3 -1
- data/lib/view_model/controller.rb +13 -3
- data/lib/view_model/deserialization_error.rb +15 -12
- data/lib/view_model/error.rb +12 -10
- data/lib/view_model/error_view.rb +3 -1
- data/lib/view_model/migration/no_path_error.rb +1 -0
- data/lib/view_model/migration/one_way_error.rb +1 -0
- data/lib/view_model/migration/unspecified_version_error.rb +1 -0
- data/lib/view_model/record.rb +11 -13
- data/lib/view_model/reference.rb +3 -1
- data/lib/view_model/references.rb +8 -5
- data/lib/view_model/registry.rb +1 -1
- data/lib/view_model/schemas.rb +9 -4
- data/lib/view_model/serialization_error.rb +4 -1
- data/lib/view_model/serialize_context.rb +4 -4
- data/lib/view_model/test_helpers.rb +8 -3
- data/lib/view_model/test_helpers/arvm_builder.rb +19 -14
- data/lib/view_model/traversal_context.rb +2 -1
- data/test/.rubocop.yml +14 -0
- data/test/helpers/arvm_test_models.rb +12 -9
- data/test/helpers/arvm_test_utilities.rb +5 -3
- data/test/helpers/controller_test_helpers.rb +31 -29
- data/test/helpers/match_enumerator.rb +1 -0
- data/test/helpers/query_logging.rb +2 -1
- data/test/helpers/test_access_control.rb +5 -3
- data/test/helpers/viewmodel_spec_helpers.rb +21 -20
- data/test/unit/view_model/access_control_test.rb +144 -144
- data/test/unit/view_model/active_record/alias_test.rb +15 -13
- data/test/unit/view_model/active_record/belongs_to_test.rb +40 -39
- data/test/unit/view_model/active_record/cache_test.rb +27 -26
- data/test/unit/view_model/active_record/cloner_test.rb +67 -63
- data/test/unit/view_model/active_record/controller_test.rb +37 -38
- data/test/unit/view_model/active_record/counter_test.rb +10 -9
- data/test/unit/view_model/active_record/customization_test.rb +59 -58
- data/test/unit/view_model/active_record/has_many_test.rb +112 -111
- data/test/unit/view_model/active_record/has_many_through_poly_test.rb +15 -14
- data/test/unit/view_model/active_record/has_many_through_test.rb +33 -38
- data/test/unit/view_model/active_record/has_one_test.rb +37 -36
- data/test/unit/view_model/active_record/migration_test.rb +13 -13
- data/test/unit/view_model/active_record/namespacing_test.rb +19 -17
- data/test/unit/view_model/active_record/poly_test.rb +44 -45
- data/test/unit/view_model/active_record/shared_test.rb +30 -28
- data/test/unit/view_model/active_record/version_test.rb +9 -7
- data/test/unit/view_model/active_record_test.rb +72 -72
- data/test/unit/view_model/callbacks_test.rb +19 -15
- data/test/unit/view_model/controller_test.rb +4 -2
- data/test/unit/view_model/record_test.rb +92 -97
- data/test/unit/view_model/traversal_context_test.rb +4 -5
- data/test/unit/view_model_test.rb +18 -16
- metadata +7 -5
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ViewModel
|
2
4
|
# A bucket for configuration, used for serializing and deserializing.
|
3
5
|
class References
|
@@ -17,14 +19,15 @@ class ViewModel
|
|
17
19
|
# under which the data is stored. If the data is not present, will compute
|
18
20
|
# it by calling the given block.
|
19
21
|
def add_reference(value)
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
ref = @ref_by_value[value]
|
23
|
+
|
24
|
+
unless ref.present?
|
23
25
|
ref = new_ref!(value)
|
24
26
|
@ref_by_value[value] = ref
|
25
27
|
@value_by_ref[ref] = value
|
26
|
-
ref
|
27
28
|
end
|
29
|
+
|
30
|
+
ref
|
28
31
|
end
|
29
32
|
|
30
33
|
def clear!
|
@@ -41,7 +44,7 @@ class ViewModel
|
|
41
44
|
hash = Digest::SHA256.base64digest("#{vm_ref.viewmodel_class.name}.#{vm_ref.model_id}")
|
42
45
|
"ref:h:#{hash}"
|
43
46
|
else
|
44
|
-
'ref:i:%
|
47
|
+
format('ref:i:%06<count>d', count: (@last_ref += 1))
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
data/lib/view_model/registry.rb
CHANGED
data/lib/view_model/schemas.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'json_schema'
|
3
5
|
|
@@ -9,7 +11,8 @@ class ViewModel::Schemas
|
|
9
11
|
|
10
12
|
ID_SCHEMA =
|
11
13
|
{ 'oneOf' => [{ 'type' => 'integer' },
|
12
|
-
{ 'type' => 'string', 'format' => 'uuid' }] }
|
14
|
+
{ 'type' => 'string', 'format' => 'uuid' },] }.freeze
|
15
|
+
|
13
16
|
ID = JsonSchema.parse!(ID_SCHEMA)
|
14
17
|
|
15
18
|
VIEWMODEL_UPDATE_SCHEMA =
|
@@ -20,8 +23,9 @@ class ViewModel::Schemas
|
|
20
23
|
ViewModel::ID_ATTRIBUTE => ID_SCHEMA,
|
21
24
|
ViewModel::NEW_ATTRIBUTE => { 'type' => 'boolean' },
|
22
25
|
ViewModel::VERSION_ATTRIBUTE => { 'type' => 'integer' } },
|
23
|
-
'required' => [ViewModel::TYPE_ATTRIBUTE]
|
24
|
-
}
|
26
|
+
'required' => [ViewModel::TYPE_ATTRIBUTE],
|
27
|
+
}.freeze
|
28
|
+
|
25
29
|
VIEWMODEL_UPDATE = JsonSchema.parse!(VIEWMODEL_UPDATE_SCHEMA)
|
26
30
|
|
27
31
|
VIEWMODEL_REFERENCE_SCHEMA =
|
@@ -31,7 +35,8 @@ class ViewModel::Schemas
|
|
31
35
|
'properties' => { ViewModel::REFERENCE_ATTRIBUTE => { 'type' => 'string' } },
|
32
36
|
'additionalProperties' => false,
|
33
37
|
'required' => [ViewModel::REFERENCE_ATTRIBUTE],
|
34
|
-
}
|
38
|
+
}.freeze
|
39
|
+
|
35
40
|
VIEWMODEL_REFERENCE = JsonSchema.parse!(VIEWMODEL_REFERENCE_SCHEMA)
|
36
41
|
|
37
42
|
def self.verify_schema!(schema, value)
|
@@ -38,10 +38,10 @@ class ViewModel::SerializeContext < ViewModel::TraversalContext
|
|
38
38
|
|
39
39
|
while references.present?
|
40
40
|
extract_referenced_views!.each do |ref, value|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
next if serialized_refs.has_key?(ref)
|
42
|
+
|
43
|
+
serialized_refs[ref] = Jbuilder.new do |j|
|
44
|
+
ViewModel.serialize(value, j, serialize_context: reference_context)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
##
|
2
4
|
# Helpers useful for writing tests for viewmodel implementations
|
3
5
|
module ViewModel::TestHelpers
|
@@ -51,11 +53,13 @@ module ViewModel::TestHelpers
|
|
51
53
|
|
52
54
|
def assert_consistent_record(viewmodel, been_there: Set.new)
|
53
55
|
return if been_there.include?(viewmodel.model)
|
56
|
+
|
54
57
|
been_there << viewmodel.model
|
55
58
|
|
56
|
-
|
59
|
+
case viewmodel
|
60
|
+
when ViewModel::ActiveRecord
|
57
61
|
assert_model_represents_database(viewmodel.model, been_there: been_there)
|
58
|
-
|
62
|
+
when ViewModel::Record
|
59
63
|
viewmodel.class._members.each do |name, attribute_data|
|
60
64
|
if attribute_data.attribute_viewmodel
|
61
65
|
assert_consistent_record(viewmodel.send(name), been_there: been_there)
|
@@ -66,6 +70,7 @@ module ViewModel::TestHelpers
|
|
66
70
|
|
67
71
|
def assert_model_represents_database(model, been_there: Set.new)
|
68
72
|
return if been_there.include?(model)
|
73
|
+
|
69
74
|
been_there << model
|
70
75
|
|
71
76
|
refute(model.new_record?, 'model represents database entity')
|
@@ -83,7 +88,7 @@ module ViewModel::TestHelpers
|
|
83
88
|
next unless association.loaded?
|
84
89
|
|
85
90
|
case
|
86
|
-
when association.target
|
91
|
+
when association.target.nil?
|
87
92
|
assert_nil(database_model.association(reflection.name).target,
|
88
93
|
'in memory nil association matches database')
|
89
94
|
when reflection.collection?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ViewModel::TestHelpers::ARVMBuilder
|
2
4
|
attr_reader :name, :model, :viewmodel, :namespace
|
3
5
|
|
@@ -46,16 +48,16 @@ class ViewModel::TestHelpers::ARVMBuilder
|
|
46
48
|
instance_eval(&block)
|
47
49
|
end
|
48
50
|
|
49
|
-
raise
|
50
|
-
raise
|
51
|
-
raise
|
51
|
+
raise 'Model not created in ARVMBuilder' unless model
|
52
|
+
raise 'Schema not created in ARVMBuilder' unless model.table_exists?
|
53
|
+
raise 'ViewModel not created in ARVMBuilder' unless viewmodel || @no_viewmodel
|
52
54
|
|
53
55
|
# Force the realization of the view model into the library's lookup
|
54
56
|
# table. If this doesn't happen the library may have conflicting entries in
|
55
57
|
# the deferred table, and will allow viewmodels to leak between tests.
|
56
58
|
unless @no_viewmodel || !(@viewmodel < ViewModel::Record)
|
57
59
|
resolved = ViewModel::Registry.for_view_name(viewmodel.view_name)
|
58
|
-
raise
|
60
|
+
raise 'Failed to register expected new class!' unless resolved == @viewmodel
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
@@ -70,7 +72,7 @@ class ViewModel::TestHelpers::ARVMBuilder
|
|
70
72
|
private
|
71
73
|
|
72
74
|
def viewmodel_name
|
73
|
-
self.name +
|
75
|
+
self.name + 'View'
|
74
76
|
end
|
75
77
|
|
76
78
|
def define_schema(&block)
|
@@ -84,10 +86,11 @@ class ViewModel::TestHelpers::ARVMBuilder
|
|
84
86
|
|
85
87
|
def define_model(&block)
|
86
88
|
model_name = name
|
87
|
-
|
88
|
-
@model = Class.new(@model_base) do |
|
89
|
-
raise "Model already defined: #{model_name}" if
|
90
|
-
|
89
|
+
model_namespace = namespace
|
90
|
+
@model = Class.new(@model_base) do |_c|
|
91
|
+
raise "Model already defined: #{model_name}" if model_namespace.const_defined?(model_name, false)
|
92
|
+
|
93
|
+
model_namespace.const_set(model_name, self)
|
91
94
|
class_eval(&block)
|
92
95
|
reset_column_information
|
93
96
|
end
|
@@ -96,13 +99,15 @@ class ViewModel::TestHelpers::ARVMBuilder
|
|
96
99
|
|
97
100
|
def define_viewmodel(&block)
|
98
101
|
vm_name = viewmodel_name
|
99
|
-
|
100
|
-
@viewmodel = Class.new(@viewmodel_base) do |
|
101
|
-
raise "Viewmodel alreay defined: #{vm_name}" if
|
102
|
-
|
102
|
+
vm_namespace = namespace
|
103
|
+
@viewmodel = Class.new(@viewmodel_base) do |_c|
|
104
|
+
raise "Viewmodel alreay defined: #{vm_name}" if vm_namespace.const_defined?(vm_name, false)
|
105
|
+
|
106
|
+
vm_namespace.const_set(vm_name, self)
|
103
107
|
class_eval(&block)
|
104
108
|
end
|
105
|
-
raise
|
109
|
+
raise 'help help' if @viewmodel.name.nil?
|
110
|
+
|
106
111
|
@viewmodel
|
107
112
|
end
|
108
113
|
|
@@ -21,6 +21,7 @@ class ViewModel::TraversalContext
|
|
21
21
|
end
|
22
22
|
|
23
23
|
attr_reader :shared_context
|
24
|
+
|
24
25
|
delegate :access_control, :callbacks, to: :shared_context
|
25
26
|
|
26
27
|
def self.new_child(*args)
|
@@ -118,7 +119,7 @@ class ViewModel::TraversalContext
|
|
118
119
|
|
119
120
|
def nearest_root_viewmodel
|
120
121
|
if root?
|
121
|
-
raise RuntimeError.new(
|
122
|
+
raise RuntimeError.new('Attempted to find nearest root from a root context. This is probably not what you wanted.')
|
122
123
|
elsif parent_context.root?
|
123
124
|
parent_viewmodel
|
124
125
|
else
|
data/test/.rubocop.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
inherit_from: ../.rubocop.yml
|
2
|
+
|
3
|
+
Lint/NestedMethodDefinition:
|
4
|
+
Enabled: false
|
5
|
+
Lint/ConstantDefinitionInBlock:
|
6
|
+
Enabled: false
|
7
|
+
Layout/MultilineBlockLayout:
|
8
|
+
Enabled: false
|
9
|
+
Layout/HashAlignment:
|
10
|
+
Enabled: false
|
11
|
+
Layout/BlockEndNewline:
|
12
|
+
Enabled: false
|
13
|
+
Style/Semicolon:
|
14
|
+
Enabled: false
|
@@ -1,19 +1,22 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "view_model/active_record"
|
5
|
-
require "view_model/active_record/controller"
|
3
|
+
require_relative 'test_access_control'
|
6
4
|
|
7
|
-
require
|
5
|
+
require 'iknow_view_models'
|
6
|
+
require 'view_model/active_record'
|
7
|
+
require 'view_model/active_record/controller'
|
8
|
+
|
9
|
+
require 'acts_as_manual_list'
|
8
10
|
|
9
11
|
db_config_path = File.join(File.dirname(__FILE__), '../config/database.yml')
|
10
|
-
db_config = YAML.
|
11
|
-
raise
|
12
|
-
|
12
|
+
db_config = YAML.safe_load(File.open(db_config_path))
|
13
|
+
raise 'Test database configuration missing' unless db_config['test']
|
14
|
+
|
15
|
+
ActiveRecord::Base.establish_connection(db_config['test'])
|
13
16
|
|
14
17
|
# Remove test tables if any exist
|
15
18
|
%w[labels parents children targets poly_ones poly_twos owners
|
16
|
-
|
19
|
+
grand_parents categories tags parents_tags].each do |t|
|
17
20
|
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{t} CASCADE")
|
18
21
|
end
|
19
22
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support'
|
2
4
|
require 'minitest/hooks'
|
3
5
|
|
@@ -10,7 +12,7 @@ unless ViewModel::Config.configured?
|
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
13
|
-
require_relative 'query_logging
|
15
|
+
require_relative 'query_logging'
|
14
16
|
|
15
17
|
ActiveSupport::TestCase.include(Minitest::Hooks)
|
16
18
|
|
@@ -91,7 +93,7 @@ module ARVMTestUtilities
|
|
91
93
|
|
92
94
|
def enable_logging!
|
93
95
|
if ENV['DEBUG']
|
94
|
-
ActiveRecord::Base.logger = Logger.new(
|
96
|
+
ActiveRecord::Base.logger = Logger.new($stderr)
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
@@ -170,7 +172,7 @@ module ARVMTestUtilities
|
|
170
172
|
{
|
171
173
|
ViewModel::ActiveRecord::TYPE_ATTRIBUTE => type::NAME,
|
172
174
|
ViewModel::ActiveRecord::VALUES_ATTRIBUTE => values,
|
173
|
-
}.merge(rest.transform_keys(&:to_s))
|
175
|
+
}.merge(rest.transform_keys(&:to_s)),
|
174
176
|
)
|
175
177
|
end
|
176
178
|
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
require "view_model/active_record"
|
3
|
-
require "view_model/active_record/controller"
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
require 'iknow_view_models'
|
4
|
+
require 'view_model/active_record'
|
5
|
+
require 'view_model/active_record/controller'
|
7
6
|
|
8
|
-
|
7
|
+
require_relative '../helpers/arvm_test_utilities'
|
8
|
+
require_relative '../helpers/arvm_test_models'
|
9
|
+
|
10
|
+
require 'acts_as_manual_list'
|
9
11
|
|
10
12
|
# models for ARVM controller test
|
11
13
|
module ControllerTestModels
|
@@ -94,7 +96,7 @@ module ControllerTestModels
|
|
94
96
|
end
|
95
97
|
end
|
96
98
|
|
97
|
-
down do |view,
|
99
|
+
down do |view, _refs|
|
98
100
|
view['old_name'] = view.delete('name')
|
99
101
|
end
|
100
102
|
end
|
@@ -152,18 +154,16 @@ class DummyController
|
|
152
154
|
end
|
153
155
|
|
154
156
|
def invoke(method)
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
self.instance_exec(ex, &handler)
|
166
|
-
end
|
157
|
+
self.public_send(method)
|
158
|
+
rescue StandardError => ex
|
159
|
+
handler = self.class.rescue_block(ex.class)
|
160
|
+
case handler
|
161
|
+
when nil
|
162
|
+
raise
|
163
|
+
when Symbol
|
164
|
+
self.send(handler, ex)
|
165
|
+
when Proc
|
166
|
+
self.instance_exec(ex, &handler)
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
@@ -183,7 +183,8 @@ class DummyController
|
|
183
183
|
end
|
184
184
|
|
185
185
|
def json_response
|
186
|
-
raise
|
186
|
+
raise 'Not a JSON response' unless @content_type == 'application/json'
|
187
|
+
|
187
188
|
@response_body
|
188
189
|
end
|
189
190
|
|
@@ -194,6 +195,7 @@ class DummyController
|
|
194
195
|
class << self
|
195
196
|
def inherited(subclass)
|
196
197
|
subclass.initialize_rescue_blocks
|
198
|
+
super
|
197
199
|
end
|
198
200
|
|
199
201
|
def initialize_rescue_blocks
|
@@ -205,11 +207,10 @@ class DummyController
|
|
205
207
|
end
|
206
208
|
|
207
209
|
def rescue_block(type)
|
208
|
-
@rescue_blocks.to_a.reverse.detect { |btype,
|
210
|
+
@rescue_blocks.to_a.reverse.detect { |btype, _h| type <= btype }.try(&:last)
|
209
211
|
end
|
210
212
|
|
211
|
-
def etag(*)
|
212
|
-
end
|
213
|
+
def etag(*); end
|
213
214
|
end
|
214
215
|
end
|
215
216
|
|
@@ -230,14 +231,15 @@ end
|
|
230
231
|
|
231
232
|
module CallbackTracing
|
232
233
|
attr_reader :callback_tracer
|
234
|
+
|
233
235
|
delegate :hook_trace, to: :callback_tracer
|
234
236
|
|
235
|
-
def new_deserialize_context(**
|
237
|
+
def new_deserialize_context(**_args)
|
236
238
|
@callback_tracer ||= CallbackTracer.new
|
237
239
|
super(callbacks: [@callback_tracer])
|
238
240
|
end
|
239
241
|
|
240
|
-
def new_serialize_context(**
|
242
|
+
def new_serialize_context(**_args)
|
241
243
|
@callback_tracer ||= CallbackTracer.new
|
242
244
|
super(callbacks: [@callback_tracer])
|
243
245
|
end
|
@@ -247,14 +249,14 @@ module ControllerTestControllers
|
|
247
249
|
def before_all
|
248
250
|
super
|
249
251
|
|
250
|
-
Class.new(DummyController) do |
|
252
|
+
Class.new(DummyController) do |_c|
|
251
253
|
Object.const_set(:ParentController, self)
|
252
254
|
include ViewModel::ActiveRecord::Controller
|
253
255
|
include CallbackTracing
|
254
256
|
self.access_control = ViewModel::AccessControl::Open
|
255
257
|
end
|
256
258
|
|
257
|
-
Class.new(DummyController) do |
|
259
|
+
Class.new(DummyController) do |_c|
|
258
260
|
Object.const_set(:ChildController, self)
|
259
261
|
include ViewModel::ActiveRecord::Controller
|
260
262
|
include CallbackTracing
|
@@ -262,7 +264,7 @@ module ControllerTestControllers
|
|
262
264
|
nested_in :parent, as: :children
|
263
265
|
end
|
264
266
|
|
265
|
-
Class.new(DummyController) do |
|
267
|
+
Class.new(DummyController) do |_c|
|
266
268
|
Object.const_set(:LabelController, self)
|
267
269
|
include ViewModel::ActiveRecord::Controller
|
268
270
|
include CallbackTracing
|
@@ -270,7 +272,7 @@ module ControllerTestControllers
|
|
270
272
|
nested_in :parent, as: :label
|
271
273
|
end
|
272
274
|
|
273
|
-
Class.new(DummyController) do |
|
275
|
+
Class.new(DummyController) do |_c|
|
274
276
|
Object.const_set(:TargetController, self)
|
275
277
|
include ViewModel::ActiveRecord::Controller
|
276
278
|
include CallbackTracing
|