wor-simple_crud 0.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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +52 -0
- data/.gitignore +338 -0
- data/.overcommit.yml +15 -0
- data/.rubocop.yml +16 -0
- data/Appraisals +40 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +42 -0
- data/LICENSE.md +22 -0
- data/README.md +286 -0
- data/Rakefile +6 -0
- data/lib/simple_crud/authorization/action_policy_adapter.rb +22 -0
- data/lib/simple_crud/authorization/adapter.rb +20 -0
- data/lib/simple_crud/authorization/can_can_can_adapter.rb +22 -0
- data/lib/simple_crud/authorization/pundit_adapter.rb +21 -0
- data/lib/simple_crud/config.rb +24 -0
- data/lib/simple_crud/pagination/adapter.rb +14 -0
- data/lib/simple_crud/pagination/kaminari_adapter.rb +19 -0
- data/lib/simple_crud/pagination/pagy_adapter.rb +22 -0
- data/lib/simple_crud/pagination/will_paginate_adapter.rb +22 -0
- data/lib/simple_crud/pagination/wor_paginate_adapter.rb +17 -0
- data/lib/simple_crud/rspec.rb +11 -0
- data/lib/simple_crud/simple_crud_controller.rb +125 -0
- data/lib/simple_crud/version.rb +5 -0
- data/lib/simple_crud.rb +12 -0
- data/lib/spec/matchers/have_been_serialized_with.rb +30 -0
- data/lib/spec/response_helper.rb +10 -0
- data/lib/spec/shared_contexts/authenticate_user.rb +11 -0
- data/lib/spec/shared_examples/helpers.rb +50 -0
- data/lib/spec/shared_examples/simple_crud_for_create.rb +49 -0
- data/lib/spec/shared_examples/simple_crud_for_destroy.rb +68 -0
- data/lib/spec/shared_examples/simple_crud_for_index.rb +56 -0
- data/lib/spec/shared_examples/simple_crud_for_show.rb +51 -0
- data/lib/spec/shared_examples/simple_crud_for_update.rb +61 -0
- data/lib/spec/shared_examples/unauthorized_when_not_logged_in.rb +10 -0
- data/pull_request_template.md +3 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/config/manifest.js +6 -0
- data/spec/dummy/app/controllers/application_controller.rb +38 -0
- data/spec/dummy/app/controllers/dummy_models_controller.rb +23 -0
- data/spec/dummy/app/controllers/without_pagination/dummy_models_controller.rb +15 -0
- data/spec/dummy/app/models/application_record.rb +5 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/dummy_model.rb +7 -0
- data/spec/dummy/app/models/dummy_model_policy.rb +34 -0
- data/spec/dummy/app/models/user.rb +11 -0
- data/spec/dummy/app/serializers/dummy_model_serializer.rb +5 -0
- data/spec/dummy/bin/bundle +5 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/bin/rake +6 -0
- data/spec/dummy/bin/setup +35 -0
- data/spec/dummy/bin/update +30 -0
- data/spec/dummy/config/application.rb +17 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +46 -0
- data/spec/dummy/config/environments/test.rb +47 -0
- data/spec/dummy/config/initializers/devise.rb +303 -0
- data/spec/dummy/config/locales/devise.en.yml +65 -0
- data/spec/dummy/config/puma.rb +49 -0
- data/spec/dummy/config/routes.rb +10 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config/spring.rb +8 -0
- data/spec/dummy/config.ru +7 -0
- data/spec/dummy/db/migrate/20170626184159_create_dummy_models.rb +10 -0
- data/spec/dummy/db/migrate/20200110191019_devise_create_users.rb +43 -0
- data/spec/dummy/db/migrate/20200120165657_add_user_id_to_dummy_models.rb +7 -0
- data/spec/dummy/db/schema.rb +36 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/spec/factories/dummy_models.rb +9 -0
- data/spec/dummy/spec/factories/users.rb +8 -0
- data/spec/dummy_controller_spec.rb +19 -0
- data/spec/dummy_model_policy_spec.rb +29 -0
- data/spec/dummy_model_serializer_spec.rb +36 -0
- data/spec/simple_crud/authorization/action_policy_adapter_spec.rb +58 -0
- data/spec/simple_crud/authorization/can_can_can_adapter_spec.rb +53 -0
- data/spec/simple_crud/pagination/adapters_spec.rb +48 -0
- data/spec/simple_crud_configuration_spec.rb +61 -0
- data/spec/simple_crud_controller_spec.rb +41 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/spy.rb +41 -0
- data/spec/without_pagination/dummy_models_controller_spec.rb +6 -0
- data/wor-simple_crud.gemspec +23 -0
- metadata +145 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :users do |t|
|
|
6
|
+
## Database authenticatable
|
|
7
|
+
t.string :email, null: false, default: ''
|
|
8
|
+
t.string :encrypted_password, null: false, default: ''
|
|
9
|
+
|
|
10
|
+
## Recoverable
|
|
11
|
+
t.string :reset_password_token
|
|
12
|
+
t.datetime :reset_password_sent_at
|
|
13
|
+
|
|
14
|
+
## Rememberable
|
|
15
|
+
t.datetime :remember_created_at
|
|
16
|
+
|
|
17
|
+
## Trackable
|
|
18
|
+
# t.integer :sign_in_count, default: 0, null: false
|
|
19
|
+
# t.datetime :current_sign_in_at
|
|
20
|
+
# t.datetime :last_sign_in_at
|
|
21
|
+
# t.string :current_sign_in_ip
|
|
22
|
+
# t.string :last_sign_in_ip
|
|
23
|
+
|
|
24
|
+
## Confirmable
|
|
25
|
+
# t.string :confirmation_token
|
|
26
|
+
# t.datetime :confirmed_at
|
|
27
|
+
# t.datetime :confirmation_sent_at
|
|
28
|
+
# t.string :unconfirmed_email # Only if using reconfirmable
|
|
29
|
+
|
|
30
|
+
## Lockable
|
|
31
|
+
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
|
|
32
|
+
# t.string :unlock_token # Only if unlock strategy is :email or :both
|
|
33
|
+
# t.datetime :locked_at
|
|
34
|
+
|
|
35
|
+
t.timestamps null: false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
add_index :users, :email, unique: true
|
|
39
|
+
add_index :users, :reset_password_token, unique: true
|
|
40
|
+
# add_index :users, :confirmation_token, unique: true
|
|
41
|
+
# add_index :users, :unlock_token, unique: true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
4
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
5
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
6
|
+
#
|
|
7
|
+
# This file is the source Rails uses to define your schema when running `rails
|
|
8
|
+
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
|
9
|
+
# be faster and is potentially less error prone than running all of your
|
|
10
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
11
|
+
# migrations use external dependencies or application code.
|
|
12
|
+
#
|
|
13
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
14
|
+
|
|
15
|
+
ActiveRecord::Schema.define(version: 20_200_120_165_657) do
|
|
16
|
+
create_table 'dummy_models', force: :cascade do |t|
|
|
17
|
+
t.string 'name'
|
|
18
|
+
t.integer 'something'
|
|
19
|
+
t.integer 'user_id'
|
|
20
|
+
t.index ['user_id'], name: 'index_dummy_models_on_user_id'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
create_table 'users', force: :cascade do |t|
|
|
24
|
+
t.string 'email', default: '', null: false
|
|
25
|
+
t.string 'encrypted_password', default: '', null: false
|
|
26
|
+
t.string 'reset_password_token'
|
|
27
|
+
t.datetime 'reset_password_sent_at'
|
|
28
|
+
t.datetime 'remember_created_at'
|
|
29
|
+
t.datetime 'created_at', precision: 6, null: false
|
|
30
|
+
t.datetime 'updated_at', precision: 6, null: false
|
|
31
|
+
t.index ['email'], name: 'index_users_on_email', unique: true
|
|
32
|
+
t.index ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
add_foreign_key 'dummy_models', 'users'
|
|
36
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
describe DummyModelsController, type: :controller do
|
|
5
|
+
include_examples 'simple crud for update'
|
|
6
|
+
include_examples 'simple crud for show'
|
|
7
|
+
include_examples 'simple crud for create'
|
|
8
|
+
include_examples 'simple crud for index'
|
|
9
|
+
include_examples 'simple crud for destroy'
|
|
10
|
+
|
|
11
|
+
describe 'POST #create with invalid attributes' do
|
|
12
|
+
include_context 'with authenticated user'
|
|
13
|
+
|
|
14
|
+
it 'responds with unprocessable entity' do
|
|
15
|
+
post :create, params: { something: 1, user_id: current_user.id }
|
|
16
|
+
expect(response).to have_http_status(ApplicationController::UNPROCESSABLE_STATUS)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe DummyModelPolicy do
|
|
6
|
+
let(:user) { create(:user) }
|
|
7
|
+
let(:other_user) { create(:user) }
|
|
8
|
+
let(:dummy_model) { create(:dummy_model, user: user) }
|
|
9
|
+
|
|
10
|
+
describe '#destroy?' do
|
|
11
|
+
it 'allows the owning user' do
|
|
12
|
+
expect(described_class.new(user, dummy_model).destroy?).to be true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'denies a different user' do
|
|
16
|
+
expect(described_class.new(other_user, dummy_model).destroy?).to be false
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#index?' do
|
|
21
|
+
it 'allows the owning user' do
|
|
22
|
+
expect(described_class.new(user, dummy_model).index?).to be true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'denies a different user' do
|
|
26
|
+
expect(described_class.new(other_user, dummy_model).index?).to be false
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe DummyModelSerializer do
|
|
6
|
+
let(:user) { create(:user) }
|
|
7
|
+
let(:dummy_model) { create(:dummy_model, user: user) }
|
|
8
|
+
let(:full_response) { { 'id' => dummy_model.id, 'something' => dummy_model.something, 'user_id' => user.id } }
|
|
9
|
+
|
|
10
|
+
it 'matches a response including all serializer attributes' do
|
|
11
|
+
expect(full_response).to have_been_serialized_with(described_class)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'does not match a response missing serializer attributes' do
|
|
15
|
+
expect({ 'id' => dummy_model.id }).not_to have_been_serialized_with(described_class)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'describes itself' do
|
|
19
|
+
expect(have_been_serialized_with(described_class).description)
|
|
20
|
+
.to eq("checks to see if all the serializer's attributes are in the JSON response")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'produces a failure message when it does not match' do
|
|
24
|
+
matcher = have_been_serialized_with(described_class)
|
|
25
|
+
matcher.matches?({ 'id' => dummy_model.id })
|
|
26
|
+
|
|
27
|
+
expect(matcher.failure_message).to match(/expected attributes of/)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'produces a negated failure message when it matches but was expected not to' do
|
|
31
|
+
matcher = have_been_serialized_with(described_class)
|
|
32
|
+
matcher.matches?(full_response)
|
|
33
|
+
|
|
34
|
+
expect(matcher.failure_message_when_negated).to match(/not to be included in/)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'simple_crud/authorization/action_policy_adapter'
|
|
5
|
+
|
|
6
|
+
class ActionPolicyTestRecord
|
|
7
|
+
attr_reader :owner_id
|
|
8
|
+
|
|
9
|
+
def initialize(owner_id)
|
|
10
|
+
@owner_id = owner_id
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class ActionPolicyTestRecordPolicy < ActionPolicy::Base
|
|
15
|
+
def show?
|
|
16
|
+
user == record.owner_id
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class ActionPolicyTestController
|
|
21
|
+
include ActionPolicy::Controller
|
|
22
|
+
authorize :user, through: :current_user
|
|
23
|
+
|
|
24
|
+
attr_accessor :action_name, :current_user
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe SimpleCrud::Authorization::ActionPolicyAdapter do
|
|
28
|
+
subject(:adapter) { described_class.new }
|
|
29
|
+
|
|
30
|
+
let(:controller) do
|
|
31
|
+
ActionPolicyTestController.new.tap do |c|
|
|
32
|
+
c.action_name = 'show'
|
|
33
|
+
c.current_user = 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#authorize' do
|
|
38
|
+
it 'passes for a record the policy allows' do
|
|
39
|
+
expect { adapter.authorize(controller, ActionPolicyTestRecord.new(1)) }.not_to raise_error
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'raises for a record the policy denies' do
|
|
43
|
+
expect do
|
|
44
|
+
adapter.authorize(controller, ActionPolicyTestRecord.new(2))
|
|
45
|
+
end.to raise_error(ActionPolicy::Unauthorized)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe '#policy_defined?' do
|
|
50
|
+
it 'is true when a matching policy class exists' do
|
|
51
|
+
expect(adapter.policy_defined?(ActionPolicyTestRecord)).to be true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'is false when no matching policy class exists' do
|
|
55
|
+
expect(adapter.policy_defined?(String)).to be false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'simple_crud/authorization/can_can_can_adapter'
|
|
5
|
+
|
|
6
|
+
class CanCanCanTestRecord
|
|
7
|
+
attr_reader :owner_id
|
|
8
|
+
|
|
9
|
+
def initialize(owner_id)
|
|
10
|
+
@owner_id = owner_id
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Ability
|
|
15
|
+
include CanCan::Ability
|
|
16
|
+
|
|
17
|
+
def initialize(user_id)
|
|
18
|
+
can :manage, CanCanCanTestRecord, owner_id: user_id
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class CanCanCanTestController
|
|
23
|
+
include CanCan::ControllerAdditions
|
|
24
|
+
|
|
25
|
+
attr_accessor :action_name, :current_user
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe SimpleCrud::Authorization::CanCanCanAdapter do
|
|
29
|
+
subject(:adapter) { described_class.new }
|
|
30
|
+
|
|
31
|
+
let(:controller) do
|
|
32
|
+
CanCanCanTestController.new.tap do |c|
|
|
33
|
+
c.action_name = 'show'
|
|
34
|
+
c.current_user = 1
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#authorize' do
|
|
39
|
+
it 'passes for a record the ability grants' do
|
|
40
|
+
expect { adapter.authorize(controller, CanCanCanTestRecord.new(1)) }.not_to raise_error
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'raises for a record the ability denies' do
|
|
44
|
+
expect { adapter.authorize(controller, CanCanCanTestRecord.new(2)) }.to raise_error(CanCan::AccessDenied)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe '#policy_defined?' do
|
|
49
|
+
it 'is true when an Ability class exists' do
|
|
50
|
+
expect(adapter.policy_defined?(CanCanCanTestRecord)).to be true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'simple_crud/pagination/kaminari_adapter'
|
|
5
|
+
require 'simple_crud/pagination/will_paginate_adapter'
|
|
6
|
+
require 'simple_crud/pagination/pagy_adapter'
|
|
7
|
+
|
|
8
|
+
describe 'pagination adapters', type: :request do
|
|
9
|
+
let(:user) { create(:user) }
|
|
10
|
+
let(:auth_headers) { Devise::JWT::TestHelpers.auth_headers({}, user) }
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
create_list(:dummy_model, 15, user: user)
|
|
14
|
+
# Index authorizes against a blank klass.new (no owner), so the real
|
|
15
|
+
# ownership-based policy can never pass here.
|
|
16
|
+
allow(DummyModelPolicy).to receive(:new).and_return(instance_double(DummyModelPolicy, index?: true))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
around do |example|
|
|
20
|
+
original_adapter = SimpleCrud::Config.pagination_adapter
|
|
21
|
+
example.run
|
|
22
|
+
SimpleCrud::Config.pagination_adapter = original_adapter
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'paginates with Kaminari' do
|
|
26
|
+
SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::KaminariAdapter.new
|
|
27
|
+
|
|
28
|
+
get '/dummy_models', params: { page: 2, per_page: 5 }, headers: auth_headers
|
|
29
|
+
|
|
30
|
+
expect(response.parsed_body.size).to eq(5)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'paginates with will_paginate' do
|
|
34
|
+
SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::WillPaginateAdapter.new
|
|
35
|
+
|
|
36
|
+
get '/dummy_models', params: { page: 2, per_page: 5 }, headers: auth_headers
|
|
37
|
+
|
|
38
|
+
expect(response.parsed_body.size).to eq(5)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'paginates with Pagy' do
|
|
42
|
+
SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::PagyAdapter.new
|
|
43
|
+
|
|
44
|
+
get '/dummy_models', params: { page: 2, limit: 5 }, headers: auth_headers
|
|
45
|
+
|
|
46
|
+
expect(response.parsed_body.size).to eq(5)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'simple_crud/authorization/pundit_adapter'
|
|
5
|
+
require 'simple_crud/pagination/wor_paginate_adapter'
|
|
6
|
+
|
|
7
|
+
class IncompleteAuthorizationAdapter
|
|
8
|
+
include SimpleCrud::Authorization::Adapter
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class IncompletePaginationAdapter
|
|
12
|
+
include SimpleCrud::Pagination::Adapter
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe SimpleCrud do
|
|
16
|
+
describe '.configure' do
|
|
17
|
+
it 'yields SimpleCrud::Config' do
|
|
18
|
+
expect { |b| described_class.configure(&b) }.to yield_with_args(SimpleCrud::Config)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'lets a custom authorization adapter replace the Pundit default' do
|
|
22
|
+
original_adapter = SimpleCrud::Config.authorization_adapter
|
|
23
|
+
custom_adapter = instance_double(SimpleCrud::Authorization::PunditAdapter)
|
|
24
|
+
|
|
25
|
+
described_class.configure { |config| config.authorization_adapter = custom_adapter }
|
|
26
|
+
expect(SimpleCrud::Config.authorization_adapter).to eq(custom_adapter)
|
|
27
|
+
|
|
28
|
+
SimpleCrud::Config.authorization_adapter = original_adapter
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'lets a custom pagination adapter replace the wor-paginate default' do
|
|
32
|
+
original_adapter = SimpleCrud::Config.pagination_adapter
|
|
33
|
+
custom_adapter = instance_double(SimpleCrud::Pagination::WorPaginateAdapter)
|
|
34
|
+
|
|
35
|
+
described_class.configure { |config| config.pagination_adapter = custom_adapter }
|
|
36
|
+
expect(SimpleCrud::Config.pagination_adapter).to eq(custom_adapter)
|
|
37
|
+
|
|
38
|
+
SimpleCrud::Config.pagination_adapter = original_adapter
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe SimpleCrud::Authorization::Adapter do
|
|
43
|
+
subject(:adapter) { IncompleteAuthorizationAdapter.new }
|
|
44
|
+
|
|
45
|
+
it 'requires #authorize to be implemented' do
|
|
46
|
+
expect { adapter.authorize(nil, nil) }.to raise_error(NotImplementedError)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'requires #policy_defined? to be implemented' do
|
|
50
|
+
expect { adapter.policy_defined?(nil) }.to raise_error(NotImplementedError)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe SimpleCrud::Pagination::Adapter do
|
|
55
|
+
subject(:adapter) { IncompletePaginationAdapter.new }
|
|
56
|
+
|
|
57
|
+
it 'requires #paginate to be implemented' do
|
|
58
|
+
expect { adapter.paginate(nil, nil, nil) }.to raise_error(NotImplementedError)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
class UnpoliceableThing; end
|
|
6
|
+
|
|
7
|
+
class UnpoliceableThingsController
|
|
8
|
+
extend SimpleCrudController
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe SimpleCrudController do
|
|
12
|
+
describe '.check_policies' do
|
|
13
|
+
it 'returns when a matching policy exists' do
|
|
14
|
+
expect { DummyModelsController.check_policies(authorize: true) }.not_to raise_error
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'throws when no matching policy exists' do
|
|
18
|
+
expect do
|
|
19
|
+
UnpoliceableThingsController.check_policies(authorize: true)
|
|
20
|
+
end.to raise_error(UncaughtThrowError)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '.check_serializer' do
|
|
25
|
+
it 'returns when the serializer option is blank' do
|
|
26
|
+
expect { DummyModelsController.check_serializer({}) }.not_to raise_error
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'returns when a matching serializer exists' do
|
|
30
|
+
expect do
|
|
31
|
+
DummyModelsController.check_serializer(serializer: 'DummyModelSerializer')
|
|
32
|
+
end.not_to raise_error
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'throws when no matching serializer exists' do
|
|
36
|
+
expect do
|
|
37
|
+
DummyModelsController.check_serializer(serializer: 'NonExistentSerializer')
|
|
38
|
+
end.to raise_error(UncaughtThrowError)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
ENV['RAILS_ENV'] ||= 'test'
|
|
4
|
+
|
|
5
|
+
require 'simplecov'
|
|
6
|
+
SimpleCov.start do
|
|
7
|
+
minimum_coverage 100
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require File.expand_path('../spec/dummy/config/environment.rb', __dir__)
|
|
11
|
+
require 'simple_crud'
|
|
12
|
+
require 'rspec/rails'
|
|
13
|
+
require 'pundit/rspec'
|
|
14
|
+
require 'wor/paginate/rspec'
|
|
15
|
+
require 'simple_crud/rspec'
|
|
16
|
+
require 'devise'
|
|
17
|
+
require 'devise/jwt/test_helpers'
|
|
18
|
+
|
|
19
|
+
Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
|
|
20
|
+
|
|
21
|
+
ActiveRecord::Migrator.migrations_paths = [File.expand_path('../spec/dummy/db/migrate', __dir__)]
|
|
22
|
+
|
|
23
|
+
RSpec.configure do |config|
|
|
24
|
+
config.include ActionDispatch::TestProcess
|
|
25
|
+
config.file_fixture_path = Rails.root.join('spec', 'support', 'fixtures')
|
|
26
|
+
|
|
27
|
+
config.infer_spec_type_from_file_location!
|
|
28
|
+
config.include Devise::Test::ControllerHelpers, type: :controller
|
|
29
|
+
config.include Devise::Test::IntegrationHelpers, type: :request
|
|
30
|
+
config.include FactoryBot::Syntax::Methods
|
|
31
|
+
config.include Response::JSONParser, type: :controller
|
|
32
|
+
config.order = 'random'
|
|
33
|
+
|
|
34
|
+
# DatabaseCleaner already wraps each example in a transaction below.
|
|
35
|
+
# rspec-rails's own wrapping double-nests it and was observed to leak
|
|
36
|
+
# committed rows. Disable it here.
|
|
37
|
+
config.use_transactional_fixtures = false
|
|
38
|
+
|
|
39
|
+
config.before(:suite) do
|
|
40
|
+
DatabaseCleaner.strategy = :transaction
|
|
41
|
+
DatabaseCleaner.clean_with(:truncation)
|
|
42
|
+
|
|
43
|
+
# Warms the schema cache before any DatabaseCleaner transaction opens.
|
|
44
|
+
# Otherwise the first example touching a table triggers a PRAGMA
|
|
45
|
+
# table_info query mid-transaction, which silently ends it and commits
|
|
46
|
+
# that example's rows for real instead of rolling back.
|
|
47
|
+
conn = ActiveRecord::Base.connection
|
|
48
|
+
conn.data_sources.each { |t| conn.schema_cache.add(t) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
config.around do |example|
|
|
52
|
+
DatabaseCleaner.cleaning do
|
|
53
|
+
example.run
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
ActiveRecord::Migration.maintain_test_schema!
|
|
59
|
+
|
|
60
|
+
require 'byebug'
|
data/spec/spy.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Class used in tests to keep track of amount of calls for a method in a klass.
|
|
5
|
+
#
|
|
6
|
+
# Example in a controller spec:
|
|
7
|
+
#
|
|
8
|
+
# before do
|
|
9
|
+
# Spy.spy(controller, 'authenticate_entity')
|
|
10
|
+
# perform_some_request
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# it 'calls authenticate_entity defined by the user' do
|
|
14
|
+
# count = controller.spied_authenticate_entity_counter
|
|
15
|
+
# expect(count).to be(1)
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
class Spy
|
|
19
|
+
# Defines a counter for the given method and overrides it to update
|
|
20
|
+
# the counter in every call before being executed.
|
|
21
|
+
def self.spy(klass, method)
|
|
22
|
+
define_counter(klass, method)
|
|
23
|
+
klass.singleton_class.class_eval do
|
|
24
|
+
define_method(method) do |*args, &block|
|
|
25
|
+
instance_variable_set("@spied_#{method}_counter", instance_variable_get("@spied_#{method}_counter") + 1)
|
|
26
|
+
super(*args, &block)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Defines a counter (and its getter) for the given method as an instance variable of the given klass.
|
|
32
|
+
def self.define_counter(klass, method)
|
|
33
|
+
klass.instance_variable_set("@spied_#{method}_counter".to_sym, 0)
|
|
34
|
+
|
|
35
|
+
klass.singleton_class.class_eval do
|
|
36
|
+
define_method("spied_#{method}_counter".to_sym) do
|
|
37
|
+
instance_variable_get("@spied_#{method}_counter")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "simple_crud/version"
|
|
4
|
+
require 'date'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = "wor-simple_crud"
|
|
8
|
+
s.version = SimpleCrud::VERSION
|
|
9
|
+
s.platform = Gem::Platform::RUBY
|
|
10
|
+
s.date = Date.today
|
|
11
|
+
s.authors = ["icoluccio"]
|
|
12
|
+
s.email = ["ignacio.coluccio@gmail.com"]
|
|
13
|
+
s.homepage = "https://github.com/icoluccio/simple-crud"
|
|
14
|
+
s.summary = "Simplified CRUD endpoints for Rails API controllers"
|
|
15
|
+
s.description = "Simple CRUD is a gem for Rails that simplifies CRUD controllers, with default implementations and specs for common tasks"
|
|
16
|
+
s.license = "MIT"
|
|
17
|
+
s.required_ruby_version = '>= 3.2'
|
|
18
|
+
|
|
19
|
+
s.files = `git ls-files -z`.split("\x0")
|
|
20
|
+
s.require_paths = ['lib']
|
|
21
|
+
|
|
22
|
+
s.add_dependency 'rails', '>= 6.1', '< 9'
|
|
23
|
+
end
|