iknow_view_models 3.2.0 → 3.2.1

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -0
  3. data/Appraisals +6 -6
  4. data/Rakefile +5 -5
  5. data/gemfiles/rails_5_2.gemfile +5 -5
  6. data/gemfiles/rails_6_0.gemfile +5 -5
  7. data/iknow_view_models.gemspec +40 -39
  8. data/lib/iknow_view_models.rb +9 -7
  9. data/lib/iknow_view_models/version.rb +1 -1
  10. data/lib/view_model.rb +17 -14
  11. data/lib/view_model/access_control.rb +5 -2
  12. data/lib/view_model/access_control/composed.rb +10 -9
  13. data/lib/view_model/access_control/open.rb +2 -0
  14. data/lib/view_model/access_control/read_only.rb +2 -0
  15. data/lib/view_model/access_control/tree.rb +11 -6
  16. data/lib/view_model/access_control_error.rb +4 -1
  17. data/lib/view_model/active_record.rb +12 -11
  18. data/lib/view_model/active_record/association_data.rb +2 -1
  19. data/lib/view_model/active_record/association_manipulation.rb +6 -4
  20. data/lib/view_model/active_record/cache.rb +4 -2
  21. data/lib/view_model/active_record/collection_nested_controller.rb +3 -3
  22. data/lib/view_model/active_record/controller_base.rb +4 -1
  23. data/lib/view_model/active_record/nested_controller_base.rb +1 -0
  24. data/lib/view_model/active_record/update_context.rb +8 -6
  25. data/lib/view_model/active_record/update_data.rb +32 -30
  26. data/lib/view_model/active_record/update_operation.rb +17 -13
  27. data/lib/view_model/active_record/visitor.rb +0 -1
  28. data/lib/view_model/after_transaction_runner.rb +0 -1
  29. data/lib/view_model/callbacks.rb +3 -1
  30. data/lib/view_model/controller.rb +13 -3
  31. data/lib/view_model/deserialization_error.rb +15 -12
  32. data/lib/view_model/error.rb +12 -10
  33. data/lib/view_model/error_view.rb +3 -1
  34. data/lib/view_model/migration/no_path_error.rb +1 -0
  35. data/lib/view_model/migration/one_way_error.rb +1 -0
  36. data/lib/view_model/migration/unspecified_version_error.rb +1 -0
  37. data/lib/view_model/record.rb +11 -13
  38. data/lib/view_model/reference.rb +3 -1
  39. data/lib/view_model/references.rb +8 -5
  40. data/lib/view_model/registry.rb +1 -1
  41. data/lib/view_model/schemas.rb +9 -4
  42. data/lib/view_model/serialization_error.rb +4 -1
  43. data/lib/view_model/serialize_context.rb +4 -4
  44. data/lib/view_model/test_helpers.rb +8 -3
  45. data/lib/view_model/test_helpers/arvm_builder.rb +19 -14
  46. data/lib/view_model/traversal_context.rb +2 -1
  47. data/test/.rubocop.yml +14 -0
  48. data/test/helpers/arvm_test_models.rb +12 -9
  49. data/test/helpers/arvm_test_utilities.rb +5 -3
  50. data/test/helpers/controller_test_helpers.rb +31 -29
  51. data/test/helpers/match_enumerator.rb +1 -0
  52. data/test/helpers/query_logging.rb +2 -1
  53. data/test/helpers/test_access_control.rb +5 -3
  54. data/test/helpers/viewmodel_spec_helpers.rb +21 -20
  55. data/test/unit/view_model/access_control_test.rb +144 -144
  56. data/test/unit/view_model/active_record/alias_test.rb +15 -13
  57. data/test/unit/view_model/active_record/belongs_to_test.rb +40 -39
  58. data/test/unit/view_model/active_record/cache_test.rb +27 -26
  59. data/test/unit/view_model/active_record/cloner_test.rb +67 -63
  60. data/test/unit/view_model/active_record/controller_test.rb +37 -38
  61. data/test/unit/view_model/active_record/counter_test.rb +10 -9
  62. data/test/unit/view_model/active_record/customization_test.rb +59 -58
  63. data/test/unit/view_model/active_record/has_many_test.rb +112 -111
  64. data/test/unit/view_model/active_record/has_many_through_poly_test.rb +15 -14
  65. data/test/unit/view_model/active_record/has_many_through_test.rb +33 -38
  66. data/test/unit/view_model/active_record/has_one_test.rb +37 -36
  67. data/test/unit/view_model/active_record/migration_test.rb +13 -13
  68. data/test/unit/view_model/active_record/namespacing_test.rb +19 -17
  69. data/test/unit/view_model/active_record/poly_test.rb +44 -45
  70. data/test/unit/view_model/active_record/shared_test.rb +30 -28
  71. data/test/unit/view_model/active_record/version_test.rb +9 -7
  72. data/test/unit/view_model/active_record_test.rb +72 -72
  73. data/test/unit/view_model/callbacks_test.rb +19 -15
  74. data/test/unit/view_model/controller_test.rb +4 -2
  75. data/test/unit/view_model/record_test.rb +92 -97
  76. data/test/unit/view_model/traversal_context_test.rb +4 -5
  77. data/test/unit/view_model_test.rb +18 -16
  78. 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
- if (ref = @ref_by_value[value]).present?
21
- ref
22
- else
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:%06d' % (@last_ref += 1)
47
+ format('ref:i:%06<count>d', count: (@last_ref += 1))
45
48
  end
46
49
  end
47
50
  end
@@ -12,7 +12,7 @@ class ViewModel::Registry
12
12
 
13
13
  def initialize
14
14
  @lock = Monitor.new
15
- @viewmodel_classes_by_name = {}
15
+ @viewmodel_classes_by_name = {}
16
16
  @deferred_viewmodel_classes = []
17
17
  end
18
18
 
@@ -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)
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ViewModel::SerializationError < ViewModel::AbstractError
2
4
  attr_reader :detail
5
+
3
6
  status 400
4
- code "SerializationError"
7
+ code 'SerializationError'
5
8
 
6
9
  def initialize(detail)
7
10
  @detail = detail
@@ -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
- unless serialized_refs.has_key?(ref)
42
- serialized_refs[ref] = Jbuilder.new do |j|
43
- ViewModel.serialize(value, j, serialize_context: reference_context)
44
- end
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
- if viewmodel.is_a?(ViewModel::ActiveRecord)
59
+ case viewmodel
60
+ when ViewModel::ActiveRecord
57
61
  assert_model_represents_database(viewmodel.model, been_there: been_there)
58
- elsif viewmodel.is_a?(ViewModel::Record)
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 == nil
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 "Model not created in ARVMBuilder" unless model
50
- raise "Schema not created in ARVMBuilder" unless model.table_exists?
51
- raise "ViewModel not created in ARVMBuilder" unless (viewmodel || @no_viewmodel)
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 "Failed to register expected new class!" unless resolved == @viewmodel
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 + "View"
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
- _namespace = namespace
88
- @model = Class.new(@model_base) do |c|
89
- raise "Model already defined: #{model_name}" if _namespace.const_defined?(model_name, false)
90
- _namespace.const_set(model_name, self)
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
- _namespace = namespace
100
- @viewmodel = Class.new(@viewmodel_base) do |c|
101
- raise "Viewmodel alreay defined: #{vm_name}" if _namespace.const_defined?(vm_name, false)
102
- _namespace.const_set(vm_name, self)
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 "help help" if @viewmodel.name.nil?
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("Attempted to find nearest root from a root context. This is probably not what you wanted.")
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
@@ -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
- require_relative "test_access_control.rb"
1
+ # frozen_string_literal: true
2
2
 
3
- require "iknow_view_models"
4
- require "view_model/active_record"
5
- require "view_model/active_record/controller"
3
+ require_relative 'test_access_control'
6
4
 
7
- require "acts_as_manual_list"
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.load(File.open(db_config_path))
11
- raise "Test database configuration missing" unless db_config["test"]
12
- ActiveRecord::Base.establish_connection(db_config["test"])
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
- grand_parents categories tags parents_tags].each do |t|
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.rb'
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(STDERR)
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
- require "iknow_view_models"
2
- require "view_model/active_record"
3
- require "view_model/active_record/controller"
1
+ # frozen_string_literal: true
4
2
 
5
- require_relative "../helpers/arvm_test_utilities.rb"
6
- require_relative "../helpers/arvm_test_models.rb"
3
+ require 'iknow_view_models'
4
+ require 'view_model/active_record'
5
+ require 'view_model/active_record/controller'
7
6
 
8
- require "acts_as_manual_list"
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, refs|
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
- begin
156
- self.public_send(method)
157
- rescue Exception => ex
158
- handler = self.class.rescue_block(ex.class)
159
- case handler
160
- when nil
161
- raise
162
- when Symbol
163
- self.send(handler, ex)
164
- when Proc
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 "Not a JSON response" unless @content_type == 'application/json'
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, h| type <= btype }.try(&:last)
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(**args)
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(**args)
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 |c|
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 |c|
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 |c|
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 |c|
275
+ Class.new(DummyController) do |_c|
274
276
  Object.const_set(:TargetController, self)
275
277
  include ViewModel::ActiveRecord::Controller
276
278
  include CallbackTracing