rockstart 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +100 -0
- data/Rakefile +19 -0
- data/lib/generators/rockstart/USAGE +13 -0
- data/lib/generators/rockstart/devise/USAGE +9 -0
- data/lib/generators/rockstart/devise/devise_generator.rb +258 -0
- data/lib/generators/rockstart/devise/templates/controllers/passwords_controller.rb +56 -0
- data/lib/generators/rockstart/devise/templates/controllers/registrations_controller.rb +88 -0
- data/lib/generators/rockstart/devise/templates/controllers/sessions_controller.rb +32 -0
- data/lib/generators/rockstart/devise/templates/create_user_migration.rb.tt +11 -0
- data/lib/generators/rockstart/devise/templates/models/user.rb +42 -0
- data/lib/generators/rockstart/devise/templates/spec/factories/users.rb +17 -0
- data/lib/generators/rockstart/devise/templates/spec/models/user_spec.rb +64 -0
- data/lib/generators/rockstart/devise/templates/spec/requests/users/passwords_spec.rb +202 -0
- data/lib/generators/rockstart/devise/templates/spec/requests/users/registrations_spec.rb +445 -0
- data/lib/generators/rockstart/devise/templates/spec/requests/users/sessions_spec.rb +171 -0
- data/lib/generators/rockstart/devise/templates/spec/support/devise_request_spec_helper.rb +29 -0
- data/lib/generators/rockstart/devise/templates/translations.en.yml +4 -0
- data/lib/generators/rockstart/docker/USAGE +10 -0
- data/lib/generators/rockstart/docker/docker_generator.rb +86 -0
- data/lib/generators/rockstart/docker/templates/app/Dockerfile-app +47 -0
- data/lib/generators/rockstart/docker/templates/docker-compose.test.yml +29 -0
- data/lib/generators/rockstart/docker/templates/docker-compose.yml +47 -0
- data/lib/generators/rockstart/docker/templates/dockerignore +16 -0
- data/lib/generators/rockstart/docker/templates/dotenv.docker.tt +4 -0
- data/lib/generators/rockstart/docker/templates/localhost_domains.ext.tt +7 -0
- data/lib/generators/rockstart/docker/templates/setup-localhost.tt +27 -0
- data/lib/generators/rockstart/docker/templates/web/Dockerfile-web +15 -0
- data/lib/generators/rockstart/docker/templates/web/nginx.conf +62 -0
- data/lib/generators/rockstart/frontend_helpers/USAGE +8 -0
- data/lib/generators/rockstart/frontend_helpers/frontend_helpers_generator.rb +65 -0
- data/lib/generators/rockstart/frontend_helpers/templates/application_urls.rb +26 -0
- data/lib/generators/rockstart/frontend_helpers/templates/application_urls_helper.rb +20 -0
- data/lib/generators/rockstart/frontend_helpers/templates/titles.en.yml.tt +5 -0
- data/lib/generators/rockstart/logging/USAGE +8 -0
- data/lib/generators/rockstart/logging/logging_generator.rb +12 -0
- data/lib/generators/rockstart/logging/templates/rockstart/lograge_initializer.rb +50 -0
- data/lib/generators/rockstart/postgres/USAGE +8 -0
- data/lib/generators/rockstart/postgres/postgres_generator.rb +32 -0
- data/lib/generators/rockstart/postgres/templates/config/database.yml.tt +18 -0
- data/lib/generators/rockstart/postgres/templates/migration.rb.tt +7 -0
- data/lib/generators/rockstart/pundit/USAGE +8 -0
- data/lib/generators/rockstart/pundit/pundit_generator.rb +32 -0
- data/lib/generators/rockstart/pundit/templates/app/controllers/concerns/pundit_error_handling.rb +29 -0
- data/lib/generators/rockstart/pundit/templates/app/policies/application_policy.rb +71 -0
- data/lib/generators/rockstart/pundit/templates/app/policies/user_policy.rb +47 -0
- data/lib/generators/rockstart/pundit/templates/config/locales/pundit.en.yml +6 -0
- data/lib/generators/rockstart/pundit/templates/lib/templates/pundit/policy/policy.rb +36 -0
- data/lib/generators/rockstart/pundit/templates/lib/templates/rspec/policy/policy_spec.rb +58 -0
- data/lib/generators/rockstart/pundit/templates/spec/policies/user_policy_spec.rb +95 -0
- data/lib/generators/rockstart/pundit/templates/spec/support/pundit_matchers.rb +7 -0
- data/lib/generators/rockstart/quality/USAGE +10 -0
- data/lib/generators/rockstart/quality/quality_generator.rb +28 -0
- data/lib/generators/rockstart/quality/templates/quality.rake +4 -0
- data/lib/generators/rockstart/quality/templates/rubocop.rake +4 -0
- data/lib/generators/rockstart/quality/templates/rubocop.yml +45 -0
- data/lib/generators/rockstart/rockstart_generator.rb +77 -0
- data/lib/generators/rockstart/rspec/USAGE +8 -0
- data/lib/generators/rockstart/rspec/rspec_generator.rb +70 -0
- data/lib/generators/rockstart/rspec/templates/dotenv.development +1 -0
- data/lib/generators/rockstart/rspec/templates/dotenv.test +1 -0
- data/lib/generators/rockstart/rspec/templates/rspec_templates/model/model_spec.rb +13 -0
- data/lib/generators/rockstart/rspec/templates/support/factory_bot.rb +6 -0
- data/lib/generators/rockstart/rspec/templates/support/shoulda_matchers.rb +9 -0
- data/lib/generators/rockstart/rspec/templates/support/test_helpers.rb +9 -0
- data/lib/generators/rockstart/scaffold_templates/USAGE +8 -0
- data/lib/generators/rockstart/scaffold_templates/scaffold_templates_generator.rb +39 -0
- data/lib/generators/rockstart/scaffold_templates/templates/api_controller.rb.tt +96 -0
- data/lib/generators/rockstart/scaffold_templates/templates/controller.rb.tt +126 -0
- data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/api_request_spec.rb +139 -0
- data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/request_spec.rb +408 -0
- data/lib/generators/rockstart/security/USAGE +13 -0
- data/lib/generators/rockstart/security/security_generator.rb +108 -0
- data/lib/generators/rockstart/security/templates/brakeman.rake +6 -0
- data/lib/generators/rockstart/security/templates/bundler_audit.rake +4 -0
- data/lib/generators/rockstart/security/templates/cache_support.rb +18 -0
- data/lib/generators/rockstart/security/templates/content_security_policy_initializer.rb.tt +56 -0
- data/lib/generators/rockstart/security/templates/content_security_spec.rb.tt +83 -0
- data/lib/generators/rockstart/security/templates/csp_violations_controller.rb +39 -0
- data/lib/generators/rockstart/security/templates/rack_attack.rb +98 -0
- data/lib/generators/rockstart/security/templates/security.rake +9 -0
- data/lib/generators/rockstart/security/templates/session_store_initializer.rb.tt +7 -0
- data/lib/generators/rockstart/smtp_mailer/USAGE +8 -0
- data/lib/generators/rockstart/smtp_mailer/smtp_mailer_generator.rb +30 -0
- data/lib/generators/rockstart/smtp_mailer/templates/config/initializers/action_mailer.rb +10 -0
- data/lib/generators/rockstart/tailwindcss/USAGE +8 -0
- data/lib/generators/rockstart/tailwindcss/tailwindcss_generator.rb +30 -0
- data/lib/generators/rockstart/tailwindcss/templates/application.css +3 -0
- data/lib/generators/rockstart/tailwindcss/templates/postcss.config.js +32 -0
- data/lib/rockstart/base_generator.rb +32 -0
- data/lib/rockstart/env.rb +16 -0
- data/lib/rockstart/railtie.rb +6 -0
- data/lib/rockstart/version.rb +5 -0
- data/lib/rockstart.rb +9 -0
- data/lib/tasks/rockstart_tasks.rake +5 -0
- metadata +187 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rockstart::LoggingGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
def install_lograge
|
7
|
+
gem "lograge"
|
8
|
+
gem "logstash-event"
|
9
|
+
|
10
|
+
copy_file "rockstart/lograge_initializer.rb", "config/initializers/lograge.rb"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Rails.application.configure do
|
4
|
+
config.lograge.enabled = true
|
5
|
+
config.lograge.formatter = Lograge::Formatters::Logstash.new
|
6
|
+
|
7
|
+
# Update if using Rails in API mode
|
8
|
+
# config.lograge.base_controller_class = ['ActionController::API', 'ActionController::Base']
|
9
|
+
|
10
|
+
config.lograge.custom_options = lambda do |event|
|
11
|
+
exceptions = %w[controller action format id]
|
12
|
+
{
|
13
|
+
params: event.payload[:params].except(*exceptions)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
config.lograge.custom_payload do |controller|
|
18
|
+
{
|
19
|
+
host: controller.request.host,
|
20
|
+
remote_ip: controller.request.remote_ip,
|
21
|
+
request_id: controller.request.request_id,
|
22
|
+
user_id: (controller.respond_to?(:current_user) && controller.current_user.try(:id)) || :guest
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Report for rack_attack throttle responses in lograge
|
28
|
+
ActiveSupport::Notifications.subscribe("throttle.rack_attack") do |_name, start, finish, request_id, payload|
|
29
|
+
req = payload[:request]
|
30
|
+
|
31
|
+
filter_parameters = Rails.application.config.filter_parameters
|
32
|
+
params = ActiveSupport::ParameterFilter.new(filter_parameters).filter(req.params)
|
33
|
+
|
34
|
+
message_payload = {
|
35
|
+
method: req.request_method,
|
36
|
+
path: req.path,
|
37
|
+
format: params[:format] || "html",
|
38
|
+
controller: Rack::Attack.name,
|
39
|
+
action: "throttle",
|
40
|
+
status: 429,
|
41
|
+
duration: (finish - start).to_f.round(2),
|
42
|
+
params: params.except("controller", "action", "format", "id"),
|
43
|
+
host: req.host,
|
44
|
+
remote_ip: req.ip,
|
45
|
+
request_id: request_id
|
46
|
+
}
|
47
|
+
formatted_message = Lograge.lograge_config.formatter.call(message_payload)
|
48
|
+
logger = Lograge.logger.presence || Rails.logger
|
49
|
+
logger.public_send(Lograge.log_level, formatted_message)
|
50
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rockstart::PostgresGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::AppName
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
# Implement the required interface for Rails::Generators::Migration.
|
8
|
+
def self.next_migration_number(dirname)
|
9
|
+
next_migration_number = current_migration_number(dirname) + 1
|
10
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
11
|
+
end
|
12
|
+
|
13
|
+
source_root File.expand_path("templates", __dir__)
|
14
|
+
|
15
|
+
def copy_database_config
|
16
|
+
template "config/database.yml.tt", "config/database.yml"
|
17
|
+
end
|
18
|
+
|
19
|
+
def enable_uuid_support
|
20
|
+
migration_template "migration.rb.tt", "db/migrate/enable_uuid.rb"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def rails5_and_up?
|
26
|
+
Rails::VERSION::MAJOR >= 5
|
27
|
+
end
|
28
|
+
|
29
|
+
def migration_version
|
30
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if rails5_and_up?
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
default: &default
|
2
|
+
adapter: postgresql
|
3
|
+
encoding: unicode
|
4
|
+
# For details on connection pooling, see Rails configuration guide
|
5
|
+
# https://guides.rubyonrails.org/configuring.html#database-pooling
|
6
|
+
pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
7
|
+
|
8
|
+
development:
|
9
|
+
<<: *default
|
10
|
+
database: <%= app_name %>_development
|
11
|
+
|
12
|
+
test:
|
13
|
+
<<: *default
|
14
|
+
url: <%%= ENV.fetch('TEST_DATABASE_URL', "postgres://localhost/<%= app_name %>_test") %>
|
15
|
+
|
16
|
+
production:
|
17
|
+
<<: *default
|
18
|
+
url: <%%= ENV['DATABASE_URL'] %>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rockstart::PunditGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
def install_pundit
|
7
|
+
gem "pundit"
|
8
|
+
gem "pundit-matchers", group: :test
|
9
|
+
|
10
|
+
Bundler.clean_system("bundle install --quiet")
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_pundit_exception_handling
|
14
|
+
application <<~PUNDIT
|
15
|
+
# Treat Pundit authentication failures as forbidden
|
16
|
+
config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden
|
17
|
+
PUNDIT
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_pundit_to_application_controller
|
21
|
+
inject_into_file "app/controllers/application_controller.rb",
|
22
|
+
" include Pundit\n",
|
23
|
+
before: /^end$/
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_pundit_overrides_and_helpers
|
27
|
+
directory "app"
|
28
|
+
directory "config"
|
29
|
+
directory "lib"
|
30
|
+
directory "spec"
|
31
|
+
end
|
32
|
+
end
|
data/lib/generators/rockstart/pundit/templates/app/controllers/concerns/pundit_error_handling.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Handling for any Pundit-thrown errors
|
4
|
+
module PunditErrorHandling
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# redirect path for failed authentication attempts
|
14
|
+
def authentication_failed_redirect_path_for(_resource)
|
15
|
+
after_sign_in_path_for(current_user)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def user_not_authorized(exception)
|
21
|
+
policy_name = exception.policy.class.to_s.underscore
|
22
|
+
|
23
|
+
# e.g. I18n.t("pundit.example_policy.show?")
|
24
|
+
flash[:error] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default
|
25
|
+
|
26
|
+
# redirect to the either the previous page or the default sign in page
|
27
|
+
redirect_to authentication_failed_redirect_path_for(exception.record)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Default application policy generated by rockstart
|
4
|
+
class ApplicationPolicy
|
5
|
+
attr_reader :user, :record
|
6
|
+
|
7
|
+
def initialize(user, record)
|
8
|
+
@user = user || User.new
|
9
|
+
@record = record
|
10
|
+
end
|
11
|
+
|
12
|
+
# Abilities
|
13
|
+
|
14
|
+
def index?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def show?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def create?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def new?
|
27
|
+
create?
|
28
|
+
end
|
29
|
+
|
30
|
+
def update?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def edit?
|
35
|
+
update?
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Attributes
|
43
|
+
|
44
|
+
def permitted_attributes
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
def permitted_attributes_for_create
|
49
|
+
permitted_attributes
|
50
|
+
end
|
51
|
+
|
52
|
+
def permitted_attributes_for_update
|
53
|
+
permitted_attributes
|
54
|
+
end
|
55
|
+
|
56
|
+
# Scopes
|
57
|
+
|
58
|
+
# Scope filter used to limit access to resources
|
59
|
+
class Scope
|
60
|
+
attr_reader :user, :scope
|
61
|
+
|
62
|
+
def initialize(user, scope)
|
63
|
+
@user = user || User.new
|
64
|
+
@scope = scope
|
65
|
+
end
|
66
|
+
|
67
|
+
def resolve
|
68
|
+
scope.none
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Policy for updating profiles, provided by rockstart
|
4
|
+
class UserPolicy < ApplicationPolicy
|
5
|
+
# def index?
|
6
|
+
# false
|
7
|
+
# end
|
8
|
+
|
9
|
+
# def show?
|
10
|
+
# false
|
11
|
+
# end
|
12
|
+
|
13
|
+
# def create?
|
14
|
+
# false
|
15
|
+
# end
|
16
|
+
|
17
|
+
def update?
|
18
|
+
current_user?
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy?
|
22
|
+
# Prevent admins from destroying themselves
|
23
|
+
current_user? && !record.admin?
|
24
|
+
end
|
25
|
+
|
26
|
+
def permitted_attributes
|
27
|
+
if current_user?
|
28
|
+
# Allow a user to update their own details
|
29
|
+
%i[name]
|
30
|
+
else
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def current_user?
|
38
|
+
user.persisted? && user.id == record.id
|
39
|
+
end
|
40
|
+
|
41
|
+
# Safe scope for User
|
42
|
+
class Scope < Scope
|
43
|
+
def resolve
|
44
|
+
user.persisted? ? scope.where(id: user.id) : scope.none
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
<% module_namespacing do -%>
|
4
|
+
class <%= class_name %>Policy < ApplicationPolicy
|
5
|
+
def index?
|
6
|
+
user.persisted?
|
7
|
+
end
|
8
|
+
|
9
|
+
def show?
|
10
|
+
user.persisted?
|
11
|
+
end
|
12
|
+
|
13
|
+
def create?
|
14
|
+
user.admin?
|
15
|
+
end
|
16
|
+
|
17
|
+
def update?
|
18
|
+
user.admin?
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy?
|
22
|
+
user.admin?
|
23
|
+
end
|
24
|
+
|
25
|
+
def permitted_attributes
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
# Safe scope for <%= class_name %>
|
30
|
+
class Scope < Scope
|
31
|
+
def resolve
|
32
|
+
user.persisted? ? scope.all : scope.none
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
<% end -%>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
|
5
|
+
RSpec.describe <%= class_name %>Policy, type: :policy do
|
6
|
+
subject { described_class.new(user, <%= file_name %>_record) }
|
7
|
+
|
8
|
+
let(:resolved_scope) { described_class::Scope.new(user, <%= class_name %>.all).resolve }
|
9
|
+
|
10
|
+
let(:<%= file_name %>_record) { <%= class_name %>.new }
|
11
|
+
|
12
|
+
context "with a guest" do
|
13
|
+
let(:user) { nil }
|
14
|
+
|
15
|
+
it { is_expected.to forbid_action(:index) }
|
16
|
+
it { is_expected.to forbid_action(:show) }
|
17
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
18
|
+
it { is_expected.to forbid_actions(%i[edit update]) }
|
19
|
+
it { is_expected.to forbid_action(:destroy) }
|
20
|
+
|
21
|
+
it "returns no items in scope" do
|
22
|
+
expect(resolved_scope.to_sql).to eq(<%= class_name %>.none.to_sql)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a user" do
|
27
|
+
let(:user) { build_stubbed(:user) }
|
28
|
+
|
29
|
+
it { is_expected.to permit_action(:index) }
|
30
|
+
it { is_expected.to permit_action(:show) }
|
31
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
32
|
+
it { is_expected.to forbid_actions(%i[edit update]) }
|
33
|
+
it { is_expected.to forbid_action(:destroy) }
|
34
|
+
|
35
|
+
it "returns all items in scope" do
|
36
|
+
expect(resolved_scope.to_sql).to eq(<%= class_name %>.all.to_sql)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with an admin" do
|
41
|
+
let(:user) { build_stubbed(:user, :admin) }
|
42
|
+
|
43
|
+
it { is_expected.to permit_action(:index) }
|
44
|
+
it { is_expected.to permit_action(:show) }
|
45
|
+
it { is_expected.to permit_actions(%i[new create]) }
|
46
|
+
it { is_expected.to permit_actions(%i[edit update]) }
|
47
|
+
it { is_expected.to permit_action(:destroy) }
|
48
|
+
|
49
|
+
# it { is_expected.to permit_mass_assignment_of(:name).for_action(:create) }
|
50
|
+
|
51
|
+
# it { is_expected.to permit_mass_assignment_of(:name).for_action(:update) }
|
52
|
+
# it { is_expected.to forbid_mass_assignment_of(:birthdate).for_action(:update) }
|
53
|
+
|
54
|
+
it "returns all items in scope" do
|
55
|
+
expect(resolved_scope.to_sql).to eq(<%= class_name %>.all.to_sql)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
|
5
|
+
RSpec.describe UserPolicy, type: :policy do
|
6
|
+
subject { described_class.new(user, user_record) }
|
7
|
+
|
8
|
+
let(:resolved_scope) { described_class::Scope.new(user, User.all).resolve }
|
9
|
+
|
10
|
+
let(:user_record) do
|
11
|
+
build_stubbed(:user).tap do |record|
|
12
|
+
allow(record).to receive(:id).and_return(123)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with a guest" do
|
17
|
+
let(:user) { nil }
|
18
|
+
|
19
|
+
it { is_expected.to forbid_action(:index) }
|
20
|
+
it { is_expected.to forbid_action(:show) }
|
21
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
22
|
+
it { is_expected.to forbid_actions(%i[edit update]) }
|
23
|
+
it { is_expected.to forbid_action(:destroy) }
|
24
|
+
|
25
|
+
it "returns no items in scope" do
|
26
|
+
expect(resolved_scope.to_sql).to eq(User.none.to_sql)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with the same user" do
|
31
|
+
let(:user) { user_record }
|
32
|
+
|
33
|
+
it { is_expected.to permit_actions(%i[edit update]) }
|
34
|
+
it { is_expected.to permit_action(:destroy) }
|
35
|
+
|
36
|
+
it { is_expected.to forbid_action(:index) }
|
37
|
+
it { is_expected.to forbid_action(:show) }
|
38
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
39
|
+
|
40
|
+
it { is_expected.to permit_mass_assignment_of(:name).for_action(:update) }
|
41
|
+
|
42
|
+
it "returns the a scope with only the user" do
|
43
|
+
expect(resolved_scope.to_sql).to eq(User.where(id: user.id).to_sql)
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when the user is an admin" do
|
47
|
+
before do
|
48
|
+
user.admin = true
|
49
|
+
end
|
50
|
+
|
51
|
+
it { is_expected.to permit_actions(%i[edit update]) }
|
52
|
+
|
53
|
+
it { is_expected.to forbid_action(:index) }
|
54
|
+
it { is_expected.to forbid_action(:show) }
|
55
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
56
|
+
it { is_expected.to forbid_action(:destroy) }
|
57
|
+
|
58
|
+
it { is_expected.to permit_mass_assignment_of(:name).for_action(:update) }
|
59
|
+
|
60
|
+
it "returns the a scope with only the user" do
|
61
|
+
expect(resolved_scope.to_sql).to eq(User.where(id: user.id).to_sql)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with a different user" do
|
67
|
+
let(:user) do
|
68
|
+
build_stubbed(:user).tap do |user|
|
69
|
+
allow(user).to receive(:id).and_return(987)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it { is_expected.to forbid_action(:index) }
|
74
|
+
it { is_expected.to forbid_action(:show) }
|
75
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
76
|
+
it { is_expected.to forbid_actions(%i[edit update]) }
|
77
|
+
it { is_expected.to forbid_action(:destroy) }
|
78
|
+
|
79
|
+
it { is_expected.to forbid_mass_assignment_of(:name).for_action(:update) }
|
80
|
+
|
81
|
+
context "when the user is an admin" do
|
82
|
+
before do
|
83
|
+
user.admin = true
|
84
|
+
end
|
85
|
+
|
86
|
+
it { is_expected.to forbid_action(:index) }
|
87
|
+
it { is_expected.to forbid_action(:show) }
|
88
|
+
it { is_expected.to forbid_actions(%i[new create]) }
|
89
|
+
it { is_expected.to forbid_actions(%i[edit update]) }
|
90
|
+
it { is_expected.to forbid_action(:destroy) }
|
91
|
+
|
92
|
+
it { is_expected.to forbid_mass_assignment_of(:name).for_action(:update) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Description:
|
2
|
+
Adds code quality tools to a Rails project.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate quality
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
.rubocop.yml # with common default values
|
9
|
+
.rubocop_todo.yml # a todo-list of all outstanding issues
|
10
|
+
lib/tasks/quality.rake # A Rake task for running code quality tools
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rockstart::QualityGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
desc "This generator configures code quality rules"
|
7
|
+
|
8
|
+
class_option :rebuild_todo, type: :boolean,
|
9
|
+
desc: "Bundle installs rubocop and regenerates .rubocop_todo.yml",
|
10
|
+
default: true
|
11
|
+
|
12
|
+
def add_quality_rake_task
|
13
|
+
copy_file "quality.rake", "lib/tasks/quality.rake"
|
14
|
+
end
|
15
|
+
|
16
|
+
def install_rubocop
|
17
|
+
copy_file "rubocop.yml", ".rubocop.yml"
|
18
|
+
|
19
|
+
gem "rubocop-rails", require: false
|
20
|
+
|
21
|
+
copy_file "rubocop.rake", "lib/tasks/rubocop.rake"
|
22
|
+
|
23
|
+
return unless options[:rebuild_todo]
|
24
|
+
|
25
|
+
# Rebuild .rubocop_todo.yml, ensuring only existing code is excluded
|
26
|
+
run "bundle install --quiet && bundle exec rubocop --auto-gen-config --exclude-limit 100"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
Exclude:
|
5
|
+
# Exclude auto-generated files
|
6
|
+
- bin/*
|
7
|
+
- db/schema.rb
|
8
|
+
- db/seeds.rb
|
9
|
+
- lib/templates/**/*
|
10
|
+
- node_modules/**/*
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 100 # Set line width to more sensible default
|
14
|
+
Exclude:
|
15
|
+
- config/environments/*
|
16
|
+
- config/initializers/*
|
17
|
+
- db/migrate/*
|
18
|
+
- spec/**/*
|
19
|
+
- test/**/*
|
20
|
+
|
21
|
+
Lint/RaiseException:
|
22
|
+
Enabled: true
|
23
|
+
|
24
|
+
Lint/StructNewOverride:
|
25
|
+
Enabled: true
|
26
|
+
|
27
|
+
Metrics/BlockLength:
|
28
|
+
Exclude:
|
29
|
+
- spec/**/*
|
30
|
+
- test/**/*
|
31
|
+
|
32
|
+
Style/StringLiterals:
|
33
|
+
EnforcedStyle: double_quotes # prefer double quotes
|
34
|
+
|
35
|
+
Style/FrozenStringLiteralComment:
|
36
|
+
EnforcedStyle: always_true # immutable code where possible
|
37
|
+
|
38
|
+
Style/HashEachMethods:
|
39
|
+
Enabled: false # Skip unsafe rules
|
40
|
+
|
41
|
+
Style/HashTransformKeys:
|
42
|
+
Enabled: false # Skip unsafe rules
|
43
|
+
|
44
|
+
Style/HashTransformValues:
|
45
|
+
Enabled: false # Skip unsafe rules
|