iknow_view_models 3.6.0 → 3.6.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 573266179096880a34febada583570484e420b91a242920198cfa119192c3fbc
4
- data.tar.gz: c024eb9398a4b0d49103d1806379d0f20312f7c6965e9fe2751b09bb720f2fc3
3
+ metadata.gz: 0f71d69093b0d35214edcfe799aa3fc4493e0640a349e93c828e0158260d4b6c
4
+ data.tar.gz: 2c2368cd3b0bb27f37ef8e609eae6a396d4de12c4555b90c153386e8bd2a9998
5
5
  SHA512:
6
- metadata.gz: 353f42c901c4d52cba6bbe459cc6266c2e59f3a56ca432bac616cd4534c95116d0a0e27b8329799717259640e098c704c4f6b63c83713cd42cb3096c6ca6c5b3
7
- data.tar.gz: 4e74aa987e3ff26dd9b14bb3fbebc291ce86087dca8f8bcc93ef9b320d7d7ed22f150ce4f3153e27c51f6a2f8d6561fe53cdcb7ac45734caf156f50b10a543d1
6
+ metadata.gz: c27c10d2940db8b9ed7f0ec512873053f4a2d94283d11004d68b4b70153f696fe3fd45aa103f7ee02ed75fbf3c5d0d8ac899b9df769196308cf67c32dbb3af35
7
+ data.tar.gz: 882931e849905b9caaf8af154725339b7cda761b1f3ad2deb4d93c4f489cb11c8699143df1e907e51d572ec0a93c07d2f960c86f3e6c35b844d2f86c2839eb66
data/.circleci/config.yml CHANGED
@@ -8,7 +8,7 @@ executors:
8
8
  default: "2.7"
9
9
  pg-version:
10
10
  type: string
11
- default: "11"
11
+ default: "12.10"
12
12
  gemfile:
13
13
  type: string
14
14
  default: "Gemfile"
@@ -16,14 +16,14 @@ executors:
16
16
  PGHOST: 127.0.0.1
17
17
  PGUSER: eikaiwa
18
18
  docker:
19
- - image: circleci/ruby:<< parameters.ruby-version >>
19
+ - image: cimg/ruby:<< parameters.ruby-version >>
20
20
  environment:
21
21
  BUNDLE_JOBS: 3
22
22
  BUNDLE_RETRY: 3
23
23
  BUNDLE_PATH: vendor/bundle
24
24
  RAILS_ENV: test
25
25
  BUNDLE_GEMFILE: << parameters.gemfile >>
26
- - image: circleci/postgres:<< parameters.pg-version >>-alpine
26
+ - image: cimg/postgres:<< parameters.pg-version >>
27
27
  environment:
28
28
  POSTGRES_USER: eikaiwa
29
29
  POSTGRES_DB: iknow_view_models
@@ -104,23 +104,28 @@ workflows:
104
104
  - test:
105
105
  name: 'ruby 2.7 rails 5.2 pg 12'
106
106
  ruby-version: "2.7"
107
- pg-version: "12"
107
+ pg-version: "12.10"
108
108
  gemfile: gemfiles/rails_5_2.gemfile
109
109
  - test:
110
110
  name: 'ruby 2.7 rails 6.0 pg 12'
111
111
  ruby-version: "2.7"
112
- pg-version: "12"
112
+ pg-version: "12.10"
113
113
  gemfile: gemfiles/rails_6_0.gemfile
114
114
  - test:
115
115
  name: 'ruby 2.7 rails 6.1 pg 12'
116
116
  ruby-version: "2.7"
117
- pg-version: "12"
117
+ pg-version: "12.10"
118
118
  gemfile: gemfiles/rails_6_1.gemfile
119
119
  - test:
120
120
  name: 'ruby 3.0 rails 6.1 pg 12'
121
121
  ruby-version: "3.0"
122
- pg-version: "12"
122
+ pg-version: "12.10"
123
123
  gemfile: gemfiles/rails_6_1.gemfile
124
+ - test:
125
+ name: 'ruby 3.1 rails 7.0 pg 13'
126
+ ruby-version: "3.1"
127
+ pg-version: "13.6"
128
+ gemfile: gemfiles/rails_7_0.gemfile
124
129
  - publish:
125
130
  filters:
126
131
  branches:
data/Appraisals CHANGED
@@ -12,3 +12,8 @@ appraise 'rails-6-1' do
12
12
  gem 'activerecord', '~> 6.1.0'
13
13
  gem 'activesupport', '~> 6.1.0'
14
14
  end
15
+
16
+ appraise 'rails-7-0' do
17
+ gem 'activerecord', '~> 7.0.0'
18
+ gem 'activesupport', '~> 7.0.0'
19
+ end
data/Gemfile CHANGED
@@ -11,5 +11,6 @@ gem 'rubocop-iknow'
11
11
  gem 'minitest-ci'
12
12
 
13
13
  # Override gemspec for development version preferences
14
- gem 'activerecord', '~> 6.0.0'
15
- gem 'activesupport', '~> 6.0.0'
14
+ gem 'activerecord', '~> 7.0.0'
15
+ gem 'activesupport', '~> 7.0.0'
16
+ gem 'actionpack', '~> 7.0.0'
@@ -5,5 +5,6 @@ source 'https://rubygems.org'
5
5
  gem 'minitest-ci'
6
6
  gem 'activerecord', '~> 5.2.0'
7
7
  gem 'activesupport', '~> 5.2.0'
8
+ gem 'actionpack', '~> 5.2.0'
8
9
 
9
10
  gemspec path: '../'
@@ -5,5 +5,6 @@ source 'https://rubygems.org'
5
5
  gem 'minitest-ci'
6
6
  gem 'activerecord', '~> 6.0.0'
7
7
  gem 'activesupport', '~> 6.0.0'
8
+ gem 'actionpack', '~> 6.0.0'
8
9
 
9
10
  gemspec path: '../'
@@ -5,5 +5,6 @@ source 'https://rubygems.org'
5
5
  gem 'minitest-ci'
6
6
  gem 'activerecord', '~> 6.1.0'
7
7
  gem 'activesupport', '~> 6.1.0'
8
+ gem 'actionpack', '~> 6.1.0'
8
9
 
9
10
  gemspec path: '../'
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem 'minitest-ci'
6
+ gem "activerecord", "~> 7.0.0"
7
+ gem "activesupport", "~> 7.0.0"
8
+ gem "actionpack", "~> 7.0.0"
9
+
10
+ gemspec path: '../'
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.required_ruby_version = '>= 2.7'
23
23
 
24
+ spec.add_dependency 'actionpack', '>= 5.0'
24
25
  spec.add_dependency 'activerecord', '>= 5.0'
25
26
  spec.add_dependency 'activesupport', '>= 5.0'
26
27
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IknowViewModels
4
- VERSION = '3.6.0'
4
+ VERSION = '3.6.3'
5
5
  end
@@ -39,15 +39,31 @@ class ViewModel::AccessControl::Composed < ViewModel::AccessControl
39
39
  case
40
40
  when new_allow
41
41
  nil
42
- when self.allow_error && other.allow_error
43
- self.allow_error.merge(other.allow_error)
42
+ when self.allow_error.nil?
43
+ other.allow_error
44
+ when other.allow_error.nil?
45
+ self.allow_error
46
+ # Mergeable (standard) errors should be merged if possible; if not
47
+ # possible, we should take the first non-standard error.
48
+ when mergeable_error?(self.allow_error)
49
+ if mergeable_error?(other.allow_error)
50
+ self.allow_error.merge(other.allow_error)
51
+ else
52
+ other.allow_error
53
+ end
44
54
  else
45
- self.allow_error || other.allow_error
55
+ self.allow_error
46
56
  end
47
57
 
48
58
  ComposedResult.new(new_allow, other.veto, new_allow_error, other.veto_error)
49
59
  end
50
60
  end
61
+
62
+ private
63
+
64
+ def mergeable_error?(err)
65
+ err.is_a?(NoRequiredConditionsError)
66
+ end
51
67
  end
52
68
 
53
69
  PermissionsCheck = Struct.new(:location, :reason, :error_type, :checker) do
@@ -196,22 +212,54 @@ class ViewModel::AccessControl::Composed < ViewModel::AccessControl
196
212
  protected
197
213
 
198
214
  def check_delegates(env, ifs, unlesses)
199
- vetoed_checker = unlesses.detect { |checker| checker.check(env) }
215
+ veto, veto_error = detect_veto(env, unlesses)
216
+ allow, allow_error = detect_allow(env, ifs)
217
+
218
+ ComposedResult.new(allow, veto, allow_error, veto_error)
219
+ end
220
+
221
+ private
222
+
223
+ def detect_veto(env, checkers)
224
+ checkers.each do |checker|
225
+ result = checker.check(env)
226
+ next unless result
200
227
 
201
- veto = vetoed_checker.present?
202
- if veto
203
- veto_error = vetoed_checker.error_type.new('Action not permitted because: ' +
204
- vetoed_checker.reason,
205
- env.view.blame_reference)
228
+ error =
229
+ if result.is_a?(StandardError)
230
+ result
231
+ else
232
+ checker.error_type.new('Action not permitted because: ' +
233
+ checker.reason,
234
+ env.view.blame_reference)
235
+ end
236
+
237
+ # short-circuit exit with failure
238
+ return true, error
206
239
  end
207
240
 
208
- allow = ifs.any? { |checker| checker.check(env) }
241
+ return false, nil
242
+ end
243
+
244
+ def detect_allow(env, checkers)
245
+ error = nil
246
+
247
+ checkers.each do |checker|
248
+ result = checker.check(env)
249
+ next unless result
209
250
 
210
- unless allow
211
- allow_error = NoRequiredConditionsError.new(env.view.blame_reference,
212
- ifs.map(&:name))
251
+ if result.is_a?(StandardError)
252
+ error ||= result
253
+ else
254
+ # short-circuit exit with success
255
+ return true, nil
256
+ end
213
257
  end
214
258
 
215
- ComposedResult.new(allow, veto, allow_error, veto_error)
259
+ error ||= NoRequiredConditionsError.new(
260
+ env.view.blame_reference,
261
+ checkers.map(&:name))
262
+
263
+ return false, error
216
264
  end
217
265
  end
@@ -149,6 +149,14 @@ class ViewModel::AccessControl
149
149
  def raise_if_error!(result)
150
150
  raise (result.error || yield) unless result.permit?
151
151
  end
152
+
153
+ # Called from composed access controls via the `env`, this is used to make the
154
+ # if/unless DSL more readable when returning a custom failure error.
155
+ def failure(err)
156
+ raise ArgumentError.new("Unexpected failure type: #{err}") unless err.is_a?(StandardError)
157
+
158
+ err
159
+ end
152
160
  end
153
161
 
154
162
  require 'view_model/access_control/open'
@@ -14,9 +14,9 @@ class ViewModel::ActiveRecord
14
14
  def release!
15
15
  model = viewmodel.model
16
16
  case association_data.direct_reflection.options[:dependent]
17
- when :delete
17
+ when :delete, :delete_all
18
18
  model.delete
19
- when :destroy
19
+ when :destroy, :destroy_async
20
20
  model.destroy
21
21
  end
22
22
  end
@@ -14,7 +14,7 @@ class ViewModel::Migration::NoPathError < ViewModel::AbstractError
14
14
  end
15
15
 
16
16
  def detail
17
- "No migration path for #{vm_name} from #{from} to #{to}"
17
+ "No migration path for #{vm_name} between client version #{from} and server version #{to}"
18
18
  end
19
19
 
20
20
  def meta
@@ -65,8 +65,11 @@ class ViewModel::TestHelpers::ARVMBuilder
65
65
  ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{name.underscore.pluralize} CASCADE")
66
66
  namespace.send(:remove_const, name)
67
67
  namespace.send(:remove_const, viewmodel_name) if viewmodel
68
+
68
69
  # prevent cached old class from being used to resolve associations
69
- ActiveSupport::Dependencies::Reference.clear!
70
+ if ActiveSupport::VERSION::MAJOR < 7
71
+ ActiveSupport::Dependencies::Reference.clear!
72
+ end
70
73
  end
71
74
 
72
75
  private
data/lib/view_model.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  # A ViewModel encapsulates a particular aggregation of data calculated via the
4
4
  # underlying models and provides a means of serializing it into views.
5
5
  require 'jbuilder'
6
+ require 'base64'
6
7
  require 'deep_preloader'
7
8
 
8
9
  class ViewModel
data/nix/dependencies.nix CHANGED
@@ -1,5 +1,5 @@
1
1
  {pkgs}:
2
2
  {
3
- ruby = pkgs.ruby_2_7;
3
+ ruby = pkgs.ruby_3_0;
4
4
  postgresql = pkgs.postgresql_12;
5
5
  }
@@ -323,7 +323,11 @@ module ControllerTestControllers
323
323
  CONTROLLER_NAMES.each do |name|
324
324
  Object.send(:remove_const, name)
325
325
  end
326
- ActiveSupport::Dependencies::Reference.clear!
326
+
327
+ if ActiveSupport::VERSION::MAJOR < 7
328
+ ActiveSupport::Dependencies::Reference.clear!
329
+ end
330
+
327
331
  super
328
332
  end
329
333
  end
@@ -156,6 +156,54 @@ class ViewModel::AccessControlTest < ActiveSupport::TestCase
156
156
  assert_equal(2, ex.reasons.count)
157
157
  end
158
158
 
159
+ def test_veto_ordering
160
+ TestAccessControl.visible_if!('always') { true }
161
+
162
+ TestAccessControl.visible_unless!('car starts with i') do
163
+ view.car =~ /^i/
164
+ end
165
+
166
+ TestAccessControl.visible_unless!('car ends with e') do
167
+ view.car =~ /e$/
168
+ end
169
+
170
+ assert_serializes(ListView, List.create!(car: 'ok'))
171
+ refute_serializes(ListView, List.create!(car: 'invisible'), /not permitted.*car starts with i/)
172
+ end
173
+
174
+ def test_custom_error_if
175
+ TestAccessControl.visible_if!('car is visible1') do
176
+ view.car == 'visible1' ||
177
+ # In principle a failure() may return any error, but by returning an
178
+ # AccessControlError we make it possible to test with refute_serializes
179
+ failure(ViewModel::AccessControlError.new('Custom Error Message', view.blame_reference))
180
+ end
181
+
182
+ TestAccessControl.visible_if!('car is visible2') do
183
+ view.car == 'visible2' ||
184
+ # Only the first failure() recorded by a failed if check will be
185
+ # raised as the error.
186
+ failure(ViewModel::AccessControlError.new('Should not be seen', view.blame_reference))
187
+ end
188
+
189
+ assert_serializes(ListView, List.create!(car: 'visible1'))
190
+ assert_serializes(ListView, List.create!(car: 'visible2'))
191
+ refute_serializes(ListView, List.create!(car: 'bad'), /Custom Error Message/)
192
+ end
193
+
194
+ def test_custom_error_unless
195
+ TestAccessControl.visible_if!('always') { true }
196
+
197
+ TestAccessControl.visible_unless!('car is invisible') do
198
+ if view.car == 'invisible'
199
+ failure(ViewModel::AccessControlError.new('Custom Error Message', view.blame_reference))
200
+ end
201
+ end
202
+
203
+ assert_serializes(ListView, List.create!(car: 'ok'))
204
+ refute_serializes(ListView, List.create!(car: 'invisible'), /Custom Error Message/)
205
+ end
206
+
159
207
  def test_inheritance
160
208
  child_access_control = Class.new(ViewModel::AccessControl::Composed)
161
209
  child_access_control.include_from(TestAccessControl)
@@ -15,16 +15,11 @@ require 'view_model/active_record'
15
15
 
16
16
  DUMMY_RAILS_CACHE = ActiveSupport::Cache::MemoryStore.new
17
17
 
18
- module RailsDummyCache
19
- def cache
20
- DUMMY_RAILS_CACHE
21
- end
18
+ IknowCache.configure! do
19
+ logger ::ActiveRecord::Base.logger
20
+ cache DUMMY_RAILS_CACHE
22
21
  end
23
22
 
24
- # Ensure we have a dummy Rails, and then prepend our dummy cache
25
- module Rails; end
26
- Rails.singleton_class.prepend(RailsDummyCache)
27
-
28
23
  class ViewModel::ActiveRecord
29
24
  class CacheTest < ActiveSupport::TestCase
30
25
  using ViewModel::Utils::Collections
@@ -472,7 +472,11 @@ class ViewModel::RecordTest < ActiveSupport::TestCase
472
472
  def teardown
473
473
  Object.send(:remove_const, :Nested)
474
474
  Object.send(:remove_const, :NestedView)
475
- ActiveSupport::Dependencies::Reference.clear!
475
+
476
+ if ActiveSupport::VERSION::MAJOR < 7
477
+ ActiveSupport::Dependencies::Reference.clear!
478
+ end
479
+
476
480
  super
477
481
  end
478
482
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iknow_view_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - iKnow Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-17 00:00:00.000000000 Z
11
+ date: 2022-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: activerecord
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -380,6 +394,7 @@ files:
380
394
  - gemfiles/rails_5_2.gemfile
381
395
  - gemfiles/rails_6_0.gemfile
382
396
  - gemfiles/rails_6_1.gemfile
397
+ - gemfiles/rails_7_0.gemfile
383
398
  - iknow_view_models.gemspec
384
399
  - lib/iknow_view_models.rb
385
400
  - lib/iknow_view_models/railtie.rb
@@ -494,7 +509,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
494
509
  - !ruby/object:Gem::Version
495
510
  version: '0'
496
511
  requirements: []
497
- rubygems_version: 3.1.6
512
+ rubygems_version: 3.3.11
498
513
  signing_key:
499
514
  specification_version: 4
500
515
  summary: ViewModels provide a means of encapsulating a collection of related data