standardapi 6.0.0.32 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -58
  3. data/lib/standard_api/access_control_list.rb +148 -0
  4. data/lib/standard_api/controller.rb +116 -27
  5. data/lib/standard_api/helpers.rb +17 -10
  6. data/lib/standard_api/includes.rb +9 -0
  7. data/lib/standard_api/middleware/query_encoding.rb +3 -3
  8. data/lib/standard_api/middleware.rb +5 -0
  9. data/lib/standard_api/railtie.rb +30 -2
  10. data/lib/standard_api/route_helpers.rb +64 -14
  11. data/lib/standard_api/test_case/calculate_tests.rb +15 -8
  12. data/lib/standard_api/test_case/create_tests.rb +7 -9
  13. data/lib/standard_api/test_case/destroy_tests.rb +19 -7
  14. data/lib/standard_api/test_case/index_tests.rb +10 -6
  15. data/lib/standard_api/test_case/schema_tests.rb +7 -1
  16. data/lib/standard_api/test_case/show_tests.rb +8 -7
  17. data/lib/standard_api/test_case/update_tests.rb +15 -15
  18. data/lib/standard_api/test_case.rb +13 -3
  19. data/lib/standard_api/version.rb +1 -1
  20. data/lib/standard_api/views/application/_record.json.jbuilder +18 -17
  21. data/lib/standard_api/views/application/_record.streamer +40 -37
  22. data/lib/standard_api/views/application/_schema.json.jbuilder +20 -8
  23. data/lib/standard_api/views/application/_schema.streamer +22 -8
  24. data/lib/standard_api/views/application/new.streamer +1 -1
  25. data/lib/standard_api.rb +5 -0
  26. data/test/standard_api/caching_test.rb +14 -4
  27. data/test/standard_api/controller/include_test.rb +107 -0
  28. data/test/standard_api/controller/subresource_test.rb +157 -0
  29. data/test/standard_api/helpers_test.rb +34 -17
  30. data/test/standard_api/nested_attributes/belongs_to_test.rb +71 -0
  31. data/test/standard_api/nested_attributes/has_and_belongs_to_many_test.rb +70 -0
  32. data/test/standard_api/nested_attributes/has_many_test.rb +85 -0
  33. data/test/standard_api/nested_attributes/has_one_test.rb +71 -0
  34. data/test/standard_api/route_helpers_test.rb +56 -0
  35. data/test/standard_api/standard_api_test.rb +182 -44
  36. data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
  37. data/test/standard_api/test_app/app/controllers/acl/camera_acl.rb +7 -0
  38. data/test/standard_api/test_app/app/controllers/acl/photo_acl.rb +13 -0
  39. data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +33 -0
  40. data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
  41. data/test/standard_api/test_app/controllers.rb +28 -43
  42. data/test/standard_api/test_app/models.rb +76 -7
  43. data/test/standard_api/test_app/test/factories.rb +7 -3
  44. data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
  45. data/test/standard_api/test_app/views/photos/_photo.streamer +2 -1
  46. data/test/standard_api/test_app/views/sessions/create.json.jbuilder +1 -0
  47. data/test/standard_api/test_app/views/sessions/create.streamer +3 -0
  48. data/test/standard_api/test_app.rb +12 -1
  49. data/test/standard_api/test_helper.rb +21 -0
  50. metadata +59 -16
@@ -6,13 +6,13 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
6
6
 
7
7
  self.includes = [ :photos, :landlord, :english_name ]
8
8
 
9
- # def normalizers
10
- # {
11
- # Property => {
12
- # "size" => lambda { |value| value.to_i }
13
- # }
14
- # }
15
- # end
9
+ def normalizers
10
+ {
11
+ Property => {
12
+ "size" => lambda { |value| value.round(4).to_s }
13
+ }
14
+ }
15
+ end
16
16
 
17
17
  # = Routing Tests
18
18
  #
@@ -92,17 +92,33 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
92
92
  assert_equal @controller.send(:model_includes), []
93
93
  end
94
94
 
95
- test 'Controller#model_params defaults to []' do
95
+ test 'Controller#model_params defaults to ActionController::Parameters' do
96
+ @controller = DocumentsController.new
97
+ @controller.params = ActionController::Parameters.new
98
+ assert_equal @controller.send(:model_params), ActionController::Parameters.new
99
+ end
100
+
101
+ test 'Controller#model_params defaults to ActionController::Parameters when no resource_attributes' do
96
102
  @controller = ReferencesController.new
97
- assert_equal @controller.send(:model_params), []
103
+ @controller.params = ActionController::Parameters.new
104
+ assert_equal @controller.send(:model_params), ActionController::Parameters.new
98
105
  end
99
106
 
100
- test 'Controller#current_mask' do
107
+ test 'Controller#mask' do
101
108
  @controller = ReferencesController.new
102
- @controller.instance_variable_set('@current_mask', { 'references' => { 'subject_id' => 1 }})
109
+ @controller.define_singleton_method(:mask_for) do |table_name|
110
+ {subject_id: 1}
111
+ end
103
112
  @controller.params = {}
104
113
  assert_equal 'SELECT "references".* FROM "references" WHERE "references"."subject_id" = 1', @controller.send(:resources).to_sql
105
114
  end
115
+
116
+ test "Auto includes on a controller without a model" do
117
+ @controller = SessionsController.new
118
+ assert_nil @controller.send(:model)
119
+ post sessions_path(format: :json), params: {session: {user: 'user', pass: 'pass'}}
120
+ assert_response :ok
121
+ end
106
122
 
107
123
  test 'ApplicationController#schema.json' do
108
124
  get schema_path(format: 'json')
@@ -111,7 +127,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
111
127
  controllers = ApplicationController.descendants
112
128
  controllers.select! { |c| c.ancestors.include?(StandardAPI::Controller) && c != StandardAPI::Controller }
113
129
 
114
- @controller.send(:models).reject { |x| x.name == 'Photo' }.each do |model|
130
+ @controller.send(:models).reject { |x| %w(Photo Document).include?(x.name) }.each do |model|
115
131
  assert_equal true, schema['models'].has_key?(model.name)
116
132
 
117
133
  model_comment = model.connection.table_comment(model.table_name)
@@ -123,8 +139,9 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
123
139
 
124
140
  model.columns.each do |column|
125
141
  assert_equal json_column_type(column.sql_type), schema.dig('models', model.name, 'attributes', column.name, 'type')
126
- default = column.default || column.default_function
127
- if default then
142
+ default = column.default
143
+ if default
144
+ default = model.connection.lookup_cast_type_from_column(column).deserialize(default)
128
145
  assert_equal default, schema.dig('models', model.name, 'attributes', column.name, 'default')
129
146
  else
130
147
  assert_nil schema.dig('models', model.name, 'attributes', column.name, 'default')
@@ -132,11 +149,15 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
132
149
  assert_equal column.name == model.primary_key, schema.dig('models', model.name, 'attributes', column.name, 'primary_key')
133
150
  assert_equal column.null, schema.dig('models', model.name, 'attributes', column.name, 'null')
134
151
  assert_equal column.array, schema.dig('models', model.name, 'attributes', column.name, 'array')
135
- if column.comment then
152
+ if column.comment
136
153
  assert_equal column.comment, schema.dig('models', model.name, 'attributes', column.name, 'comment')
137
154
  else
138
155
  assert_nil schema.dig('models', model.name, 'attributes', column.name, 'comment')
139
156
  end
157
+
158
+ if column.respond_to?(:auto_populated?)
159
+ assert_equal !!column.auto_populated?, schema.dig('models', model.name, 'attributes', column.name, 'auto_populated')
160
+ end
140
161
  end
141
162
  end
142
163
 
@@ -161,6 +182,26 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
161
182
  assert_nil schema['limit']
162
183
  end
163
184
 
185
+ test 'Controller#schema.json for an enum with default' do
186
+ get schema_documents_path(format: 'json')
187
+
188
+ schema = JSON(response.body)
189
+
190
+ assert_equal true, schema.has_key?('attributes')
191
+ assert_equal 'string', schema['attributes']['level']['type']
192
+ assert_equal 'public', schema['attributes']['level']['default']
193
+ end
194
+
195
+ test 'Controller#schema.json for an enum without default' do
196
+ get schema_documents_path(format: 'json')
197
+
198
+ schema = JSON(response.body)
199
+
200
+ assert_equal true, schema.has_key?('attributes')
201
+ assert_equal 'string', schema['attributes']['rating']['type']
202
+ assert_nil schema['attributes']['rating']['default']
203
+ end
204
+
164
205
  test 'Controller#index w/o limit' do
165
206
  account = create(:account)
166
207
  get unlimited_index_path(format: 'json')
@@ -186,29 +227,6 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
186
227
  assert_redirected_to document_path(pdf)
187
228
  end
188
229
 
189
- test 'Controller#add_resource' do
190
- property = create(:property, photos: [])
191
- photo = create(:photo)
192
-
193
- post "/properties/#{property.id}/photos/#{photo.id}.json"
194
- assert_equal property.photos.reload.map(&:id), [photo.id]
195
- assert_response :created
196
-
197
- post "/properties/#{property.id}/photos/9999999"
198
- assert_response :not_found
199
- end
200
-
201
- test 'Controller#remove_resource' do
202
- photo = create(:photo)
203
- property = create(:property, photos: [photo])
204
- delete "/properties/#{property.id}/photos/#{photo.id}"
205
- assert_equal property.photos.reload, []
206
- assert_response :no_content
207
-
208
- delete "/properties/#{property.id}/photos/9999999"
209
- assert_response :not_found
210
- end
211
-
212
230
  # = View Tests
213
231
 
214
232
  test 'rendering tables' do
@@ -216,23 +234,55 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
216
234
  assert_response :ok
217
235
  # assert_equal ['properties', 'accounts', 'photos', 'references', 'sessions', 'unlimited'], response.parsed_body
218
236
  # Multiple 'accounts' because multiple controllers with that model for testing.
219
- assert_equal ["properties", "accounts", "documents", "photos", "references", "accounts", 'accounts'].sort, response.parsed_body.sort
237
+ assert_equal ["properties", "accounts", "documents", "photos", "references", "accounts", 'accounts', 'uuid_models'].sort, response.parsed_body.sort
220
238
  end
221
239
 
222
240
  test 'rendering null attribute' do
223
241
  property = create(:property)
224
242
  get property_path(property, format: 'json'), params: { id: property.id, include: [:landlord] }
225
- assert_equal true, JSON(response.body).has_key?('landlord')
243
+ assert JSON(response.body).has_key?('landlord')
226
244
  assert_nil JSON(response.body)['landlord']
227
245
  end
228
246
 
247
+ test 'rendering binary attribute' do
248
+ reference = create(:reference, sha: "Hello World")
249
+ get reference_path(reference, format: 'json'), params: { id: reference.id }
250
+ assert_equal "48656c6c6f20576f726c64", JSON(response.body)['sha']
251
+ end
252
+
253
+ test 'rendering a custom binary attribute' do
254
+ reference = create(:reference, custom_binary: 2)
255
+ get reference_path(reference, format: 'json'), params: { id: reference.id }
256
+ assert_equal 2, JSON(response.body)['custom_binary']
257
+ assert_equal "\\x00000002".b,reference.custom_binary_before_type_cast
258
+ end
259
+
229
260
  test 'rendering null attribute for has_one through' do
230
261
  property = create(:property)
231
262
  get property_path(property, format: 'json'), params: { id: property.id, include: [:document] }
232
263
  assert JSON(response.body).has_key?('document')
233
264
  assert_nil JSON(response.body)['document']
234
265
  end
266
+
267
+ test 'rendering serialize_attribute' do
268
+ property = create(:property, description: 'This text will magically change')
269
+ get property_path(property, format: 'json'), params: { id: property.id, magic: true }
235
270
 
271
+ body = JSON(response.body)
272
+ assert_equal body['description'], 'See it changed!'
273
+ end
274
+
275
+ test 'rendering an enum' do
276
+ public_document = create(:document, level: 'public')
277
+
278
+ get documents_path(format: 'json'), params: { limit: 1 }
279
+ assert_equal JSON(response.body)[0]['level'], 'public'
280
+
281
+ secret_document = create(:document, level: 'secret')
282
+ get document_path(secret_document, format: 'json')
283
+ assert_equal JSON(response.body)['level'], 'secret'
284
+ end
285
+
236
286
  test '#index.json uses overridden partial' do
237
287
  create(:property, photos: [create(:photo)])
238
288
  get properties_path(format: 'json'), params: { limit: 100, include: [{:photos => { order: :id }}] }
@@ -320,14 +370,22 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
320
370
  test 'include with where key' do
321
371
  photo_a = create(:photo)
322
372
  photo_b = create(:photo)
373
+ photo_c = create(:photo)
323
374
 
324
- property = create(:property, photos: [photo_b])
375
+ property = create(:property, photos: [photo_b, photo_c])
325
376
  get property_path(property, include: { photos: { where: { id: photo_a.id } } }, format: :json)
326
377
  assert_equal [], JSON(response.body)['photos']
327
378
 
328
379
  property.photos << photo_a
329
380
  get property_path(property, include: { photos: { where: { id: photo_a.id } } }, format: :json)
330
381
  assert_equal [photo_a.id], JSON(response.body)['photos'].map { |x| x['id'] }
382
+ get property_path(property, include: { photos: { where: [
383
+ { id: photo_a.id },
384
+ 'OR',
385
+ { id: photo_c.id}
386
+ ] } }, format: :json)
387
+ assert_equal [photo_a.id, photo_c.id].sort,
388
+ JSON(response.body)['photos'].map { |x| x['id'] }.sort
331
389
  end
332
390
 
333
391
  test 'include with order key' do
@@ -338,6 +396,14 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
338
396
  assert_equal photos.map(&:id).sort, JSON(response.body)['photos'].map { |x| x['id'] }
339
397
  end
340
398
 
399
+ test 'include relation with default order using an order key' do
400
+ p1 = create(:photo)
401
+ p2 = create(:photo)
402
+ account = create(:account, photos: [ p1, p2 ])
403
+ get account_path(account, include: { photos: { order: { created_at: :desc } } }, format: 'json')
404
+ assert_equal [ p2.id, p1.id ], JSON(response.body)['photos'].map { |x| x["id"] }
405
+ end
406
+
341
407
  test 'include with limit key' do
342
408
  5.times { create(:property, photos: Array.new(5) { create(:photo) }) }
343
409
  get properties_path(include: { photos: { limit: 1 } }, limit: 5, format: 'json')
@@ -380,8 +446,8 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
380
446
 
381
447
  json = JSON(response.body)
382
448
 
383
- assert_equal json.find { |x| x['id'] == account_reference.id }.dig('subject', 'photos', 0, 'id'), photo.id
384
- assert_equal json.find { |x| x['id'] == property_reference.id }.dig('subject', 'landlord', 'id'), account.id
449
+ assert_equal photo.id, json.find { |x| x['id'] == account_reference.id }.dig('subject', 'photos', 0, 'id')
450
+ assert_equal account.id, json.find { |x| x['id'] == property_reference.id }.dig('subject', 'landlord', 'id')
385
451
  end
386
452
 
387
453
  test 'include with distinct key' do
@@ -408,7 +474,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
408
474
  },
409
475
  format: 'json')
410
476
 
411
- assert_equal [photos.first.id], JSON(response.body)['photos'].map { |x| x['id'] }
477
+ assert_equal [photos.map(&:id).sort.first], JSON(response.body)['photos'].map { |x| x['id'] }
412
478
 
413
479
  get property_path(property,
414
480
  include: {
@@ -549,6 +615,19 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
549
615
  assert_equal properties.map(&:id).sort.reverse, JSON(response.body).map { |x| x['id'] }
550
616
  end
551
617
 
618
+ test 'ordering via nulls_first/last' do
619
+ p1 = create(:property, description: 'test')
620
+ p2 = create(:property, description: nil)
621
+
622
+ get properties_path(format: 'json'), params: { limit: 100, order: { description: { desc: 'nulls_last' } } }
623
+ properties = JSON(response.body)
624
+ assert_equal p1.id, properties.first['id']
625
+
626
+ get properties_path(format: 'json'), params: { limit: 100, order: { description: { asc: 'nulls_last' } } }
627
+ properties = JSON(response.body)
628
+ assert_equal p1.id, properties.first['id']
629
+ end
630
+
552
631
  # Calculate Test
553
632
  test 'calculate' do
554
633
  create(:photo)
@@ -556,6 +635,65 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
556
635
  assert_equal [1], JSON(response.body)
557
636
  end
558
637
 
638
+ test 'calculate distinct aggregation' do
639
+ assert_sql(<<-SQL) do
640
+ SELECT DISTINCT COUNT("properties"."id") FROM "properties"
641
+ SQL
642
+ create(:property)
643
+ create(:property)
644
+ get '/properties/calculate', params: {
645
+ select: { count: "id" },
646
+ distinct: true
647
+ }
648
+ assert_equal [2], JSON(response.body)
649
+ end
650
+ end
651
+
652
+ test 'calculate aggregation distinctly' do
653
+ assert_sql(<<-SQL) do
654
+ SELECT COUNT(DISTINCT "properties"."id") FROM "properties"
655
+ SQL
656
+ create(:property)
657
+ create(:property)
658
+ get '/properties/calculate', params: {
659
+ select: { count: { distinct: "id" } }
660
+ }
661
+ assert_equal [2], JSON(response.body)
662
+ end
663
+ end
664
+
665
+ test 'calculate distinct aggregation distinctly' do
666
+ assert_sql(<<-SQL) do
667
+ SELECT DISTINCT COUNT(DISTINCT "properties"."id") FROM "properties"
668
+ SQL
669
+ create(:property)
670
+ create(:property)
671
+ get '/properties/calculate', params: {
672
+ select: { count: { distinct: "id" } },
673
+ distinct: true
674
+ }
675
+ assert_equal [2], JSON(response.body)
676
+ end
677
+ end
678
+
679
+ test 'calculate distinct count' do
680
+ p1 = create(:property)
681
+ p2 = create(:property)
682
+ a1 = create(:account, property: p1)
683
+ a2 = create(:account, property: p2)
684
+ get '/properties/calculate', params: {
685
+ select: { count: 'id' },
686
+ where: {
687
+ accounts: {
688
+ id: [a1.id, a2.id]
689
+ }
690
+ },
691
+ group_by: 'id',
692
+ distinct: true
693
+ }
694
+ assert_equal Hash[p1.id.to_s, 1, p2.id.to_s, 1], JSON(response.body)
695
+ end
696
+
559
697
  test 'calculate group_by' do
560
698
  create(:photo, format: 'jpg')
561
699
  create(:photo, format: 'jpg')
@@ -0,0 +1,15 @@
1
+ module AccountACL
2
+
3
+ def attributes
4
+ [ "property_id", "name" ]
5
+ end
6
+
7
+ def orders
8
+ [ "id" ]
9
+ end
10
+
11
+ def includes
12
+ [ "photos", "subject", "property" ]
13
+ end
14
+
15
+ end
@@ -0,0 +1,7 @@
1
+ module CameraACL
2
+
3
+ def attributes
4
+ [ :make ]
5
+ end
6
+
7
+ end
@@ -0,0 +1,13 @@
1
+ module PhotoACL
2
+
3
+ def attributes
4
+ [
5
+ :format
6
+ ]
7
+ end
8
+
9
+ def nested
10
+ [ :account, :camera ]
11
+ end
12
+
13
+ end
@@ -0,0 +1,33 @@
1
+ module PropertyACL
2
+
3
+ # Attributes allowed to be updated
4
+ def attributes
5
+ [ :name,
6
+ :aliases,
7
+ :description,
8
+ :constructed,
9
+ :size,
10
+ :active
11
+ # :photos_attributes,
12
+ # { photos_attributes: [ :id, :account_id, :property_id, :format] }
13
+ ]
14
+ end
15
+
16
+ # Orderings allowed
17
+ def orders
18
+ ["id", "name", "aliases", "description", "constructed", "size", "created_at", "active"]
19
+ end
20
+
21
+ # Sub resources allowed to be included in the response
22
+ def includes
23
+ [ :photos, :landlord, :english_name, :document ]
24
+ end
25
+
26
+ # Sub resourced allowed to be set during create / update / delete if a user is
27
+ # allowed to ....
28
+ # only add to and from the relation, can also create or update the subresource
29
+ def nested
30
+ [ :photos, :accounts ]
31
+ end
32
+
33
+ end
@@ -0,0 +1,7 @@
1
+ module ReferenceACL
2
+
3
+ def includes
4
+ { subject: [ :landlord, :photos ] }
5
+ end
6
+
7
+ end
@@ -1,43 +1,18 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  include StandardAPI::Controller
3
+ include StandardAPI::AccessControlList
3
4
  prepend_view_path File.join(File.dirname(__FILE__), 'views')
5
+
6
+ helper_method :serialize_attribute
4
7
 
5
- private
6
-
7
- def account_params
8
- [ "property_id", "name" ]
9
- end
10
-
11
- def account_orders
12
- [ "id" ]
13
- end
14
-
15
- def account_includes
16
- [ "photos" ]
17
- end
18
-
19
- def property_params
20
- [ :name,
21
- :aliases,
22
- :description,
23
- :constructed,
24
- :size,
25
- :active,
26
- :photos_attributes,
27
- { photos_attributes: [ :id, :account_id, :property_id, :format] }
28
- ]
29
- end
30
-
31
- def property_orders
32
- ["id", "name", "aliases", "description", "constructed", "size", "created_at", "active"]
33
- end
34
-
35
- def property_includes
36
- [:photos, :landlord, :english_name, :document]
37
- end
38
-
39
- def reference_includes
40
- { subject: [ :landlord, :photos ] }
8
+ def serialize_attribute(json, record, attribute, type)
9
+ value = if attribute == 'description' && params["magic"] === "true"
10
+ 'See it changed!'
11
+ else
12
+ record.send(attribute)
13
+ end
14
+
15
+ json.set! attribute, type == :binary ? value&.unpack1('H*') : value
41
16
  end
42
17
 
43
18
  end
@@ -46,32 +21,37 @@ class PropertiesController < ApplicationController
46
21
  end
47
22
 
48
23
  class AccountsController < ApplicationController
24
+
25
+ def show
26
+ @account = Account.last
27
+ end
28
+
49
29
  end
50
30
 
51
31
  class DocumentsController < ApplicationController
52
32
 
53
- def document_params
33
+ def document_attributes
54
34
  [ :file, :type ]
55
35
  end
56
-
36
+
57
37
  def document_orders
58
- [:id]
38
+ [ :id ]
59
39
  end
60
40
 
61
41
  end
62
42
 
63
43
  class PhotosController < ApplicationController
64
44
 
65
- def photo_params
45
+ def photo_attributes
66
46
  [ :id, :account_id, :property_id, :format ]
67
47
  end
68
48
 
69
49
  def photo_orders
70
- [:id]
50
+ [ :id ]
71
51
  end
72
52
 
73
53
  def photo_includes
74
- [:account]
54
+ [ :account ]
75
55
  end
76
56
 
77
57
  end
@@ -80,6 +60,8 @@ class ReferencesController < ApplicationController
80
60
  end
81
61
 
82
62
  class SessionsController < ApplicationController
63
+ def create
64
+ end
83
65
  end
84
66
 
85
67
  class UnlimitedController < ApplicationController
@@ -104,4 +86,7 @@ class DefaultLimitController < ApplicationController
104
86
  100
105
87
  end
106
88
 
107
- end
89
+ end
90
+
91
+ class UuidModelController < ApplicationController
92
+ end