staples 0.1.0 → 1.0.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 +8 -1
- data/README.md +225 -5
- data/lib/staples/cli.rb +25 -2
- data/lib/staples/version.rb +3 -1
- data/lib/staples.rb +6 -1
- data/lib/templates/Procfile +3 -0
- data/lib/templates/README.md +127 -0
- data/lib/templates/app/controllers/pages_controller.rb +3 -0
- data/lib/templates/app/models/membership.rb +4 -0
- data/lib/templates/app/models/organization.rb +3 -0
- data/lib/templates/app/models/user/account.rb +24 -0
- data/lib/templates/app/models/user.rb +3 -0
- data/lib/templates/app/views/application/_card.html.erb +5 -0
- data/lib/templates/app/views/application/_error_messages.html.erb +11 -0
- data/lib/templates/app/views/application/_flashes.html.erb +10 -0
- data/lib/templates/app/views/application/_nav.html.erb +27 -0
- data/lib/templates/app/views/devise/confirmations/new.html.erb +22 -0
- data/lib/templates/app/views/devise/passwords/edit.html.erb +31 -0
- data/lib/templates/app/views/devise/passwords/new.html.erb +22 -0
- data/lib/templates/app/views/devise/registrations/edit.html.erb +50 -0
- data/lib/templates/app/views/devise/registrations/new.html.erb +35 -0
- data/lib/templates/app/views/devise/sessions/new.html.erb +32 -0
- data/lib/templates/app/views/devise/shared/_links.html.erb +27 -0
- data/lib/templates/app/views/pages/home.html.erb +21 -0
- data/lib/templates/base.rb +246 -0
- data/lib/templates/config/initializers/high_voltage.rb +3 -0
- data/lib/templates/config/initializers/sidekiq.rb +12 -0
- data/lib/templates/db/migrate/20251121192825_devise_create_users.rb +37 -0
- data/lib/templates/db/migrate/20251122141432_create_organizations.rb +7 -0
- data/lib/templates/db/migrate/20251122141520_create_memberships.rb +11 -0
- data/lib/templates/lib/development/seeder.rb +10 -0
- data/lib/templates/lib/tasks/development.rake +15 -0
- data/lib/templates/test/controllers/devise/registrations_controller_test.rb +40 -0
- data/lib/templates/test/factories/memberships.rb +6 -0
- data/lib/templates/test/factories/organizations.rb +4 -0
- data/lib/templates/test/factories/users.rb +12 -0
- data/lib/templates/test/models/membership_test.rb +13 -0
- data/lib/templates/test/models/organization_test.rb +19 -0
- data/lib/templates/test/models/user_test.rb +36 -0
- data/lib/templates/test/system/authentication_stories_test.rb +72 -0
- metadata +36 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% content_for :title, "Forgot your password?" %>
|
|
2
|
+
|
|
3
|
+
<h1 id="main_label" class="text-center"><%= content_for :title %></h1>
|
|
4
|
+
|
|
5
|
+
<div class="mx-auto w-100" style="max-width: 400px;">
|
|
6
|
+
<%= render "card" do %>
|
|
7
|
+
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
|
|
8
|
+
<%= render "error_messages", resource: resource %>
|
|
9
|
+
|
|
10
|
+
<div class="mb-3">
|
|
11
|
+
<%= f.label :email, class: "form-label" %>
|
|
12
|
+
<%= f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" %>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div class="mb-3">
|
|
16
|
+
<%= f.submit "Send me password reset instructions", class: "btn btn-primary" %>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<%= render "devise/shared/links" %>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<% content_for :title, "Edit #{resource_name.to_s.humanize}" %>
|
|
2
|
+
|
|
3
|
+
<h1 id="main_label" class="text-center"><%= content_for :title %></h1>
|
|
4
|
+
|
|
5
|
+
<div class="mx-auto w-100" style="max-width: 400px;">
|
|
6
|
+
<%= render "card" do %>
|
|
7
|
+
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
|
|
8
|
+
<%= render "error_messages", resource: resource %>
|
|
9
|
+
|
|
10
|
+
<div class="mb-3">
|
|
11
|
+
<%= f.label :email, class: "form-label" %>
|
|
12
|
+
<%= f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" %>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
|
|
16
|
+
<div class="alert alert-info">Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<div class="mb-3">
|
|
20
|
+
<%= f.label :password, class: "form-label" %>
|
|
21
|
+
<div class="form-text">Leave blank if you don't want to change it</div>
|
|
22
|
+
<%= f.password_field :password, autocomplete: "new-password", class: "form-control" %>
|
|
23
|
+
<% if @minimum_password_length %>
|
|
24
|
+
<div class="form-text"><%= @minimum_password_length %> characters minimum</div>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="mb-3">
|
|
29
|
+
<%= f.label :password_confirmation, class: "form-label" %>
|
|
30
|
+
<%= f.password_field :password_confirmation, autocomplete: "new-password", class: "form-control" %>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="mb-3">
|
|
34
|
+
<%= f.label :current_password, class: "form-label" %>
|
|
35
|
+
<div class="form-text">We need your current password to confirm your changes</div>
|
|
36
|
+
<%= f.password_field :current_password, autocomplete: "current-password", class: "form-control" %>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="mb-3">
|
|
40
|
+
<%= f.submit "Update", class: "btn btn-primary" %>
|
|
41
|
+
</div>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
44
|
+
<h2 class="mt-5">Cancel my account</h2>
|
|
45
|
+
|
|
46
|
+
<div class="mb-3">Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete, class: "btn btn-danger" %></div>
|
|
47
|
+
|
|
48
|
+
<%= link_to "Back", :back, class: "btn btn-link" %>
|
|
49
|
+
<% end %>
|
|
50
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<% content_for :title, "Sign up" %>
|
|
2
|
+
|
|
3
|
+
<h1 id="main_label" class="text-center"><%= content_for :title %></h1>
|
|
4
|
+
|
|
5
|
+
<div class="mx-auto w-100" style="max-width: 400px;">
|
|
6
|
+
<%= render "card" do %>
|
|
7
|
+
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
|
|
8
|
+
<%= render "error_messages", resource: resource %>
|
|
9
|
+
|
|
10
|
+
<div class="mb-3">
|
|
11
|
+
<%= f.label :email, class: "form-label" %>
|
|
12
|
+
<%= f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" %>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div class="mb-3">
|
|
16
|
+
<%= f.label :password, class: "form-label" %>
|
|
17
|
+
<% if @minimum_password_length %>
|
|
18
|
+
<div class="form-text"><%= @minimum_password_length %> characters minimum</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
<%= f.password_field :password, autocomplete: "new-password", class: "form-control" %>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="mb-3">
|
|
24
|
+
<%= f.label :password_confirmation, class: "form-label" %>
|
|
25
|
+
<%= f.password_field :password_confirmation, autocomplete: "new-password", class: "form-control" %>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="mb-3">
|
|
29
|
+
<%= f.submit "Sign up", class: "btn btn-primary" %>
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<%= render "devise/shared/links" %>
|
|
34
|
+
<% end %>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<% content_for :title, "Log in" %>
|
|
2
|
+
|
|
3
|
+
<h1 id="main_label" class="text-center"><%= content_for :title %></h1>
|
|
4
|
+
|
|
5
|
+
<div class="mx-auto w-100" style="max-width: 400px;">
|
|
6
|
+
<%= render "card" do %>
|
|
7
|
+
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
|
8
|
+
<div class="mb-3">
|
|
9
|
+
<%= f.label :email, class: "form-label" %>
|
|
10
|
+
<%= f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" %>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="mb-3">
|
|
14
|
+
<%= f.label :password, class: "form-label" %>
|
|
15
|
+
<%= f.password_field :password, autocomplete: "current-password", class: "form-control" %>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<% if devise_mapping.rememberable? %>
|
|
19
|
+
<div class="mb-3 form-check">
|
|
20
|
+
<%= f.check_box :remember_me, class: "form-check-input" %>
|
|
21
|
+
<%= f.label :remember_me, class: "form-check-label" %>
|
|
22
|
+
</div>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<div class="mb-3">
|
|
26
|
+
<%= f.submit "Log in", class: "btn btn-primary" %>
|
|
27
|
+
</div>
|
|
28
|
+
<% end %>
|
|
29
|
+
|
|
30
|
+
<%= render "devise/shared/links" %>
|
|
31
|
+
<% end %>
|
|
32
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<div class="mt-3">
|
|
2
|
+
<%- if controller_name != 'sessions' %>
|
|
3
|
+
<div class="mb-2"><%= link_to "Log in", new_session_path(resource_name) %></div>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
|
|
7
|
+
<div class="mb-2"><%= link_to "Sign up", new_registration_path(resource_name) %></div>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
|
11
|
+
<div class="mb-2"><%= link_to "Forgot your password?", new_password_path(resource_name) %></div>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
|
15
|
+
<div class="mb-2"><%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %></div>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
|
19
|
+
<div class="mb-2"><%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %></div>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<%- if devise_mapping.omniauthable? %>
|
|
23
|
+
<%- resource_class.omniauth_providers.each do |provider| %>
|
|
24
|
+
<div class="mb-2"><%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false }, class: "btn btn-outline-secondary" %></div>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<% content_for :title, "Hello world!" %>
|
|
2
|
+
|
|
3
|
+
<h1 id="main_label"><%= content_for :title %></h1>
|
|
4
|
+
|
|
5
|
+
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
|
|
6
|
+
Click Here
|
|
7
|
+
</button>
|
|
8
|
+
|
|
9
|
+
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
|
10
|
+
<div class="modal-dialog">
|
|
11
|
+
<div class="modal-content">
|
|
12
|
+
<div class="modal-header">
|
|
13
|
+
<h2 class="modal-title fs-5" id="exampleModalLabel">Hello world!</h2>
|
|
14
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="modal-body">
|
|
17
|
+
If you can read this, Staples has successfully installed.
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
def source_paths
|
|
2
|
+
Array(super) + [__dir__]
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
def install_gems
|
|
6
|
+
gem "active_link_to"
|
|
7
|
+
gem "devise", github: "heartcombo/devise"
|
|
8
|
+
gem "high_voltage"
|
|
9
|
+
gem "sidekiq"
|
|
10
|
+
gem "strong_migrations"
|
|
11
|
+
|
|
12
|
+
gem_group :development, :test do
|
|
13
|
+
gem "capybara-email"
|
|
14
|
+
gem "factory_bot_rails"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
gem_group :test do
|
|
18
|
+
gem "capybara_accessibility_audit"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
install_gems
|
|
23
|
+
|
|
24
|
+
after_bundle do
|
|
25
|
+
# Generators
|
|
26
|
+
generate_devise
|
|
27
|
+
generate_strong_migrations
|
|
28
|
+
|
|
29
|
+
# Initializers & Configuration
|
|
30
|
+
configure_environments
|
|
31
|
+
configure_database
|
|
32
|
+
add_high_voltage_initializer
|
|
33
|
+
add_sidekiq_initializer
|
|
34
|
+
|
|
35
|
+
# CI
|
|
36
|
+
configure_ci
|
|
37
|
+
|
|
38
|
+
# Database
|
|
39
|
+
add_migrations
|
|
40
|
+
|
|
41
|
+
# Deployment
|
|
42
|
+
add_procfiles
|
|
43
|
+
|
|
44
|
+
# Application Code
|
|
45
|
+
add_application_code
|
|
46
|
+
configure_routes
|
|
47
|
+
add_rake_tasks
|
|
48
|
+
|
|
49
|
+
# Test Suite
|
|
50
|
+
add_test_suite
|
|
51
|
+
|
|
52
|
+
# Assets
|
|
53
|
+
configure_sass
|
|
54
|
+
|
|
55
|
+
# Finalization
|
|
56
|
+
run_migrations
|
|
57
|
+
update_readme
|
|
58
|
+
lint_codebase
|
|
59
|
+
|
|
60
|
+
print_message
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generate_devise
|
|
64
|
+
rails_command "generate devise:install"
|
|
65
|
+
gsub_file "config/initializers/devise.rb", /config\.mailer_sender = ['"]please-change-me-at-config-initializers-devise@example\.com['"]/, 'config.mailer_sender = ENV.fetch("MAILER_SENDER", "contact@example.com")'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def generate_strong_migrations
|
|
69
|
+
rails_command "generate strong_migrations:install"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def configure_ci
|
|
73
|
+
uncomment_lines ".github/workflows/ci.yml", /RAILS_MASTER_KEY/
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def add_migrations
|
|
77
|
+
copy_file "db/migrate/20251121192825_devise_create_users.rb"
|
|
78
|
+
copy_file "db/migrate/20251122141432_create_organizations.rb"
|
|
79
|
+
copy_file "db/migrate/20251122141520_create_memberships.rb"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add_procfiles
|
|
83
|
+
copy_file "Procfile"
|
|
84
|
+
append_to_file "Procfile.dev", "worker: bundle exec sidekiq -c 10"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def configure_database
|
|
88
|
+
gsub_file "config/database.yml", /^production:.*?password:.*?\n/m, <<~YAML
|
|
89
|
+
production:
|
|
90
|
+
<<: *default
|
|
91
|
+
url: <%= ENV["DATABASE_URL"] %>
|
|
92
|
+
YAML
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def add_application_code
|
|
96
|
+
# Controllers and Pages
|
|
97
|
+
copy_file "app/controllers/pages_controller.rb"
|
|
98
|
+
copy_file "app/views/pages/home.html.erb"
|
|
99
|
+
|
|
100
|
+
# Models
|
|
101
|
+
copy_file "app/models/membership.rb"
|
|
102
|
+
copy_file "app/models/organization.rb"
|
|
103
|
+
copy_file "app/models/user.rb"
|
|
104
|
+
copy_file "app/models/user/account.rb"
|
|
105
|
+
|
|
106
|
+
# Application Partials
|
|
107
|
+
copy_file "app/views/application/_card.html.erb"
|
|
108
|
+
copy_file "app/views/application/_error_messages.html.erb"
|
|
109
|
+
copy_file "app/views/application/_flashes.html.erb"
|
|
110
|
+
copy_file "app/views/application/_nav.html.erb"
|
|
111
|
+
|
|
112
|
+
# Devise Views
|
|
113
|
+
copy_file "app/views/devise/confirmations/new.html.erb"
|
|
114
|
+
copy_file "app/views/devise/passwords/edit.html.erb"
|
|
115
|
+
copy_file "app/views/devise/passwords/new.html.erb"
|
|
116
|
+
copy_file "app/views/devise/registrations/edit.html.erb"
|
|
117
|
+
copy_file "app/views/devise/registrations/new.html.erb"
|
|
118
|
+
copy_file "app/views/devise/sessions/new.html.erb"
|
|
119
|
+
copy_file "app/views/devise/shared/_links.html.erb"
|
|
120
|
+
|
|
121
|
+
# Application Layout
|
|
122
|
+
gsub_file "app/views/layouts/application.html.erb", /<html>/, "<html lang=\"<%= I18n.locale %>\">"
|
|
123
|
+
application_html_erb = <<-ERB
|
|
124
|
+
<%= render "nav" %>
|
|
125
|
+
<main class="container" aria-labelledby="main_label">
|
|
126
|
+
<%= render "flashes" %>
|
|
127
|
+
<%= yield %>
|
|
128
|
+
</main>
|
|
129
|
+
ERB
|
|
130
|
+
gsub_file "app/views/layouts/application.html.erb", /^ <%= yield %>\n/, application_html_erb
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def configure_environments
|
|
134
|
+
environment "config.active_job.queue_adapter = :sidekiq"
|
|
135
|
+
environment "config.active_record.strict_loading_by_default = true"
|
|
136
|
+
environment "config.active_record.strict_loading_mode = :n_plus_one_only"
|
|
137
|
+
environment "config.require_master_key = true"
|
|
138
|
+
|
|
139
|
+
environment "config.sandbox_by_default = true", env: "production"
|
|
140
|
+
environment "config.active_record.action_on_strict_loading_violation = :log", env: "production"
|
|
141
|
+
gsub_file "config/environments/production.rb", /# config\.asset_host =.*$/, 'config.asset_host = ENV["ASSET_HOST"]'
|
|
142
|
+
gsub_file "config/environments/production.rb", /config\.action_mailer\.default_url_options = \{ host: .*? \}/, 'config.action_mailer.default_url_options = { host: ENV.fetch("APPLICATION_HOST") }'
|
|
143
|
+
|
|
144
|
+
environment "config.active_model.i18n_customize_full_message = true", env: "development"
|
|
145
|
+
uncomment_lines "config/environments/development.rb", /config\.i18n\.raise_on_missing_translations/
|
|
146
|
+
uncomment_lines "config/environments/development.rb", /config\.generators\.apply_rubocop_autocorrect_after_generate!/
|
|
147
|
+
|
|
148
|
+
gsub_file "config/environments/test.rb", /config\.action_dispatch\.show_exceptions = :rescuable/, "config.action_dispatch.show_exceptions = :none"
|
|
149
|
+
gsub_file "config/environments/test.rb", /config\.action_mailer\.default_url_options = \{ host: .*? \}/, "config.action_mailer.default_url_options = { host: \"localhost\", port: 3001 }"
|
|
150
|
+
uncomment_lines "config/environments/test.rb", /config\.i18n\.raise_on_missing_translations/
|
|
151
|
+
environment "config.active_job.queue_adapter = :inline", env: "test"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def add_high_voltage_initializer
|
|
155
|
+
copy_file "config/initializers/high_voltage.rb"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def add_sidekiq_initializer
|
|
159
|
+
copy_file "config/initializers/sidekiq.rb"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def configure_routes
|
|
163
|
+
prepend_to_file "config/routes.rb", "require \"sidekiq/web\"\n\n"
|
|
164
|
+
sidekiq_route = <<-RUBY
|
|
165
|
+
if Rails.env.local?
|
|
166
|
+
mount Sidekiq::Web => "/sidekiq"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
RUBY
|
|
170
|
+
|
|
171
|
+
insert_into_file "config/routes.rb", sidekiq_route, after: "Rails.application.routes.draw do\n # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html\n"
|
|
172
|
+
route "devise_for :users"
|
|
173
|
+
route 'root to: "pages#show", id: "home"'
|
|
174
|
+
gsub_file "config/routes.rb", / # Defines the root path route.*\n # root "posts#index"\n/, ""
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def add_rake_tasks
|
|
178
|
+
copy_file "lib/development/seeder.rb"
|
|
179
|
+
copy_file "lib/tasks/development.rake"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def add_test_suite
|
|
183
|
+
copy_file "test/controllers/devise/registrations_controller_test.rb"
|
|
184
|
+
copy_file "test/factories/memberships.rb"
|
|
185
|
+
copy_file "test/factories/organizations.rb"
|
|
186
|
+
copy_file "test/factories/users.rb"
|
|
187
|
+
copy_file "test/models/membership_test.rb"
|
|
188
|
+
copy_file "test/models/organization_test.rb"
|
|
189
|
+
copy_file "test/models/user_test.rb"
|
|
190
|
+
copy_file "test/system/authentication_stories_test.rb"
|
|
191
|
+
|
|
192
|
+
application_system_test_case = <<-RUBY
|
|
193
|
+
|
|
194
|
+
def sign_in_as(user)
|
|
195
|
+
visit new_user_session_path
|
|
196
|
+
|
|
197
|
+
fill_in "Email", with: user.email
|
|
198
|
+
fill_in "Password", with: user.password
|
|
199
|
+
click_button "Log in"
|
|
200
|
+
|
|
201
|
+
assert_text I18n.translate("devise.sessions.signed_in")
|
|
202
|
+
end
|
|
203
|
+
RUBY
|
|
204
|
+
insert_into_file "test/application_system_test_case.rb", application_system_test_case, before: "end\n"
|
|
205
|
+
|
|
206
|
+
test_helper_config = <<-RUBY
|
|
207
|
+
include Devise::Test::IntegrationHelpers
|
|
208
|
+
include Capybara::Email::DSL
|
|
209
|
+
include FactoryBot::Syntax::Methods
|
|
210
|
+
|
|
211
|
+
Capybara.configure do |config|
|
|
212
|
+
Rails.application.config.action_mailer.default_url_options => { host:, port: }
|
|
213
|
+
|
|
214
|
+
config.server = :puma, { Silent: true }
|
|
215
|
+
config.server_port = port
|
|
216
|
+
config.app_host = "http://\#{host}:\#{port}"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
RUBY
|
|
220
|
+
insert_into_file "test/test_helper.rb", test_helper_config, after: "class TestCase\n"
|
|
221
|
+
insert_into_file "test/test_helper.rb", "require \"capybara/email\"\n", after: "require \"rails/test_help\"\n"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def configure_sass
|
|
225
|
+
gsub_file "package.json", /"build:css:compile": "sass \.\/app\/assets\/stylesheets\/application\.bootstrap\.scss:\.\/app\/assets\/builds\/application\.css --no-source-map --load-path=node_modules"/, '"build:css:compile": "sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules --silence-deprecation=import --silence-deprecation=global-builtin --silence-deprecation=color-functions"'
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def run_migrations
|
|
229
|
+
rails_command "db:create"
|
|
230
|
+
rails_command "db:migrate"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def update_readme
|
|
234
|
+
remove_file "README.md"
|
|
235
|
+
copy_file "README.md"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def lint_codebase
|
|
239
|
+
run "bin/rubocop -a"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def print_message
|
|
243
|
+
say "*" * 50
|
|
244
|
+
say "Installation complete! 🎉"
|
|
245
|
+
say "*" * 50
|
|
246
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
SIDEKIQ_REDIS_CONFIGURATION = {
|
|
2
|
+
url: ENV.fetch(ENV.fetch("REDIS_PROVIDER", "REDIS_URL"), nil), # use REDIS_PROVIDER for Redis environment variable name, defaulting to REDIS_URL
|
|
3
|
+
ssl_params: {verify_mode: OpenSSL::SSL::VERIFY_NONE} # we must trust Heroku and AWS here
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
Sidekiq.configure_server do |config|
|
|
7
|
+
config.redis = SIDEKIQ_REDIS_CONFIGURATION
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Sidekiq.configure_client do |config|
|
|
11
|
+
config.redis = SIDEKIQ_REDIS_CONFIGURATION
|
|
12
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class DeviseCreateUsers < ActiveRecord::Migration[8.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
|
+
t.timestamps null: false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
add_index :users, :email, unique: true
|
|
34
|
+
add_index :users, :reset_password_token, unique: true
|
|
35
|
+
add_index :users, :confirmation_token, unique: true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class CreateMemberships < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :memberships do |t|
|
|
4
|
+
t.belongs_to :user, null: false, foreign_key: true
|
|
5
|
+
t.belongs_to :organization, null: false, foreign_key: true
|
|
6
|
+
t.index [:user_id, :organization_id], unique: true
|
|
7
|
+
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
if Rails.env.local?
|
|
2
|
+
namespace :development do
|
|
3
|
+
namespace :db do
|
|
4
|
+
desc "Loads seed data for the local environment."
|
|
5
|
+
task seed: :environment do
|
|
6
|
+
Development::Seeder.load_seeds
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
namespace :seed do
|
|
10
|
+
desc "Truncate tables of each database for development and loads seed data."
|
|
11
|
+
task replant: ["environment", "db:truncate_all", "development:db:seed"]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
class RegistrationsController::CreateTest < ActionDispatch::IntegrationTest
|
|
5
|
+
test "creates initial organization on successful registration" do
|
|
6
|
+
attributes_for(:user) => {email:, password:}
|
|
7
|
+
|
|
8
|
+
assert_changes -> { User.count }, from: 0, to: 1 do
|
|
9
|
+
assert_changes -> { Organization.count }, from: 0, to: 1 do
|
|
10
|
+
post user_registration_path, params: {
|
|
11
|
+
user: {
|
|
12
|
+
email:,
|
|
13
|
+
password:,
|
|
14
|
+
password_confirmation: password
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
user = User.sole
|
|
21
|
+
organization = Organization.sole
|
|
22
|
+
|
|
23
|
+
assert_equal organization, user.organization
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test "does not create initial organization on unsuccessful registration" do
|
|
27
|
+
assert_no_changes -> { User.count }, from: 0 do
|
|
28
|
+
assert_no_changes -> { Organization.count }, from: 0 do
|
|
29
|
+
post user_registration_path, params: {
|
|
30
|
+
user: {
|
|
31
|
+
email: "",
|
|
32
|
+
password: "",
|
|
33
|
+
password_confirmation: ""
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
sequence(:user_factory_email) { "person-#{_1}@example.com" }
|
|
3
|
+
|
|
4
|
+
factory :user do
|
|
5
|
+
sequence(:email) { "user-#{_1}@example.com" }
|
|
6
|
+
password { "s3kret" } # avoid 'password', since Chrome will render a security dialog
|
|
7
|
+
|
|
8
|
+
trait :confirmed do
|
|
9
|
+
confirmed_at { Time.current }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class Membership::DatabaseConstraintTest < ActiveSupport::TestCase
|
|
4
|
+
test "uniqueness constraint" do
|
|
5
|
+
membership = create(:membership)
|
|
6
|
+
user = membership.user
|
|
7
|
+
organization = membership.organization
|
|
8
|
+
|
|
9
|
+
assert_raises ActiveRecord::RecordNotUnique do
|
|
10
|
+
create(:membership, user:, organization:)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class Organization::DestroyTest < ActiveSupport::TestCase
|
|
4
|
+
test "raises" do
|
|
5
|
+
user = create(:user)
|
|
6
|
+
|
|
7
|
+
assert_raise ActiveRecord::DeleteRestrictionError do
|
|
8
|
+
user.organization.destroy
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
test "destroys" do
|
|
13
|
+
organization = create(:organization)
|
|
14
|
+
|
|
15
|
+
assert_changes -> { Organization.count }, from: 1, to: 0 do
|
|
16
|
+
organization.destroy
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|