wor-simple_crud 0.5.0 → 0.7.0

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/Gemfile +5 -1
  4. data/README.md +104 -8
  5. data/lib/simple_crud/action_context.rb +45 -10
  6. data/lib/simple_crud/create_context.rb +2 -1
  7. data/lib/simple_crud/destroy_context.rb +2 -1
  8. data/lib/simple_crud/index_context.rb +30 -12
  9. data/lib/simple_crud/pagination/wor_paginate_adapter.rb +7 -1
  10. data/lib/simple_crud/persistence_context.rb +33 -6
  11. data/lib/simple_crud/relation_source.rb +107 -0
  12. data/lib/simple_crud/rspec/config.rb +6 -1
  13. data/lib/simple_crud/rspec/helpers/authentication.rb +2 -1
  14. data/lib/simple_crud/rspec/helpers/controller_options.rb +2 -2
  15. data/lib/simple_crud/rspec/helpers/requests.rb +5 -2
  16. data/lib/simple_crud/rspec.rb +2 -0
  17. data/lib/simple_crud/serializer.rb +14 -0
  18. data/lib/simple_crud/show_context.rb +3 -1
  19. data/lib/simple_crud/simple_crud_controller.rb +29 -6
  20. data/lib/simple_crud/update_context.rb +2 -1
  21. data/lib/simple_crud/version.rb +1 -1
  22. data/lib/spec/matchers/have_been_serialized_with.rb +19 -2
  23. data/lib/spec/shared_examples/simple_crud_for_create.rb +6 -6
  24. data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +2 -2
  25. data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +1 -1
  26. data/lib/spec/shared_examples/simple_crud_for_destroy.rb +8 -8
  27. data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +1 -1
  28. data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +3 -3
  29. data/lib/spec/shared_examples/simple_crud_for_edit.rb +2 -2
  30. data/lib/spec/shared_examples/simple_crud_for_index.rb +3 -3
  31. data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +1 -1
  32. data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +1 -1
  33. data/lib/spec/shared_examples/simple_crud_for_nested_resource.rb +21 -0
  34. data/lib/spec/shared_examples/simple_crud_for_new.rb +1 -1
  35. data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +1 -1
  36. data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +1 -1
  37. data/lib/spec/shared_examples/simple_crud_for_owned_resource.rb +35 -0
  38. data/lib/spec/shared_examples/simple_crud_for_show.rb +2 -2
  39. data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +1 -1
  40. data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +1 -1
  41. data/lib/spec/shared_examples/simple_crud_for_update.rb +7 -7
  42. data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +1 -1
  43. data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +1 -1
  44. data/spec/blueprint/dummy_models_controller_spec.rb +25 -0
  45. data/spec/blueprint_unpaginated/dummy_models_controller_spec.rb +9 -0
  46. data/spec/dummy/app/blueprints/dummy_model_blueprint.rb +10 -0
  47. data/spec/dummy/app/controllers/blueprint/dummy_models_controller.rb +21 -0
  48. data/spec/dummy/app/controllers/blueprint_unpaginated/dummy_models_controller.rb +10 -0
  49. data/spec/dummy/app/controllers/html_notice/dummy_models_controller.rb +11 -0
  50. data/spec/dummy/app/controllers/owned/dummy_models_controller.rb +21 -0
  51. data/spec/dummy/app/controllers/parented/dummy_models_controller.rb +23 -0
  52. data/spec/dummy/app/controllers/scoped_symbol/dummy_models_controller.rb +11 -0
  53. data/spec/dummy/app/models/dummy_model.rb +4 -0
  54. data/spec/dummy/app/serializers/dummy_model_plain_serializer.rb +8 -0
  55. data/spec/dummy/app/views/html_notice/dummy_models/edit.html.erb +1 -0
  56. data/spec/dummy/config/routes.rb +24 -0
  57. data/spec/dummy_model_blueprint_spec.rb +16 -0
  58. data/spec/html_notice/dummy_models_controller_spec.rb +38 -0
  59. data/spec/owned/dummy_models_controller_spec.rb +12 -0
  60. data/spec/parented/dummy_models_controller_spec.rb +29 -0
  61. data/spec/scoped_symbol/dummy_models_controller_spec.rb +7 -0
  62. data/spec/simple_crud/action_context_spec.rb +34 -0
  63. data/spec/simple_crud/index_context_spec.rb +49 -0
  64. data/spec/simple_crud/relation_source_spec.rb +125 -0
  65. data/spec/simple_crud/rspec_config_spec.rb +13 -0
  66. data/spec/simple_crud/serializer_spec.rb +49 -0
  67. data/spec/simple_crud/show_context_spec.rb +5 -0
  68. data/spec/simple_crud_controller_spec.rb +15 -0
  69. metadata +25 -3
@@ -11,8 +11,11 @@ module SimpleCrud
11
11
  Rack::Utils::SYMBOL_TO_STATUS_CODE.invert[422]
12
12
  end
13
13
 
14
- def request_format(action)
15
- check_html(action) ? :html : :json
14
+ # Rails copies the controller-test `format:` kwarg into params, which
15
+ # collides with a model attribute named `format`. Only HTML requests
16
+ # need it, so leave params untouched for JSON.
17
+ def format_param(action)
18
+ check_html(action) ? :html : nil
16
19
  end
17
20
 
18
21
  # Extra params (e.g. a parent slug) added to every request, for nested
@@ -27,6 +27,8 @@ require_relative '../spec/shared_examples/simple_crud_for_destroy_with_finder'
27
27
  require_relative '../spec/shared_examples/simple_crud_not_found_with_finder'
28
28
  require_relative '../spec/shared_examples/simple_crud_for_new_with_build'
29
29
  require_relative '../spec/shared_examples/simple_crud_for_create_with_build'
30
+ require_relative '../spec/shared_examples/simple_crud_for_owned_resource'
31
+ require_relative '../spec/shared_examples/simple_crud_for_nested_resource'
30
32
  require_relative '../spec/shared_examples/authorization_adapter_authorize'
31
33
  require_relative '../spec/matchers/have_been_serialized_with'
32
34
 
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCrud
4
+ # Precedence: Blueprinter, then a plain class .render, then ActiveModelSerializers.
5
+ module Serializer
6
+ def self.render(serializer, record, options = {})
7
+ return record.as_json if serializer.nil?
8
+ return serializer.render_as_hash(record, options) if serializer.respond_to?(:render_as_hash)
9
+ return serializer.render(record, **options) if serializer.is_a?(Class) && serializer.respond_to?(:render)
10
+
11
+ serializer.new(record, options).as_json
12
+ end
13
+ end
14
+ end
@@ -21,7 +21,9 @@ module SimpleCrud
21
21
  record = find_record
22
22
  maybe_authorize(record)
23
23
  controller.instance_variable_set(:@record, record)
24
- block ? controller.instance_exec(record, &block) : record.as_json
24
+ return controller.instance_exec(record, &block) if block
25
+
26
+ SimpleCrud::Serializer.render(parameters[:serializer], record, serializer_options(record))
25
27
  end
26
28
  end
27
29
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'active_support/all'
4
4
  require_relative 'config'
5
+ require_relative 'serializer'
5
6
  require_relative 'cache_helpers'
7
+ require_relative 'relation_source'
6
8
  require_relative 'action_context'
7
9
  require_relative 'persistence_context'
8
10
  require_relative 'show_context'
@@ -31,16 +33,26 @@ module SimpleCrudController
31
33
  ### serializer: use a particular serializer (both each_serializer and serializer)
32
34
  ### html: render the action's ERB template instead of JSON (index/show/new/edit/create/update/destroy)
33
35
  ### finder: custom record lookup (Proc/lambda or Symbol) for :show/:update/:destroy/:edit
34
- ### scope: custom index scope (Proc/lambda taking current_user and optional params), overrides policy_scope
36
+ ### scope: custom index scope for :index, overriding policy_scope. A Proc/lambda run with the
37
+ ### controller as self (arity 0 no args, arity 1 the user, else user and params), or a Symbol
38
+ ### naming a model class method taking (current_user, params)
35
39
  ### build: custom record builder (Proc/lambda) for :new/:create, invoked with the controller as self
40
+ ### owned_by: association on Config.user_method for the default finder/build/index-scope
41
+ ### parent: nested-route parent for the same defaults; exclusive with owned_by:
42
+ ### parent_association: parent association when it differs from the controller model's plural
43
+ ### notice:/alert: flash on the html: create/update/destroy success redirect / failure re-render
36
44
  ### raise_on_invalid: use strict create!/update! semantics instead of returning 422
37
45
  ### A block given to simple_crud_for renders explicitly: it receives the records for :index,
38
46
  ### the record for :show/:new/:edit, or the record and a saved flag for :create/:update/:destroy
47
+ ### An Array of actions declares several at once with the same options
39
48
  def simple_crud_for(method, parameters = {}, &block)
49
+ return method.each { |name| simple_crud_for(name, parameters.dup, &block) } if method.is_a?(Array)
50
+
40
51
  parameters[:block] = true if block
41
52
  parameters = parameters_with_defaults(parameters)
42
53
  klass = simple_crud_controller_model
43
54
  check_valid_method(method)
55
+ check_relation_source(parameters)
44
56
  check_policies(parameters)
45
57
  check_serializer(parameters)
46
58
  define_method(method, send("crud_lambda_for_#{method}", klass, parameters, &block))
@@ -61,17 +73,22 @@ module SimpleCrudController
61
73
  end
62
74
 
63
75
  def parameters_with_defaults(parameters)
64
- defaults = {
65
- authorize: true, paginate: true, authenticate: true, authenticate_headers: nil,
66
- serializer: nil, html: false, finder: nil, scope: nil, build: nil, raise_on_invalid: false
67
- }
68
76
  defaults.merge(simple_crud_inherited_defaults).each do |key, value|
69
77
  parameters[key] = value unless parameters.key?(key)
70
78
  end
71
- parameters[:authenticate_headers] = parameters[:authenticate] if parameters[:authenticate_headers].nil?
79
+ parameters[:authenticate_headers] ||= parameters[:authenticate]
72
80
  parameters
73
81
  end
74
82
 
83
+ def defaults
84
+ {
85
+ authorize: true, paginate: true, authenticate: true, authenticate_headers: nil,
86
+ serializer: nil, serializer_options: nil, status: nil, after_persist: nil, html: false,
87
+ finder: nil, scope: nil, build: nil, owned_by: nil, parent: nil, parent_association: nil,
88
+ notice: nil, alert: nil, raise_on_invalid: false
89
+ }
90
+ end
91
+
75
92
  def write_metadata(method, parameters)
76
93
  @simple_crud_metadata ||= {}
77
94
  @simple_crud_metadata[method] = parameters
@@ -85,6 +102,12 @@ module SimpleCrudController
85
102
  raise ArgumentError, 'invalid method' unless %i[show index create update destroy new edit].include? method
86
103
  end
87
104
 
105
+ def check_relation_source(parameters)
106
+ return unless parameters[:owned_by] && parameters[:parent]
107
+
108
+ raise ArgumentError, 'owned_by: and parent: are mutually exclusive, they both scope the same action'
109
+ end
110
+
88
111
  def check_policies(parameters)
89
112
  return if !parameters.key?(:authorize) || !parameters[:authorize]
90
113
 
@@ -9,7 +9,8 @@ module SimpleCrud
9
9
  maybe_authorize(record)
10
10
  attrs = permitted_params
11
11
  persist = ->(bang:) { bang ? record.update!(attrs) : record.update(attrs) }
12
- options = { status: :ok, failure_template: :edit, redirect: parameters[:redirect] || record }
12
+ options = { status: parameters[:status] || :ok, failure_template: :edit,
13
+ redirect: parameters[:redirect] || record }
13
14
  persist_and_render(record, options, persist)
14
15
  end
15
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCrud
4
- VERSION = '0.5.0'
4
+ VERSION = '0.7.0'
5
5
  end
@@ -4,12 +4,29 @@ RSpec::Matchers.define :have_been_serialized_with do |serializer|
4
4
  # TODO: Add support for optional relations `has_many :zarasa, if: zarasa2`
5
5
  # TODO: Support multilevel serialization
6
6
  match do |json_response|
7
+ attributes = serializer_attributes(serializer)
8
+ reflections = serializer_reflections(serializer)
9
+
7
10
  [json_response].flatten.all? do |json_response_item|
8
- all_attributes_included?(serializer._attributes, json_response_item) &&
9
- all_attributes_included?(serializer._reflections.keys, json_response_item)
11
+ all_attributes_included?(attributes, json_response_item) &&
12
+ all_attributes_included?(reflections, json_response_item)
10
13
  end
11
14
  end
12
15
 
16
+ # Blueprinter blueprints expose their fields through the view collection;
17
+ # ActiveModelSerializers expose `._attributes` / `._reflections`.
18
+ def serializer_attributes(serializer)
19
+ return serializer._attributes unless serializer.respond_to?(:view_collection)
20
+
21
+ serializer.view_collection.fields_for(:default).map(&:name)
22
+ end
23
+
24
+ def serializer_reflections(serializer)
25
+ return serializer._reflections.keys unless serializer.respond_to?(:view_collection)
26
+
27
+ []
28
+ end
29
+
13
30
  failure_message do |json_response|
14
31
  "expected attributes of #{serializer} serializer to be included in #{json_response}"
15
32
  end
@@ -4,7 +4,7 @@ RSpec.shared_examples 'simple crud for create' do
4
4
  describe 'POST #create' do
5
5
  context 'without authenticated user' do
6
6
  subject!(:req) do
7
- post :create, params: with_route_params(body_params(params_for(model_class))), format: request_format(:create)
7
+ post :create, params: with_route_params(body_params(params_for(model_class))), format: format_param(:create)
8
8
  end
9
9
 
10
10
  include_examples 'unauthorized when not logged in' if check_authenticate(:create)
@@ -16,7 +16,7 @@ RSpec.shared_examples 'simple crud for create' do
16
16
 
17
17
  before do
18
18
  make_policies_fail(:create)
19
- post :create, params: with_route_params(body_params(model_params)), format: request_format(:create)
19
+ post :create, params: with_route_params(body_params(model_params)), format: format_param(:create)
20
20
  end
21
21
 
22
22
  it 'fails with forbidden' do
@@ -30,7 +30,7 @@ RSpec.shared_examples 'simple crud for create' do
30
30
  let(:create_params) { model_params.merge(owner_params) }
31
31
 
32
32
  before do
33
- post :create, params: with_route_params(body_params(create_params)), format: request_format(:create)
33
+ post :create, params: with_route_params(body_params(create_params)), format: format_param(:create)
34
34
  end
35
35
 
36
36
  if check_html(:create)
@@ -38,8 +38,8 @@ RSpec.shared_examples 'simple crud for create' do
38
38
  expect(response).to have_http_status(:found)
39
39
  end
40
40
  else
41
- it 'response with 201 status code' do
42
- expect(response).to have_http_status(:created)
41
+ it 'response with the configured status code' do
42
+ expect(response).to have_http_status(check_status(:create) || :created)
43
43
  end
44
44
 
45
45
  it 'creates an article with valid attributes' do
@@ -58,7 +58,7 @@ RSpec.shared_examples 'simple crud for create' do
58
58
  include_context 'with authenticated user' if check_authenticate(:create)
59
59
 
60
60
  before do
61
- post :create, params: with_route_params(body_params(owner_params)), format: request_format(:create)
61
+ post :create, params: with_route_params(body_params(owner_params)), format: format_param(:create)
62
62
  end
63
63
 
64
64
  unless check_block(:create)
@@ -7,7 +7,7 @@ RSpec.shared_examples 'simple crud for create with block' do
7
7
  let(:create_params) { model_params.merge(owner_params) }
8
8
 
9
9
  before do
10
- post :create, params: with_route_params(body_params(create_params)), format: request_format(:create)
10
+ post :create, params: with_route_params(body_params(create_params)), format: format_param(:create)
11
11
  end
12
12
 
13
13
  it 'passes the saved record to the block' do
@@ -24,7 +24,7 @@ RSpec.shared_examples 'simple crud for create with block' do
24
24
  include_context 'with authenticated user' if check_authenticate(:create)
25
25
 
26
26
  before do
27
- post :create, params: with_route_params(body_params(owner_params)), format: request_format(:create)
27
+ post :create, params: with_route_params(body_params(owner_params)), format: format_param(:create)
28
28
  end
29
29
 
30
30
  it 'does not create a model' do
@@ -5,7 +5,7 @@ RSpec.shared_examples 'simple crud for create with build' do
5
5
  include_context 'with authenticated user' if check_authenticate(:create)
6
6
 
7
7
  before do
8
- post :create, params: with_route_params(body_params(params_for(model_class))), format: request_format(:create)
8
+ post :create, params: with_route_params(body_params(params_for(model_class))), format: format_param(:create)
9
9
  end
10
10
 
11
11
  it 'creates the record owned by the current user' do
@@ -5,7 +5,7 @@ RSpec.shared_examples 'simple crud for destroy' do
5
5
  context 'without authenticated user' do
6
6
  subject!(:req) do
7
7
  delete :destroy, params: with_route_params(record_param(:destroy, nil, not_found: true)),
8
- format: request_format(:destroy)
8
+ format: format_param(:destroy)
9
9
  end
10
10
 
11
11
  include_examples 'unauthorized when not logged in' if check_authenticate(:destroy)
@@ -17,7 +17,7 @@ RSpec.shared_examples 'simple crud for destroy' do
17
17
 
18
18
  before do
19
19
  make_policies_fail(:destroy)
20
- delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
20
+ delete :destroy, params: with_route_params(record_param(:destroy, model)), format: format_param(:destroy)
21
21
  end
22
22
 
23
23
  it 'fails with forbidden' do
@@ -31,7 +31,7 @@ RSpec.shared_examples 'simple crud for destroy' do
31
31
 
32
32
  before do
33
33
  model
34
- delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
34
+ delete :destroy, params: with_route_params(record_param(:destroy, model)), format: format_param(:destroy)
35
35
  end
36
36
 
37
37
  if check_html(:destroy)
@@ -40,8 +40,8 @@ RSpec.shared_examples 'simple crud for destroy' do
40
40
  expect(model_class_object.exists?(model.id)).to be false
41
41
  end
42
42
  else
43
- it 'response with 200 status code' do
44
- expect(response).to have_http_status(:ok)
43
+ it 'response with the configured status code' do
44
+ expect(response).to have_http_status(check_status(:destroy) || :ok)
45
45
  end
46
46
  end
47
47
  end
@@ -53,7 +53,7 @@ RSpec.shared_examples 'simple crud for destroy' do
53
53
  model
54
54
  allow(model).to receive(:destroy).and_return(false)
55
55
  allow_any_instance_of(SimpleCrud::DestroyContext).to receive(:find_record).and_return(model)
56
- delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
56
+ delete :destroy, params: with_route_params(record_param(:destroy, model)), format: format_param(:destroy)
57
57
  end
58
58
 
59
59
  it 'keeps the record' do
@@ -77,7 +77,7 @@ RSpec.shared_examples 'simple crud for destroy' do
77
77
  before do
78
78
  model
79
79
  delete :destroy, params: with_route_params(record_param(:destroy, nil, not_found: true)),
80
- format: request_format(:destroy)
80
+ format: format_param(:destroy)
81
81
  end
82
82
 
83
83
  it 'responds with not found status' do
@@ -92,7 +92,7 @@ RSpec.shared_examples 'simple crud for destroy' do
92
92
  before do
93
93
  model
94
94
  make_policies_fail(:destroy)
95
- delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
95
+ delete :destroy, params: with_route_params(record_param(:destroy, model)), format: format_param(:destroy)
96
96
  end
97
97
 
98
98
  it 'responds with forbidden status' do
@@ -7,7 +7,7 @@ RSpec.shared_examples 'simple crud for destroy with block' do
7
7
  let!(:record) { model }
8
8
 
9
9
  before do
10
- delete :destroy, params: with_route_params(record_param(:destroy, record)), format: request_format(:destroy)
10
+ delete :destroy, params: with_route_params(record_param(:destroy, record)), format: format_param(:destroy)
11
11
  end
12
12
 
13
13
  it 'passes the destroyed record to the block' do
@@ -8,7 +8,7 @@ RSpec.shared_examples 'simple crud for destroy with finder' do
8
8
  before do
9
9
  model
10
10
  delete :destroy, params: with_route_params(finder_key => model.public_send(finder_key)),
11
- format: request_format(:destroy)
11
+ format: format_param(:destroy)
12
12
  end
13
13
 
14
14
  it 'destroys the model' do
@@ -24,8 +24,8 @@ RSpec.shared_examples 'simple crud for destroy with finder' do
24
24
  expect(response).to have_http_status(:found)
25
25
  end
26
26
  else
27
- it 'responds with ok status' do
28
- expect(response).to have_http_status(:ok)
27
+ it 'responds with the configured status code' do
28
+ expect(response).to have_http_status(check_status(:destroy) || :ok)
29
29
  end
30
30
  end
31
31
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.shared_examples 'simple crud for edit' do
4
4
  describe 'GET #edit' do
5
- subject(:edit_request) { get :edit, params: with_route_params(edit_params), format: request_format(:edit) }
5
+ subject(:edit_request) { get :edit, params: with_route_params(edit_params), format: format_param(:edit) }
6
6
 
7
7
  before do
8
8
  model
@@ -10,7 +10,7 @@ RSpec.shared_examples 'simple crud for edit' do
10
10
 
11
11
  context 'without authenticated user' do
12
12
  subject!(:req) do
13
- get :edit, params: with_route_params(record_param(:edit, model)), format: request_format(:edit)
13
+ get :edit, params: with_route_params(record_param(:edit, model)), format: format_param(:edit)
14
14
  end
15
15
 
16
16
  include_examples 'unauthorized when not logged in' if check_authenticate(:edit)
@@ -14,7 +14,7 @@ RSpec.shared_examples 'simple crud for index' do
14
14
 
15
15
  before do
16
16
  make_policies_succeed(:index) if check_authorize(:index)
17
- get :index, params: with_route_params({}), format: request_format(:index)
17
+ get :index, params: with_route_params({}), format: format_param(:index)
18
18
  end
19
19
 
20
20
  if check_html(:index)
@@ -25,7 +25,7 @@ RSpec.shared_examples 'simple crud for index' do
25
25
  end
26
26
 
27
27
  it 'renders the correct paginated models' do
28
- expect(response_body['page'].map { |a| a['id'] }).to eq(model_class_object.all.map(&:id))
28
+ expect(response_body['page'].map { |a| a['id'] }).to match_array(model_class_object.all.map(&:id))
29
29
  end
30
30
  else
31
31
  it 'renders unpaginated models correctly serialized' do
@@ -33,7 +33,7 @@ RSpec.shared_examples 'simple crud for index' do
33
33
  end
34
34
 
35
35
  it 'renders the correct unpaginated models' do
36
- expect(response_body.map { |a| a['id'] }).to eq(model_class_object.all.map(&:id))
36
+ expect(response_body.map { |a| a['id'] }).to match_array(model_class_object.all.map(&:id))
37
37
  end
38
38
  end
39
39
  end
@@ -8,7 +8,7 @@ RSpec.shared_examples 'simple crud for index with block' do
8
8
 
9
9
  before do
10
10
  created_models
11
- get :index, params: with_route_params({}), format: request_format(:index)
11
+ get :index, params: with_route_params({}), format: format_param(:index)
12
12
  end
13
13
 
14
14
  if check_html(:index)
@@ -13,7 +13,7 @@ RSpec.shared_examples 'simple crud for index with scope' do
13
13
  make_policies_succeed(:index) if check_authorize(:index)
14
14
  my_models
15
15
  other_models
16
- get :index, params: with_route_params(request_params), format: request_format(:index)
16
+ get :index, params: with_route_params(request_params), format: format_param(:index)
17
17
  end
18
18
 
19
19
  if check_html(:index)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # For a parent: controller. The parent in route_params must match the record's owner_association.
4
+ RSpec.shared_examples 'simple crud for nested resource' do
5
+ describe 'parent: scoping' do
6
+ include_context 'with authenticated user' if check_authenticate(:show)
7
+
8
+ it 'finds a record inside the parent' do
9
+ get :show, params: with_route_params(record_param(:show, model)), format: format_param(:show)
10
+
11
+ expect(response).to have_http_status(:ok)
12
+ end
13
+
14
+ it 'returns not found for a record outside the parent' do
15
+ outside = create_record(model_class, model_attributes.merge(owner_association => other_user))
16
+ get :show, params: with_route_params(record_param(:show, outside)), format: format_param(:show)
17
+
18
+ expect(response).to have_http_status(:not_found)
19
+ end
20
+ end
21
+ end
@@ -10,7 +10,7 @@ RSpec.shared_examples 'simple crud for new' do
10
10
 
11
11
  before do
12
12
  make_policies_succeed(:new) if check_authorize(:new)
13
- get :new, params: with_route_params({}), format: request_format(:new)
13
+ get :new, params: with_route_params({}), format: format_param(:new)
14
14
  end
15
15
 
16
16
  it 'responds with ok status' do
@@ -6,7 +6,7 @@ RSpec.shared_examples 'simple crud for new with block' do
6
6
 
7
7
  before do
8
8
  make_policies_succeed(:new) if check_authorize(:new)
9
- get :new, params: with_route_params({}), format: request_format(:new)
9
+ get :new, params: with_route_params({}), format: format_param(:new)
10
10
  end
11
11
 
12
12
  if check_html(:new)
@@ -5,7 +5,7 @@ RSpec.shared_examples 'simple crud for new with build' do
5
5
  include_context 'with authenticated user' if check_authenticate(:new)
6
6
 
7
7
  before do
8
- get :new, params: with_route_params({}), format: request_format(:new)
8
+ get :new, params: with_route_params({}), format: format_param(:new)
9
9
  end
10
10
 
11
11
  it 'builds the record owned by the current user', :aggregate_failures do
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Include alongside the per-action examples for a controller using owned_by:.
4
+ RSpec.shared_examples 'simple crud for owned resource' do
5
+ describe 'owned_by: scoping' do
6
+ include_context 'with authenticated user'
7
+
8
+ let!(:someone_elses_model) do
9
+ create_record(model_class, model_attributes.merge(owner_association => other_user))
10
+ end
11
+
12
+ it 'returns not found for a record owned by another user' do
13
+ get :show, params: with_route_params(record_param(:show, someone_elses_model)), format: format_param(:show)
14
+
15
+ expect(response).to have_http_status(:not_found)
16
+ end
17
+
18
+ it 'ignores an attempt to update a record owned by another user', :aggregate_failures do
19
+ params = with_route_params(record_param(:update, someone_elses_model)).merge(body_params(model_params))
20
+ patch :update, params: params, format: format_param(:update)
21
+
22
+ expect(response).to have_http_status(:not_found)
23
+ expect(someone_elses_model.reload.public_send(required_attribute))
24
+ .not_to eq(model_params[required_attribute])
25
+ end
26
+
27
+ it 'attributes a created record to the current user regardless of the request', :aggregate_failures do
28
+ body = body_params(model_params.merge(owner_association => other_user.id))
29
+ post :create, params: with_route_params(body), format: format_param(:create)
30
+
31
+ expect(response).to have_http_status(check_status(:create) || :created)
32
+ expect(model_class_object.last.public_send(owner_association)).to eq(current_user)
33
+ end
34
+ end
35
+ end
@@ -2,14 +2,14 @@
2
2
 
3
3
  RSpec.shared_examples 'simple crud for show' do
4
4
  describe 'GET #show' do
5
- subject(:show_request) { get :show, params: with_route_params(show_params), format: request_format(:show) }
5
+ subject(:show_request) { get :show, params: with_route_params(show_params), format: format_param(:show) }
6
6
 
7
7
  before do
8
8
  model
9
9
  end
10
10
 
11
11
  context 'without authenticated user' do
12
- subject!(:req) { get :show, params: with_route_params(record_param(:show, model)), format: request_format(:show) }
12
+ subject!(:req) { get :show, params: with_route_params(record_param(:show, model)), format: format_param(:show) }
13
13
 
14
14
  include_examples 'unauthorized when not logged in' if check_authenticate(:show)
15
15
  end
@@ -8,7 +8,7 @@ RSpec.shared_examples 'simple crud for show with block' do
8
8
 
9
9
  before do
10
10
  make_policies_succeed(:show) if check_authorize(:show)
11
- get :show, params: with_route_params(record_param(:show, record)), format: request_format(:show)
11
+ get :show, params: with_route_params(record_param(:show, record)), format: format_param(:show)
12
12
  end
13
13
 
14
14
  if check_html(:show)
@@ -8,7 +8,7 @@ RSpec.shared_examples 'simple crud for show with finder' do
8
8
  before do
9
9
  model
10
10
  get :show, params: with_route_params(finder_key => model.public_send(finder_key)),
11
- format: request_format(:show)
11
+ format: format_param(:show)
12
12
  end
13
13
 
14
14
  if check_block(:show)
@@ -5,7 +5,7 @@ RSpec.shared_examples 'simple crud for update' do
5
5
  context 'without authenticated user' do
6
6
  subject!(:req) do
7
7
  update_params = body_params(params_for(model_class)).merge(record_param(:update, model))
8
- put :update, params: with_route_params(update_params), format: request_format(:update)
8
+ put :update, params: with_route_params(update_params), format: format_param(:update)
9
9
  end
10
10
 
11
11
  include_examples 'unauthorized when not logged in' if check_authenticate(:update)
@@ -18,7 +18,7 @@ RSpec.shared_examples 'simple crud for update' do
18
18
  before do
19
19
  make_policies_fail(:update)
20
20
  put :update, params: with_route_params(body_params(model_params).merge(record_param(:update, model))),
21
- format: request_format(:update)
21
+ format: format_param(:update)
22
22
  end
23
23
 
24
24
  it 'fails with forbidden' do
@@ -34,7 +34,7 @@ RSpec.shared_examples 'simple crud for update' do
34
34
 
35
35
  before do
36
36
  model
37
- put :update, params: with_route_params(update_params), format: request_format(:update)
37
+ put :update, params: with_route_params(update_params), format: format_param(:update)
38
38
  end
39
39
 
40
40
  if check_html(:update)
@@ -42,8 +42,8 @@ RSpec.shared_examples 'simple crud for update' do
42
42
  expect(response).to have_http_status(:found)
43
43
  end
44
44
  else
45
- it 'response with 200 status code' do
46
- expect(response).to have_http_status(:ok)
45
+ it 'response with the configured status code' do
46
+ expect(response).to have_http_status(check_status(:update) || :ok)
47
47
  end
48
48
 
49
49
  it 'updates an model' do
@@ -66,7 +66,7 @@ RSpec.shared_examples 'simple crud for update' do
66
66
 
67
67
  before do
68
68
  put :update, params: with_route_params(record_param(:update, nil, not_found: true)),
69
- format: request_format(:update)
69
+ format: format_param(:update)
70
70
  end
71
71
 
72
72
  it 'response with 404 status code' do
@@ -86,7 +86,7 @@ RSpec.shared_examples 'simple crud for update' do
86
86
  model
87
87
  put :update, params: with_route_params(body_params({ required_attribute => nil }.merge(owner_params))
88
88
  .merge(record_param(:update, model))),
89
- format: request_format(:update)
89
+ format: format_param(:update)
90
90
  end
91
91
 
92
92
  unless check_block(:update)
@@ -12,7 +12,7 @@ RSpec.shared_examples 'simple crud for update with finder' do
12
12
 
13
13
  before do
14
14
  model
15
- put :update, params: update_params, format: request_format(:update)
15
+ put :update, params: update_params, format: format_param(:update)
16
16
  end
17
17
 
18
18
  it 'updates the model' do
@@ -4,7 +4,7 @@ RSpec.shared_examples 'simple crud not found with finder' do |http_method, actio
4
4
  context 'when the model does not exist' do
5
5
  before do
6
6
  send(http_method, action, params: with_route_params(finder_key => "nonexistent-#{finder_key}"),
7
- format: request_format(action))
7
+ format: format_param(action))
8
8
  end
9
9
 
10
10
  it 'responds with not found status' do
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Blueprint::DummyModelsController, type: :controller do
6
+ let(:serializer) { DummyModelBlueprint }
7
+
8
+ include_examples 'simple crud for index'
9
+ include_examples 'simple crud for show'
10
+ include_examples 'simple crud for create'
11
+ include_examples 'simple crud for update'
12
+ include_examples 'simple crud for destroy'
13
+
14
+ describe 'GET #show' do
15
+ include_context 'with authenticated user'
16
+
17
+ let!(:record) { create(:dummy_model, user: current_user) }
18
+
19
+ it 'passes serializer_options to the blueprint' do
20
+ get :show, params: { id: record.id }
21
+
22
+ expect(response.parsed_body['url']).to eq(dummy_model_url(record))
23
+ end
24
+ end
25
+ end