iknow_view_models 2.8.4
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/.circleci/config.yml +115 -0
- data/.gitignore +36 -0
- data/.travis.yml +31 -0
- data/Appraisals +9 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +19 -0
- data/Rakefile +21 -0
- data/appveyor.yml +22 -0
- data/gemfiles/rails_5_2.gemfile +15 -0
- data/gemfiles/rails_6_0_beta.gemfile +15 -0
- data/iknow_view_models.gemspec +49 -0
- data/lib/iknow_view_models.rb +12 -0
- data/lib/iknow_view_models/railtie.rb +8 -0
- data/lib/iknow_view_models/version.rb +5 -0
- data/lib/view_model.rb +333 -0
- data/lib/view_model/access_control.rb +154 -0
- data/lib/view_model/access_control/composed.rb +216 -0
- data/lib/view_model/access_control/open.rb +13 -0
- data/lib/view_model/access_control/read_only.rb +13 -0
- data/lib/view_model/access_control/tree.rb +264 -0
- data/lib/view_model/access_control_error.rb +10 -0
- data/lib/view_model/active_record.rb +383 -0
- data/lib/view_model/active_record/association_data.rb +178 -0
- data/lib/view_model/active_record/association_manipulation.rb +389 -0
- data/lib/view_model/active_record/cache.rb +265 -0
- data/lib/view_model/active_record/cache/cacheable_view.rb +51 -0
- data/lib/view_model/active_record/cloner.rb +113 -0
- data/lib/view_model/active_record/collection_nested_controller.rb +100 -0
- data/lib/view_model/active_record/controller.rb +77 -0
- data/lib/view_model/active_record/controller_base.rb +185 -0
- data/lib/view_model/active_record/nested_controller_base.rb +93 -0
- data/lib/view_model/active_record/singular_nested_controller.rb +34 -0
- data/lib/view_model/active_record/update_context.rb +252 -0
- data/lib/view_model/active_record/update_data.rb +749 -0
- data/lib/view_model/active_record/update_operation.rb +810 -0
- data/lib/view_model/active_record/visitor.rb +77 -0
- data/lib/view_model/after_transaction_runner.rb +29 -0
- data/lib/view_model/callbacks.rb +219 -0
- data/lib/view_model/changes.rb +62 -0
- data/lib/view_model/config.rb +29 -0
- data/lib/view_model/controller.rb +142 -0
- data/lib/view_model/deserialization_error.rb +437 -0
- data/lib/view_model/deserialize_context.rb +16 -0
- data/lib/view_model/error.rb +191 -0
- data/lib/view_model/error_view.rb +35 -0
- data/lib/view_model/record.rb +367 -0
- data/lib/view_model/record/attribute_data.rb +48 -0
- data/lib/view_model/reference.rb +31 -0
- data/lib/view_model/references.rb +48 -0
- data/lib/view_model/registry.rb +73 -0
- data/lib/view_model/schemas.rb +45 -0
- data/lib/view_model/serialization_error.rb +10 -0
- data/lib/view_model/serialize_context.rb +118 -0
- data/lib/view_model/test_helpers.rb +103 -0
- data/lib/view_model/test_helpers/arvm_builder.rb +111 -0
- data/lib/view_model/traversal_context.rb +126 -0
- data/lib/view_model/utils.rb +24 -0
- data/lib/view_model/utils/collections.rb +49 -0
- data/test/helpers/arvm_test_models.rb +59 -0
- data/test/helpers/arvm_test_utilities.rb +187 -0
- data/test/helpers/callback_tracer.rb +27 -0
- data/test/helpers/controller_test_helpers.rb +270 -0
- data/test/helpers/match_enumerator.rb +58 -0
- data/test/helpers/query_logging.rb +71 -0
- data/test/helpers/test_access_control.rb +56 -0
- data/test/helpers/viewmodel_spec_helpers.rb +326 -0
- data/test/unit/view_model/access_control_test.rb +769 -0
- data/test/unit/view_model/active_record/alias_test.rb +35 -0
- data/test/unit/view_model/active_record/belongs_to_test.rb +376 -0
- data/test/unit/view_model/active_record/cache_test.rb +351 -0
- data/test/unit/view_model/active_record/cloner_test.rb +313 -0
- data/test/unit/view_model/active_record/controller_test.rb +561 -0
- data/test/unit/view_model/active_record/counter_test.rb +80 -0
- data/test/unit/view_model/active_record/customization_test.rb +388 -0
- data/test/unit/view_model/active_record/has_many_test.rb +957 -0
- data/test/unit/view_model/active_record/has_many_through_poly_test.rb +269 -0
- data/test/unit/view_model/active_record/has_many_through_test.rb +736 -0
- data/test/unit/view_model/active_record/has_one_test.rb +334 -0
- data/test/unit/view_model/active_record/namespacing_test.rb +75 -0
- data/test/unit/view_model/active_record/optional_attribute_view_test.rb +58 -0
- data/test/unit/view_model/active_record/poly_test.rb +320 -0
- data/test/unit/view_model/active_record/shared_test.rb +285 -0
- data/test/unit/view_model/active_record/version_test.rb +121 -0
- data/test/unit/view_model/active_record_test.rb +542 -0
- data/test/unit/view_model/callbacks_test.rb +582 -0
- data/test/unit/view_model/deserialization_error/unique_violation_test.rb +73 -0
- data/test/unit/view_model/record_test.rb +524 -0
- data/test/unit/view_model/traversal_context_test.rb +371 -0
- data/test/unit/view_model_test.rb +62 -0
- metadata +490 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CallbackTracer
|
|
4
|
+
include ViewModel::Callbacks
|
|
5
|
+
|
|
6
|
+
Visit = Struct.new(:hook, :view) do
|
|
7
|
+
def inspect
|
|
8
|
+
"#{hook.name}(#{view.to_reference})"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :hook_trace
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@hook_trace = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ViewModel::Callbacks::Hook.each do |hook|
|
|
19
|
+
send(hook.dsl_add_hook_name) do
|
|
20
|
+
hook_trace << Visit.new(hook, view)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def log!
|
|
25
|
+
puts hook_trace.map { |t| [t.hook.name, t.view.class, t.view.model].inspect }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
require "iknow_view_models"
|
|
2
|
+
require "view_model/active_record"
|
|
3
|
+
require "view_model/active_record/controller"
|
|
4
|
+
|
|
5
|
+
require_relative "../helpers/arvm_test_utilities.rb"
|
|
6
|
+
require_relative "../helpers/arvm_test_models.rb"
|
|
7
|
+
|
|
8
|
+
require "acts_as_manual_list"
|
|
9
|
+
|
|
10
|
+
# models for ARVM controller test
|
|
11
|
+
module ControllerTestModels
|
|
12
|
+
def before_all
|
|
13
|
+
super
|
|
14
|
+
|
|
15
|
+
build_viewmodel(:Label) do
|
|
16
|
+
define_schema do |t|
|
|
17
|
+
t.string :text
|
|
18
|
+
end
|
|
19
|
+
define_model do
|
|
20
|
+
has_one :parent
|
|
21
|
+
has_one :target
|
|
22
|
+
end
|
|
23
|
+
define_viewmodel do
|
|
24
|
+
attributes :text
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
build_viewmodel(:Category) do
|
|
29
|
+
define_schema do |t|
|
|
30
|
+
t.string :name
|
|
31
|
+
end
|
|
32
|
+
define_model do
|
|
33
|
+
has_many :parents
|
|
34
|
+
end
|
|
35
|
+
define_viewmodel do
|
|
36
|
+
attributes :name
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
build_viewmodel(:PolyOne) do
|
|
41
|
+
define_schema do |t|
|
|
42
|
+
t.integer :number
|
|
43
|
+
end
|
|
44
|
+
define_model do
|
|
45
|
+
has_one :parent, as: :poly
|
|
46
|
+
end
|
|
47
|
+
define_viewmodel do
|
|
48
|
+
attributes :number
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
build_viewmodel(:PolyTwo) do
|
|
53
|
+
define_schema do |t|
|
|
54
|
+
t.string :text
|
|
55
|
+
end
|
|
56
|
+
define_model do
|
|
57
|
+
has_one :parent, as: :poly
|
|
58
|
+
end
|
|
59
|
+
define_viewmodel do
|
|
60
|
+
attributes :text
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
build_viewmodel(:Parent) do
|
|
65
|
+
define_schema do |t|
|
|
66
|
+
t.string :name
|
|
67
|
+
t.references :label, foreign_key: true
|
|
68
|
+
t.string :poly_type
|
|
69
|
+
t.integer :poly_id
|
|
70
|
+
t.references :category, foreign_key: true # shared reference
|
|
71
|
+
end
|
|
72
|
+
define_model do
|
|
73
|
+
has_many :children, dependent: :destroy, inverse_of: :parent
|
|
74
|
+
belongs_to :label, dependent: :destroy
|
|
75
|
+
has_one :target, dependent: :destroy, inverse_of: :parent
|
|
76
|
+
belongs_to :poly, polymorphic: true, dependent: :destroy, inverse_of: :parent
|
|
77
|
+
belongs_to :category
|
|
78
|
+
end
|
|
79
|
+
define_viewmodel do
|
|
80
|
+
attributes :name
|
|
81
|
+
associations :label, :target
|
|
82
|
+
association :children, optional: true
|
|
83
|
+
association :poly, viewmodels: [:PolyOne, :PolyTwo]
|
|
84
|
+
association :category, shared: true
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
build_viewmodel(:Child) do
|
|
89
|
+
define_schema do |t|
|
|
90
|
+
t.references :parent, null: false, foreign_key: true
|
|
91
|
+
t.string :name
|
|
92
|
+
t.float :position
|
|
93
|
+
end
|
|
94
|
+
# Add age column separately in order to define CHECK constraint (no way to
|
|
95
|
+
# specify in activerecord schema.
|
|
96
|
+
ActiveRecord::Base.connection.execute(<<-SQL)
|
|
97
|
+
ALTER TABLE children ADD COLUMN age integer CHECK(age > 21)
|
|
98
|
+
SQL
|
|
99
|
+
define_model do
|
|
100
|
+
belongs_to :parent, inverse_of: :children
|
|
101
|
+
acts_as_manual_list scope: :parent
|
|
102
|
+
validates :age, numericality: {less_than: 42}, allow_nil: true
|
|
103
|
+
end
|
|
104
|
+
define_viewmodel do
|
|
105
|
+
attributes :name, :age
|
|
106
|
+
acts_as_list :position
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
build_viewmodel(:Target) do
|
|
111
|
+
define_schema do |t|
|
|
112
|
+
t.string :text
|
|
113
|
+
t.references :parent, foreign_key: true
|
|
114
|
+
t.references :label, foreign_key: true
|
|
115
|
+
end
|
|
116
|
+
define_model do
|
|
117
|
+
belongs_to :parent, inverse_of: :target
|
|
118
|
+
belongs_to :label, dependent: :destroy
|
|
119
|
+
end
|
|
120
|
+
define_viewmodel do
|
|
121
|
+
attributes :text
|
|
122
|
+
association :label
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
## Dummy Rails Controllers
|
|
129
|
+
class DummyController
|
|
130
|
+
attr_reader :params, :status
|
|
131
|
+
|
|
132
|
+
def initialize(**params)
|
|
133
|
+
# in Rails 5, this will not be a hash, which weakens the value of the test.
|
|
134
|
+
@params = params.with_indifferent_access
|
|
135
|
+
@status = 200
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def invoke(method)
|
|
139
|
+
begin
|
|
140
|
+
self.public_send(method)
|
|
141
|
+
rescue Exception => ex
|
|
142
|
+
handler = self.class.rescue_block(ex.class)
|
|
143
|
+
case handler
|
|
144
|
+
when nil
|
|
145
|
+
raise
|
|
146
|
+
when Symbol
|
|
147
|
+
self.send(handler, ex)
|
|
148
|
+
when Proc
|
|
149
|
+
self.instance_exec(ex, &handler)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def invoke_without_rescue(method)
|
|
155
|
+
self.public_send(method)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def render(status:, **options)
|
|
159
|
+
if options.has_key?(:json)
|
|
160
|
+
@response_body = options[:json]
|
|
161
|
+
@content_type = options[:content_type] || 'application/json'
|
|
162
|
+
elsif options.has_key?(:plain)
|
|
163
|
+
@response_body = options[:plain]
|
|
164
|
+
@content_type = options[:content_type] || 'text/plain'
|
|
165
|
+
end
|
|
166
|
+
@status = status unless status.nil?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def json_response
|
|
170
|
+
raise "Not a JSON response" unless @content_type == 'application/json'
|
|
171
|
+
@response_body
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def hash_response
|
|
175
|
+
JSON.parse(json_response)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
class << self
|
|
179
|
+
def inherited(subclass)
|
|
180
|
+
subclass.initialize_rescue_blocks
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def initialize_rescue_blocks
|
|
184
|
+
@rescue_blocks = {}
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def rescue_from(type, with:)
|
|
188
|
+
@rescue_blocks[type] = with
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def rescue_block(type)
|
|
192
|
+
@rescue_blocks.to_a.reverse.detect { |btype, h| type <= btype }.try(&:last)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def etag(*)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Provide dummy Rails env
|
|
201
|
+
module Rails
|
|
202
|
+
def self.env
|
|
203
|
+
'production'
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
module ActionController
|
|
208
|
+
class Parameters
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
module CallbackTracing
|
|
213
|
+
attr_reader :callback_tracer
|
|
214
|
+
delegate :hook_trace, to: :callback_tracer
|
|
215
|
+
|
|
216
|
+
def new_deserialize_context(**args)
|
|
217
|
+
@callback_tracer ||= CallbackTracer.new
|
|
218
|
+
super(callbacks: [@callback_tracer])
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def new_serialize_context(**args)
|
|
222
|
+
@callback_tracer ||= CallbackTracer.new
|
|
223
|
+
super(callbacks: [@callback_tracer])
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
module ControllerTestControllers
|
|
228
|
+
def before_all
|
|
229
|
+
super
|
|
230
|
+
|
|
231
|
+
Class.new(DummyController) do |c|
|
|
232
|
+
Object.const_set(:ParentController, self)
|
|
233
|
+
include ViewModel::ActiveRecord::Controller
|
|
234
|
+
include CallbackTracing
|
|
235
|
+
self.access_control = ViewModel::AccessControl::Open
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
Class.new(DummyController) do |c|
|
|
239
|
+
Object.const_set(:ChildController, self)
|
|
240
|
+
include ViewModel::ActiveRecord::Controller
|
|
241
|
+
include CallbackTracing
|
|
242
|
+
self.access_control = ViewModel::AccessControl::Open
|
|
243
|
+
nested_in :parent, as: :children
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
Class.new(DummyController) do |c|
|
|
247
|
+
Object.const_set(:LabelController, self)
|
|
248
|
+
include ViewModel::ActiveRecord::Controller
|
|
249
|
+
include CallbackTracing
|
|
250
|
+
self.access_control = ViewModel::AccessControl::Open
|
|
251
|
+
nested_in :parent, as: :label
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
Class.new(DummyController) do |c|
|
|
255
|
+
Object.const_set(:TargetController, self)
|
|
256
|
+
include ViewModel::ActiveRecord::Controller
|
|
257
|
+
include CallbackTracing
|
|
258
|
+
self.access_control = ViewModel::AccessControl::Open
|
|
259
|
+
nested_in :parent, as: :target
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def after_all
|
|
264
|
+
[:ParentController, :ChildController, :LabelController, :TargetController].each do |name|
|
|
265
|
+
Object.send(:remove_const, name)
|
|
266
|
+
end
|
|
267
|
+
ActiveSupport::Dependencies::Reference.clear!
|
|
268
|
+
super
|
|
269
|
+
end
|
|
270
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# From https://stackoverflow.com/a/41293357
|
|
4
|
+
module MiniTest::Assertions
|
|
5
|
+
class MatchEnumerator
|
|
6
|
+
def initialize(expected, actual)
|
|
7
|
+
@expected = expected
|
|
8
|
+
@actual = actual
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def match
|
|
12
|
+
return result, message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def result
|
|
16
|
+
return false unless @actual.respond_to? :to_a
|
|
17
|
+
@extra_items = difference_between_enumerators(@actual, @expected)
|
|
18
|
+
@missing_items = difference_between_enumerators(@expected, @actual)
|
|
19
|
+
@extra_items.empty? & @missing_items.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def message
|
|
23
|
+
if @actual.respond_to? :to_a
|
|
24
|
+
message = "expected collection contained: #{safe_sort(@expected).inspect}\n"
|
|
25
|
+
message += "actual collection contained: #{safe_sort(@actual).inspect}\n"
|
|
26
|
+
message += "the missing elements were: #{safe_sort(@missing_items).inspect}\n" unless @missing_items.empty?
|
|
27
|
+
message += "the extra elements were: #{safe_sort(@extra_items).inspect}\n" unless @extra_items.empty?
|
|
28
|
+
else
|
|
29
|
+
message = "expected an array, actual collection was #{@actual.inspect}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def safe_sort(array)
|
|
38
|
+
array.sort rescue array
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def difference_between_enumerators(array_1, array_2)
|
|
42
|
+
difference = array_1.to_a.dup
|
|
43
|
+
array_2.to_a.each do |element|
|
|
44
|
+
if (index = difference.index(element))
|
|
45
|
+
difference.delete_at(index)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
difference
|
|
49
|
+
end
|
|
50
|
+
end # MatchEnumerator
|
|
51
|
+
|
|
52
|
+
def assert_match_enumerator(expected, actual)
|
|
53
|
+
result, message = MatchEnumerator.new(expected, actual).match
|
|
54
|
+
assert result, message
|
|
55
|
+
end
|
|
56
|
+
end # MiniTest::Assertions
|
|
57
|
+
|
|
58
|
+
Enumerator.infect_an_assertion :assert_match_enumerator, :must_contain_exactly
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Test mixin that allows queries executed in a block to be introspected.
|
|
2
|
+
#
|
|
3
|
+
# Code run within a `log_queries` block will collect data. Collected data is
|
|
4
|
+
# inspected via `logged_queries` which returns everything, or via
|
|
5
|
+
# `logged_named_queries`, which returns only valid payload names.
|
|
6
|
+
#
|
|
7
|
+
# Caveats: only supports single threaded testing.
|
|
8
|
+
|
|
9
|
+
require 'active_support/subscriber'
|
|
10
|
+
|
|
11
|
+
module QueryLogging
|
|
12
|
+
|
|
13
|
+
# ActiveRecord integration
|
|
14
|
+
class QueryLogger < ActiveSupport::Subscriber
|
|
15
|
+
@log = false
|
|
16
|
+
@query_log = []
|
|
17
|
+
|
|
18
|
+
attach_to :active_record
|
|
19
|
+
|
|
20
|
+
def self.clear!
|
|
21
|
+
@query_log = []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.with_query_log
|
|
25
|
+
clear!
|
|
26
|
+
@log = true
|
|
27
|
+
yield
|
|
28
|
+
ensure
|
|
29
|
+
@log = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.log?
|
|
33
|
+
@log
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.logged_events
|
|
37
|
+
@query_log
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# All public methods are event handlers. The instance defines what to log,
|
|
41
|
+
# while the class defines how to handle it.
|
|
42
|
+
|
|
43
|
+
def sql(event)
|
|
44
|
+
if self.class.log?
|
|
45
|
+
self.class.logged_events << event
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Defensively clean up before every test.
|
|
51
|
+
def setup
|
|
52
|
+
super
|
|
53
|
+
QueryLogger.clear!
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Test helpers
|
|
57
|
+
|
|
58
|
+
def log_queries
|
|
59
|
+
QueryLogger.with_query_log { yield }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def logged_queries
|
|
63
|
+
QueryLogger.logged_events
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def logged_load_queries
|
|
67
|
+
QueryLogger.logged_events
|
|
68
|
+
.map { |x| x.payload[:name] }
|
|
69
|
+
.select { |x| x && x =~ / Load$/ }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "iknow_view_models"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TestAccessControl < ViewModel::AccessControl
|
|
5
|
+
attr_accessor :editable_checks, :visible_checks
|
|
6
|
+
|
|
7
|
+
def initialize(can_view, can_edit, can_change)
|
|
8
|
+
super()
|
|
9
|
+
@can_edit = can_edit
|
|
10
|
+
@can_view = can_view
|
|
11
|
+
@can_change = can_change
|
|
12
|
+
@editable_checks = []
|
|
13
|
+
@visible_checks = []
|
|
14
|
+
@valid_edit_checks = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Collect
|
|
18
|
+
|
|
19
|
+
def editable_check(traversal_env)
|
|
20
|
+
@editable_checks << traversal_env.view.to_reference
|
|
21
|
+
ViewModel::AccessControl::Result.new(@can_edit)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def valid_edit_check(traversal_env)
|
|
25
|
+
ref = traversal_env.view.to_reference
|
|
26
|
+
@valid_edit_checks << [ref, traversal_env.changes]
|
|
27
|
+
ViewModel::AccessControl::Result.new(@can_change)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def visible_check(traversal_env)
|
|
31
|
+
@visible_checks << traversal_env.view.to_reference
|
|
32
|
+
ViewModel::AccessControl::Result.new(@can_view)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Query (also see attr_accessors)
|
|
36
|
+
|
|
37
|
+
def valid_edit_refs
|
|
38
|
+
@valid_edit_checks.map { |ref, _changes| ref }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def valid_edit_changes(ref)
|
|
42
|
+
all = all_valid_edit_changes(ref)
|
|
43
|
+
raise "Expected single change for ref '#{ref}'; found #{all}" unless all.size == 1
|
|
44
|
+
all.first
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def all_valid_edit_changes(ref)
|
|
48
|
+
@valid_edit_checks
|
|
49
|
+
.select { | cref, _changes| cref == ref }
|
|
50
|
+
.map { |_cref, changes| changes }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def was_edited?(ref)
|
|
54
|
+
all_valid_edit_changes(ref).present?
|
|
55
|
+
end
|
|
56
|
+
end
|