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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/Gemfile +5 -1
- data/README.md +104 -8
- data/lib/simple_crud/action_context.rb +45 -10
- data/lib/simple_crud/create_context.rb +2 -1
- data/lib/simple_crud/destroy_context.rb +2 -1
- data/lib/simple_crud/index_context.rb +30 -12
- data/lib/simple_crud/pagination/wor_paginate_adapter.rb +7 -1
- data/lib/simple_crud/persistence_context.rb +33 -6
- data/lib/simple_crud/relation_source.rb +107 -0
- data/lib/simple_crud/rspec/config.rb +6 -1
- data/lib/simple_crud/rspec/helpers/authentication.rb +2 -1
- data/lib/simple_crud/rspec/helpers/controller_options.rb +2 -2
- data/lib/simple_crud/rspec/helpers/requests.rb +5 -2
- data/lib/simple_crud/rspec.rb +2 -0
- data/lib/simple_crud/serializer.rb +14 -0
- data/lib/simple_crud/show_context.rb +3 -1
- data/lib/simple_crud/simple_crud_controller.rb +29 -6
- data/lib/simple_crud/update_context.rb +2 -1
- data/lib/simple_crud/version.rb +1 -1
- data/lib/spec/matchers/have_been_serialized_with.rb +19 -2
- data/lib/spec/shared_examples/simple_crud_for_create.rb +6 -6
- data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +2 -2
- data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_destroy.rb +8 -8
- data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +3 -3
- data/lib/spec/shared_examples/simple_crud_for_edit.rb +2 -2
- data/lib/spec/shared_examples/simple_crud_for_index.rb +3 -3
- data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_nested_resource.rb +21 -0
- data/lib/spec/shared_examples/simple_crud_for_new.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_owned_resource.rb +35 -0
- data/lib/spec/shared_examples/simple_crud_for_show.rb +2 -2
- data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_update.rb +7 -7
- data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +1 -1
- data/spec/blueprint/dummy_models_controller_spec.rb +25 -0
- data/spec/blueprint_unpaginated/dummy_models_controller_spec.rb +9 -0
- data/spec/dummy/app/blueprints/dummy_model_blueprint.rb +10 -0
- data/spec/dummy/app/controllers/blueprint/dummy_models_controller.rb +21 -0
- data/spec/dummy/app/controllers/blueprint_unpaginated/dummy_models_controller.rb +10 -0
- data/spec/dummy/app/controllers/html_notice/dummy_models_controller.rb +11 -0
- data/spec/dummy/app/controllers/owned/dummy_models_controller.rb +21 -0
- data/spec/dummy/app/controllers/parented/dummy_models_controller.rb +23 -0
- data/spec/dummy/app/controllers/scoped_symbol/dummy_models_controller.rb +11 -0
- data/spec/dummy/app/models/dummy_model.rb +4 -0
- data/spec/dummy/app/serializers/dummy_model_plain_serializer.rb +8 -0
- data/spec/dummy/app/views/html_notice/dummy_models/edit.html.erb +1 -0
- data/spec/dummy/config/routes.rb +24 -0
- data/spec/dummy_model_blueprint_spec.rb +16 -0
- data/spec/html_notice/dummy_models_controller_spec.rb +38 -0
- data/spec/owned/dummy_models_controller_spec.rb +12 -0
- data/spec/parented/dummy_models_controller_spec.rb +29 -0
- data/spec/scoped_symbol/dummy_models_controller_spec.rb +7 -0
- data/spec/simple_crud/action_context_spec.rb +34 -0
- data/spec/simple_crud/index_context_spec.rb +49 -0
- data/spec/simple_crud/relation_source_spec.rb +125 -0
- data/spec/simple_crud/rspec_config_spec.rb +13 -0
- data/spec/simple_crud/serializer_spec.rb +49 -0
- data/spec/simple_crud/show_context_spec.rb +5 -0
- data/spec/simple_crud_controller_spec.rb +15 -0
- metadata +25 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Blueprint
|
|
4
|
+
class DummyModelsController < ApplicationController
|
|
5
|
+
include Wor::Paginate
|
|
6
|
+
include Pundit::Authorization
|
|
7
|
+
extend SimpleCrudController
|
|
8
|
+
|
|
9
|
+
def dummy_model_params
|
|
10
|
+
params.permit(:name, :something, :user_id, :slug)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
SERIALIZER_OPTIONS = -> { { url_builder: self } }
|
|
14
|
+
|
|
15
|
+
simple_crud_for :index, serializer: DummyModelBlueprint
|
|
16
|
+
simple_crud_for :show, serializer: DummyModelBlueprint, serializer_options: SERIALIZER_OPTIONS
|
|
17
|
+
simple_crud_for :create, serializer: DummyModelBlueprint, serializer_options: SERIALIZER_OPTIONS
|
|
18
|
+
simple_crud_for :update, serializer: DummyModelBlueprint, serializer_options: SERIALIZER_OPTIONS
|
|
19
|
+
simple_crud_for :destroy, serializer: DummyModelBlueprint, serializer_options: SERIALIZER_OPTIONS
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BlueprintUnpaginated
|
|
4
|
+
class DummyModelsController < ApplicationController
|
|
5
|
+
include Pundit::Authorization
|
|
6
|
+
extend SimpleCrudController
|
|
7
|
+
|
|
8
|
+
simple_crud_for :index, paginate: false, serializer: DummyModelBlueprint
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HtmlNotice
|
|
4
|
+
class DummyModelsController < HtmlModes::BaseController
|
|
5
|
+
simple_crud_for :create, html: true, authenticate: false, authorize: false,
|
|
6
|
+
notice: 'created', alert: 'invalid'
|
|
7
|
+
simple_crud_for :update, html: true, authenticate: false, authorize: false,
|
|
8
|
+
notice: 'updated', alert: 'invalid'
|
|
9
|
+
simple_crud_for :destroy, html: true, authenticate: false, authorize: false, notice: 'removed'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Owned
|
|
4
|
+
class DummyModelsController < ApplicationController
|
|
5
|
+
include Wor::Paginate
|
|
6
|
+
include Pundit::Authorization
|
|
7
|
+
extend SimpleCrudController
|
|
8
|
+
|
|
9
|
+
# No :user_id: a permitted FK would let a request override the owner set by owned_by:.
|
|
10
|
+
def dummy_model_params
|
|
11
|
+
params.permit(:name, :something, :slug)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
simple_crud_defaults owned_by: :dummy_models, authorize: false
|
|
15
|
+
simple_crud_for :index
|
|
16
|
+
simple_crud_for :show
|
|
17
|
+
simple_crud_for :create
|
|
18
|
+
simple_crud_for :update
|
|
19
|
+
simple_crud_for :destroy
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Parented
|
|
4
|
+
class DummyModelsController < ApplicationController
|
|
5
|
+
include Wor::Paginate
|
|
6
|
+
include Pundit::Authorization
|
|
7
|
+
extend SimpleCrudController
|
|
8
|
+
|
|
9
|
+
# The default association for this model is @user.dummy_models.
|
|
10
|
+
def owner = @owner ||= User.find(params[:owner_id])
|
|
11
|
+
|
|
12
|
+
def dummy_model_params
|
|
13
|
+
params.permit(:name, :something, :slug)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
simple_crud_defaults parent: :owner, authorize: false
|
|
17
|
+
simple_crud_for :index
|
|
18
|
+
simple_crud_for :show
|
|
19
|
+
simple_crud_for :create
|
|
20
|
+
simple_crud_for :update
|
|
21
|
+
simple_crud_for :destroy
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Edit
|
data/spec/dummy/config/routes.rb
CHANGED
|
@@ -40,6 +40,14 @@ Rails.application.routes.draw do
|
|
|
40
40
|
resources :dummy_models, only: :index
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
namespace :owned do
|
|
44
|
+
resources :dummy_models
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
namespace :scoped_symbol do
|
|
48
|
+
resources :dummy_models, only: :index
|
|
49
|
+
end
|
|
50
|
+
|
|
43
51
|
namespace :unpaginated_scoped do
|
|
44
52
|
resources :dummy_models, only: :index
|
|
45
53
|
end
|
|
@@ -88,6 +96,10 @@ Rails.application.routes.draw do
|
|
|
88
96
|
resources :dummy_models, only: :destroy
|
|
89
97
|
end
|
|
90
98
|
|
|
99
|
+
namespace :html_notice do
|
|
100
|
+
resources :dummy_models, only: %i[create update destroy]
|
|
101
|
+
end
|
|
102
|
+
|
|
91
103
|
namespace :block_destroy do
|
|
92
104
|
resources :dummy_models, only: :destroy
|
|
93
105
|
end
|
|
@@ -120,9 +132,21 @@ Rails.application.routes.draw do
|
|
|
120
132
|
resources :dummy_models, only: :create
|
|
121
133
|
end
|
|
122
134
|
|
|
135
|
+
namespace :blueprint do
|
|
136
|
+
resources :dummy_models
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
namespace :blueprint_unpaginated do
|
|
140
|
+
resources :dummy_models, only: :index
|
|
141
|
+
end
|
|
142
|
+
|
|
123
143
|
namespace :nested_route do
|
|
124
144
|
resources :classrooms, only: [], param: :slug do
|
|
125
145
|
resources :dummy_models, param: :slug, only: %i[show create destroy]
|
|
126
146
|
end
|
|
127
147
|
end
|
|
148
|
+
|
|
149
|
+
namespace :parented do
|
|
150
|
+
resources :dummy_models
|
|
151
|
+
end
|
|
128
152
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe DummyModelBlueprint do
|
|
6
|
+
let(:dummy_model) { DummyModel.new(id: 3, name: 'widget', something: 'thing', user_id: 7) }
|
|
7
|
+
let(:full_response) { described_class.render_as_hash(dummy_model) }
|
|
8
|
+
|
|
9
|
+
it 'matches a response including all blueprint attributes' do
|
|
10
|
+
expect(full_response).to have_been_serialized_with(described_class)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'does not match a response missing blueprint attributes' do
|
|
14
|
+
expect({ 'id' => dummy_model.id }).not_to have_been_serialized_with(described_class)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe HtmlNotice::DummyModelsController, type: :controller do
|
|
6
|
+
describe 'POST #create' do
|
|
7
|
+
it 'flashes the notice on success' do
|
|
8
|
+
post :create, params: attributes_for(:dummy_model).merge(user_id: create(:user).id), format: :html
|
|
9
|
+
|
|
10
|
+
expect(flash[:notice]).to eq('created')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'PUT #update' do
|
|
15
|
+
let(:record) { create(:dummy_model) }
|
|
16
|
+
|
|
17
|
+
it 'flashes the notice on success' do
|
|
18
|
+
put :update, params: { id: record.id, name: 'renamed' }, format: :html
|
|
19
|
+
|
|
20
|
+
expect(flash[:notice]).to eq('updated')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'flashes the alert on failure' do
|
|
24
|
+
put :update, params: { id: record.id, name: nil }, format: :html
|
|
25
|
+
|
|
26
|
+
expect(flash.now[:alert]).to eq('invalid')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe 'DELETE #destroy' do
|
|
31
|
+
it 'flashes the notice on success' do
|
|
32
|
+
record = create(:dummy_model)
|
|
33
|
+
delete :destroy, params: { id: record.id }, format: :html
|
|
34
|
+
|
|
35
|
+
expect(flash[:notice]).to eq('removed')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Owned::DummyModelsController, type: :controller do
|
|
6
|
+
include_examples 'simple crud for show'
|
|
7
|
+
include_examples 'simple crud for create'
|
|
8
|
+
include_examples 'simple crud for update'
|
|
9
|
+
include_examples 'simple crud for destroy'
|
|
10
|
+
include_examples 'simple crud for index'
|
|
11
|
+
include_examples 'simple crud for owned resource'
|
|
12
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Parented::DummyModelsController, simple_crud: {
|
|
6
|
+
route_params: -> { { owner_id: current_user.id } }
|
|
7
|
+
}, type: :controller do
|
|
8
|
+
let(:owner) { create(:user) }
|
|
9
|
+
|
|
10
|
+
include_examples 'simple crud for nested resource'
|
|
11
|
+
|
|
12
|
+
describe 'POST #create' do
|
|
13
|
+
include_context 'with authenticated user'
|
|
14
|
+
|
|
15
|
+
it 'builds the record through the parent association' do
|
|
16
|
+
post :create, params: { owner_id: owner.id, name: 'from parent' }, format: :json
|
|
17
|
+
|
|
18
|
+
expect(owner.dummy_models.find(response.parsed_body['id']).name).to eq('from parent')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe 'POST #create with parent: and owned_by: together' do
|
|
23
|
+
it 'raises, they are the same relation source' do
|
|
24
|
+
expect do
|
|
25
|
+
described_class.simple_crud_for(:show, parent: :owner, owned_by: :dummy_models)
|
|
26
|
+
end.to raise_error(ArgumentError, /mutually exclusive/)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -19,4 +19,38 @@ describe SimpleCrud::ActionContext do
|
|
|
19
19
|
expect(key).to eq('dummy_model:show:v1:/dummy_models/1')
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
|
+
|
|
23
|
+
describe '#build_record' do
|
|
24
|
+
it 'raises not found when owned_by has no owner to build from' do
|
|
25
|
+
ctx = described_class.new(double(params: {}), DummyModel, { owned_by: :dummy_models })
|
|
26
|
+
expect { ctx.send(:build_record) }.to raise_error(ActiveRecord::RecordNotFound, /no owner/)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'builds through the scoped relation when configured' do
|
|
30
|
+
relation = double(build: :built)
|
|
31
|
+
ctrl = double(params: {})
|
|
32
|
+
ctrl.instance_variable_set(:@owner, double(dummy_models: relation))
|
|
33
|
+
|
|
34
|
+
expect(described_class.new(ctrl, DummyModel, { parent: :owner }).send(:build_record)).to eq(:built)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#serializer_options' do
|
|
39
|
+
let(:record) { DummyModel.new(name: 'widget') }
|
|
40
|
+
|
|
41
|
+
it 'returns an empty hash when no callable is configured' do
|
|
42
|
+
ctx = described_class.new(double, DummyModel, {})
|
|
43
|
+
expect(ctx.send(:serializer_options, record)).to eq({})
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'calls a zero-arity callable without the record' do
|
|
47
|
+
ctx = described_class.new(double, DummyModel, { serializer_options: -> { { prefix: :none } } })
|
|
48
|
+
expect(ctx.send(:serializer_options, record)).to eq(prefix: :none)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'calls an arity-one callable with the record' do
|
|
52
|
+
ctx = described_class.new(double, DummyModel, { serializer_options: ->(record) { { name: record.name } } })
|
|
53
|
+
expect(ctx.send(:serializer_options, record)).to eq(name: 'widget')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
22
56
|
end
|
|
@@ -27,5 +27,54 @@ describe SimpleCrud::IndexContext do
|
|
|
27
27
|
ctx = described_class.new(ctrl, DummyModel, { scope: ->(u) { u }, authorize: false })
|
|
28
28
|
expect(ctx.send(:call_scope)).to eq(admin)
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
it 'runs an arity-zero scope with the controller as self and no arguments' do
|
|
32
|
+
ctrl = double(params: {}, listing: DummyModel.none)
|
|
33
|
+
ctx = described_class.new(ctrl, DummyModel, { scope: -> { listing }, authorize: false })
|
|
34
|
+
|
|
35
|
+
expect(ctx.send(:call_scope)).to eq(DummyModel.none)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'dispatches a Symbol scope to the model with user and params' do
|
|
39
|
+
ctrl = double(params: { status: 'active' })
|
|
40
|
+
allow(DummyModel).to receive(:visible_to).with(nil, { status: 'active' }).and_return(DummyModel.none)
|
|
41
|
+
ctx = described_class.new(ctrl, DummyModel, { scope: :visible_to, authorize: false })
|
|
42
|
+
|
|
43
|
+
expect(ctx.send(:call_scope)).to eq(DummyModel.none)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#index_relation' do
|
|
48
|
+
let(:owned_records) { DummyModel.none }
|
|
49
|
+
|
|
50
|
+
it 'scopes to the owned_by relation even when authorize is on' do
|
|
51
|
+
ctrl = double(params: {}, current_user: double(dummy_models: owned_records))
|
|
52
|
+
ctx = described_class.new(ctrl, DummyModel, { owned_by: :dummy_models, authorize: true })
|
|
53
|
+
|
|
54
|
+
expect(ctx.send(:index_relation)).to eq(owned_records)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'returns no records when there is no user to own them' do
|
|
58
|
+
ctx = described_class.new(double(params: {}), DummyModel, { owned_by: :dummy_models, authorize: false })
|
|
59
|
+
|
|
60
|
+
expect(ctx.send(:index_relation)).to be_empty
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'scopes to the parent relation' do
|
|
64
|
+
parent = double(dummy_models: owned_records)
|
|
65
|
+
ctrl = double(params: {})
|
|
66
|
+
ctrl.instance_variable_set(:@owner, parent)
|
|
67
|
+
ctx = described_class.new(ctrl, DummyModel, { parent: :owner, authorize: false })
|
|
68
|
+
|
|
69
|
+
expect(ctx.send(:index_relation)).to eq(owned_records)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'returns no records when the parent association is empty' do
|
|
73
|
+
ctrl = double(params: {})
|
|
74
|
+
ctrl.instance_variable_set(:@owner, double(dummy_models: nil))
|
|
75
|
+
ctx = described_class.new(ctrl, DummyModel, { parent: :owner, authorize: false })
|
|
76
|
+
|
|
77
|
+
expect(ctx.send(:index_relation)).to be_empty
|
|
78
|
+
end
|
|
30
79
|
end
|
|
31
80
|
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe SimpleCrud::RelationSource do
|
|
6
|
+
after { SimpleCrud::Config.user_method = :current_user }
|
|
7
|
+
|
|
8
|
+
let(:controller) { double(params: {}) }
|
|
9
|
+
let(:source) { described_class.new(controller, DummyModel, parameters) }
|
|
10
|
+
|
|
11
|
+
describe '#configured?' do
|
|
12
|
+
it 'is true for owned_by' do
|
|
13
|
+
expect(described_class.new(controller, DummyModel, { owned_by: :dummy_models })).to be_configured
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'is true for parent' do
|
|
17
|
+
expect(described_class.new(controller, DummyModel, { parent: :owner })).to be_configured
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'is false when neither is set' do
|
|
21
|
+
expect(described_class.new(controller, DummyModel, {})).not_to be_configured
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#relation' do
|
|
26
|
+
it 'is nil when neither option is set' do
|
|
27
|
+
expect(described_class.new(controller, DummyModel, {}).relation).to be_nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context 'with owned_by' do
|
|
31
|
+
let(:parameters) { { owned_by: :dummy_models } }
|
|
32
|
+
|
|
33
|
+
it 'resolves the association on the user' do
|
|
34
|
+
models = DummyModel.none
|
|
35
|
+
allow(controller).to receive(:current_user).and_return(double(dummy_models: models))
|
|
36
|
+
|
|
37
|
+
expect(source.relation).to eq(models)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'is nil without a current user' do
|
|
41
|
+
expect(source.relation).to be_nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'resolves the user via overridden Config.user_method' do
|
|
45
|
+
SimpleCrud::Config.user_method = :current_admin
|
|
46
|
+
models = DummyModel.none
|
|
47
|
+
allow(controller).to receive(:current_admin).and_return(double(dummy_models: models))
|
|
48
|
+
|
|
49
|
+
expect(source.relation).to eq(models)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context 'with parent' do
|
|
54
|
+
let(:parameters) { { parent: :owner } }
|
|
55
|
+
|
|
56
|
+
it 'prefers a controller method over an ivar' do
|
|
57
|
+
parent = double(dummy_models: DummyModel.none)
|
|
58
|
+
allow(controller).to receive(:owner).and_return(parent)
|
|
59
|
+
|
|
60
|
+
expect(source.relation).to eq(DummyModel.none)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'falls back to the ivar when there is no method' do
|
|
64
|
+
parent = double(dummy_models: DummyModel.none)
|
|
65
|
+
controller.instance_variable_set(:@owner, parent)
|
|
66
|
+
|
|
67
|
+
expect(source.relation).to eq(DummyModel.none)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'runs a Proc parent in controller context' do
|
|
71
|
+
parent = double(dummy_models: DummyModel.none)
|
|
72
|
+
allow(controller).to receive(:owner).and_return(parent)
|
|
73
|
+
proc_source = described_class.new(controller, DummyModel, { parent: -> { owner } })
|
|
74
|
+
|
|
75
|
+
expect(proc_source.relation).to eq(DummyModel.none)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'is nil when the parent resolves to nil' do
|
|
79
|
+
controller.instance_variable_set(:@owner, nil)
|
|
80
|
+
|
|
81
|
+
expect(source.relation).to be_nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'derives the association from the model' do
|
|
85
|
+
parent = double
|
|
86
|
+
controller.instance_variable_set(:@owner, parent)
|
|
87
|
+
allow(parent).to receive(:dummy_models).and_return(DummyModel.none)
|
|
88
|
+
|
|
89
|
+
expect(source.relation).to eq(DummyModel.none)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'honors parent_association:' do
|
|
93
|
+
models = DummyModel.none
|
|
94
|
+
controller.instance_variable_set(:@owner, double(special: models))
|
|
95
|
+
source = described_class.new(controller, DummyModel, { parent: :owner, parent_association: :special })
|
|
96
|
+
|
|
97
|
+
expect(source.relation).to eq(models)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe '#relation!' do
|
|
103
|
+
let(:parameters) { { owned_by: :dummy_models } }
|
|
104
|
+
|
|
105
|
+
it 'raises not found when the owner cannot be resolved' do
|
|
106
|
+
expect { source.relation! }.to raise_error(ActiveRecord::RecordNotFound, /no owner for dummy_models/)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe '#user' do
|
|
111
|
+
let(:parameters) { {} }
|
|
112
|
+
|
|
113
|
+
it 'returns nil when the controller has no user method' do
|
|
114
|
+
expect(source.user).to be_nil
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe 'unresolvable parent' do
|
|
119
|
+
let(:parameters) { { parent: :owner } }
|
|
120
|
+
|
|
121
|
+
it 'raises when the parent is neither a method nor an ivar' do
|
|
122
|
+
expect { source.relation }.to raise_error(ArgumentError, /neither a controller method nor an @owner ivar/)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -58,6 +58,19 @@ describe SimpleCrud::RSpec do
|
|
|
58
58
|
SimpleCrud::RSpec.configure { |c| c.required_attribute = original }
|
|
59
59
|
expect(config.required_attribute).to eq(:name)
|
|
60
60
|
end
|
|
61
|
+
|
|
62
|
+
describe 'the default serializer_class' do
|
|
63
|
+
it 'prefers the AMS serializer when the constant exists' do
|
|
64
|
+
expect(config.serializer_class.call(DummyModel.new)).to eq(DummyModelSerializer)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'falls back to the Blueprint when there is no AMS serializer' do
|
|
68
|
+
allow(Kernel).to receive(:const_defined?).and_call_original
|
|
69
|
+
allow(Kernel).to receive(:const_defined?).with('DummyModelSerializer').and_return(false)
|
|
70
|
+
|
|
71
|
+
expect(config.serializer_class.call(DummyModel.new)).to eq(DummyModelBlueprint)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
61
74
|
end
|
|
62
75
|
|
|
63
76
|
describe SimpleCrud::RSpec::Helpers do
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe SimpleCrud::Serializer do
|
|
6
|
+
let(:record) { DummyModel.new(id: 1, name: 'widget', something: 'thing', user_id: 9) }
|
|
7
|
+
let(:non_class_serializer) do
|
|
8
|
+
Module.new do
|
|
9
|
+
def self.render(_record, **)
|
|
10
|
+
:plain
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.new(*)
|
|
14
|
+
Struct.new(:as_json).new(:ams)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe '.render' do
|
|
20
|
+
it 'falls back to the record as_json when there is no serializer' do
|
|
21
|
+
expect(described_class.render(nil, record)).to eq(record.as_json)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'renders through a Blueprinter blueprint with the given options' do
|
|
25
|
+
result = described_class.render(DummyModelBlueprint, record, { url_builder: nil })
|
|
26
|
+
|
|
27
|
+
expect(result).to include(id: record.id, name: record.name, something: record.something, user_id: record.user_id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'calls a plain class .render method with the options splatted as keywords' do
|
|
31
|
+
result = described_class.render(DummyModelPlainSerializer, record, { root: false })
|
|
32
|
+
|
|
33
|
+
expect(result).to eq(id: record.id, name: record.name, something: record.something, root: false)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'falls through to the AMS path for a non-class that responds to render' do
|
|
37
|
+
expect(described_class.render(non_class_serializer, record)).to eq(:ams)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'instantiates an AMS-style serializer with the given options', :aggregate_failures do
|
|
41
|
+
allow(DummyModelSerializer).to receive(:new).and_call_original
|
|
42
|
+
|
|
43
|
+
result = described_class.render(DummyModelSerializer, record, { root: false })
|
|
44
|
+
|
|
45
|
+
expect(DummyModelSerializer).to have_received(:new).with(record, { root: false })
|
|
46
|
+
expect(result).to include(id: record.id, something: record.something)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -10,5 +10,10 @@ describe SimpleCrud::ShowContext do
|
|
|
10
10
|
expect { ctx.send(:find_record) }
|
|
11
11
|
.to raise_error(ActiveRecord::RecordNotFound, /must return a single record/)
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
it 'raises not found when owned_by has no owner to scope the lookup to' do
|
|
15
|
+
ctx = described_class.new(double(params: { id: 1 }), DummyModel, { owned_by: :dummy_models, authorize: false })
|
|
16
|
+
expect { ctx.send(:find_record) }.to raise_error(ActiveRecord::RecordNotFound, /no owner/)
|
|
17
|
+
end
|
|
13
18
|
end
|
|
14
19
|
end
|
|
@@ -16,7 +16,22 @@ end
|
|
|
16
16
|
class InheritingController < BaseWithDefaultsController
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
class MultiActionModel; end
|
|
20
|
+
|
|
21
|
+
class MultiActionModelsController
|
|
22
|
+
extend SimpleCrudController
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
describe SimpleCrudController do
|
|
26
|
+
describe '.simple_crud_for' do
|
|
27
|
+
it 'declares every action when given an array', :aggregate_failures do
|
|
28
|
+
MultiActionModelsController.simple_crud_for(%i[show create], authorize: false, authenticate: false)
|
|
29
|
+
|
|
30
|
+
expect(MultiActionModelsController.method_defined?(:show)).to be true
|
|
31
|
+
expect(MultiActionModelsController.method_defined?(:create)).to be true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
20
35
|
describe '.check_policies' do
|
|
21
36
|
it 'returns when a matching policy exists' do
|
|
22
37
|
expect { DummyModelsController.check_policies(authorize: true) }.not_to raise_error
|