diff --git a/book/code_smells/duplicated_code.md b/book/code_smells/duplicated_code.md
index d135ece..43e78f8 100644
--- a/book/code_smells/duplicated_code.md
+++ b/book/code_smells/duplicated_code.md
@@ -37,7 +37,7 @@ def update
question_params = params.
require(:question).
permit(:title, :options_attributes, :minimum, :maximum)
- @question.update_attributes(question_params)
+ @question.update(question_params)
if @question.save
redirect_to @question.survey
diff --git a/book/solutions/extract_validator.md b/book/solutions/extract_validator.md
index 419af27..949c104 100644
--- a/book/solutions/extract_validator.md
+++ b/book/solutions/extract_validator.md
@@ -58,6 +58,10 @@ is validated against it.
` app/validators/enumerable_validator.rb@21f7a57
+Please note that in the latest version of the example application the
+`EmailValidator` class was renamed to `EmailAddressValidator` to avoid a naming
+conflict with an external gem.
+
### Next Steps
* Verify the extracted validator does not have any [long methods](#long-methods).
diff --git a/book/solutions/replace_conditional_with_polymorphism.md b/book/solutions/replace_conditional_with_polymorphism.md
index 36c2f4e..91c8872 100644
--- a/book/solutions/replace_conditional_with_polymorphism.md
+++ b/book/solutions/replace_conditional_with_polymorphism.md
@@ -153,7 +153,7 @@ to the appropriate class. First, let's move the method
class MultipleChoiceQuestion < Question
def summary
total = answers.count
- counts = answers.group(:text).order('COUNT(*) DESC').count
+ counts = answers.group(:text).order(Arel.sql('COUNT(*) DESC')).count
percents = counts.map do |text, count|
percent = (100.0 * count / total).round
"#{percent}% #{text}"
diff --git a/example_app/.gitignore b/example_app/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/example_app/.ruby-version b/example_app/.ruby-version
deleted file mode 100644
index c031dda..0000000
--- a/example_app/.ruby-version
+++ /dev/null
@@ -1 +0,0 @@
-2.0.0-p0
diff --git a/example_app/.tool-versions b/example_app/.tool-versions
new file mode 100644
index 0000000..f2a971a
--- /dev/null
+++ b/example_app/.tool-versions
@@ -0,0 +1 @@
+ruby 3.2.2
diff --git a/example_app/Gemfile b/example_app/Gemfile
index 41c57f7..de5ca3d 100644
--- a/example_app/Gemfile
+++ b/example_app/Gemfile
@@ -1,29 +1,29 @@
source 'https://rubygems.org'
-gem 'rails', '3.2.8'
+ruby '3.2.2'
+
+gem 'rails', '7.0.8'
gem 'clearance'
-gem 'jquery-rails'
gem 'simple_form'
-gem 'strong_parameters'
gem 'sqlite3'
-gem 'thin'
-
-group :assets do
- gem 'coffee-rails', '~> 3.2'
- gem 'sass-rails', '~> 3.2'
- gem 'uglifier', '>= 1.0'
-end
+gem 'sprockets-rails'
+gem 'puma'
+gem 'importmap-rails'
+gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
+gem 'bootsnap', require: false
group :development, :test do
- gem 'bourne'
+ gem 'bourne', '1.3.0'
gem 'foreman'
gem 'rspec-rails'
+ gem 'rails-dom-testing'
+ gem 'listen'
end
group :test do
gem 'capybara'
gem 'email_spec'
- gem 'factory_girl_rails'
+ gem 'factory_bot_rails'
gem 'shoulda-matchers'
end
diff --git a/example_app/Gemfile.lock b/example_app/Gemfile.lock
index 3cd0f80..bca0f45 100644
--- a/example_app/Gemfile.lock
+++ b/example_app/Gemfile.lock
@@ -1,194 +1,271 @@
GEM
remote: https://rubygems.org/
specs:
- actionmailer (3.2.8)
- actionpack (= 3.2.8)
- mail (~> 2.4.4)
- actionpack (3.2.8)
- activemodel (= 3.2.8)
- activesupport (= 3.2.8)
- builder (~> 3.0.0)
- erubis (~> 2.7.0)
- journey (~> 1.0.4)
- rack (~> 1.4.0)
- rack-cache (~> 1.2)
- rack-test (~> 0.6.1)
- sprockets (~> 2.1.3)
- activemodel (3.2.8)
- activesupport (= 3.2.8)
- builder (~> 3.0.0)
- activerecord (3.2.8)
- activemodel (= 3.2.8)
- activesupport (= 3.2.8)
- arel (~> 3.0.2)
- tzinfo (~> 0.3.29)
- activeresource (3.2.8)
- activemodel (= 3.2.8)
- activesupport (= 3.2.8)
- activesupport (3.2.8)
- i18n (~> 0.6)
- multi_json (~> 1.0)
- addressable (2.3.2)
- arel (3.0.2)
- bourne (1.2.1)
- mocha (= 0.12.7)
- builder (3.0.4)
- capybara (2.0.1)
- mime-types (>= 1.16)
- nokogiri (>= 1.3.3)
- rack (>= 1.0.0)
- rack-test (>= 0.5.4)
- selenium-webdriver (~> 2.0)
- xpath (~> 1.0.0)
- childprocess (0.3.6)
- ffi (~> 1.0, >= 1.0.6)
- clearance (0.16.3)
- diesel (~> 0.1.5)
- rails (>= 3.0)
- coffee-rails (3.2.2)
- coffee-script (>= 2.2.0)
- railties (~> 3.2.0)
- coffee-script (2.2.0)
- coffee-script-source
- execjs
- coffee-script-source (1.4.0)
- daemons (1.1.9)
- diesel (0.1.5)
- railties
- diff-lcs (1.1.3)
- email_spec (1.4.0)
+ actioncable (7.0.8)
+ actionpack (= 7.0.8)
+ activesupport (= 7.0.8)
+ nio4r (~> 2.0)
+ websocket-driver (>= 0.6.1)
+ actionmailbox (7.0.8)
+ actionpack (= 7.0.8)
+ activejob (= 7.0.8)
+ activerecord (= 7.0.8)
+ activestorage (= 7.0.8)
+ activesupport (= 7.0.8)
+ mail (>= 2.7.1)
+ net-imap
+ net-pop
+ net-smtp
+ actionmailer (7.0.8)
+ actionpack (= 7.0.8)
+ actionview (= 7.0.8)
+ activejob (= 7.0.8)
+ activesupport (= 7.0.8)
+ mail (~> 2.5, >= 2.5.4)
+ net-imap
+ net-pop
+ net-smtp
+ rails-dom-testing (~> 2.0)
+ actionpack (7.0.8)
+ actionview (= 7.0.8)
+ activesupport (= 7.0.8)
+ rack (~> 2.0, >= 2.2.4)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
+ actiontext (7.0.8)
+ actionpack (= 7.0.8)
+ activerecord (= 7.0.8)
+ activestorage (= 7.0.8)
+ activesupport (= 7.0.8)
+ globalid (>= 0.6.0)
+ nokogiri (>= 1.8.5)
+ actionview (7.0.8)
+ activesupport (= 7.0.8)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ activejob (7.0.8)
+ activesupport (= 7.0.8)
+ globalid (>= 0.3.6)
+ activemodel (7.0.8)
+ activesupport (= 7.0.8)
+ activerecord (7.0.8)
+ activemodel (= 7.0.8)
+ activesupport (= 7.0.8)
+ activestorage (7.0.8)
+ actionpack (= 7.0.8)
+ activejob (= 7.0.8)
+ activerecord (= 7.0.8)
+ activesupport (= 7.0.8)
+ marcel (~> 1.0)
+ mini_mime (>= 1.1.0)
+ activesupport (7.0.8)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ addressable (2.8.5)
+ public_suffix (>= 2.0.2, < 6.0)
+ argon2 (2.3.0)
+ ffi (~> 1.15)
+ ffi-compiler (~> 1.0)
+ bcrypt (3.1.19)
+ bootsnap (1.13.0)
+ msgpack (~> 1.2)
+ bourne (1.3.0)
+ mocha (= 0.13.0)
+ builder (3.2.4)
+ capybara (3.39.2)
+ addressable
+ matrix
+ mini_mime (>= 0.1.3)
+ nokogiri (~> 1.8)
+ rack (>= 1.6.0)
+ rack-test (>= 0.6.3)
+ regexp_parser (>= 1.5, < 3.0)
+ xpath (~> 3.2)
+ clearance (2.6.1)
+ actionmailer (>= 5.0)
+ activemodel (>= 5.0)
+ activerecord (>= 5.0)
+ argon2 (~> 2.0, >= 2.0.2)
+ bcrypt (>= 3.1.1)
+ email_validator (~> 2.0)
+ railties (>= 5.0)
+ concurrent-ruby (1.2.2)
+ crass (1.0.6)
+ date (3.3.3)
+ diff-lcs (1.5.0)
+ email_spec (2.2.2)
+ htmlentities (~> 4.3.3)
launchy (~> 2.1)
- mail (~> 2.2)
- erubis (2.7.0)
- eventmachine (1.0.3)
- execjs (1.4.0)
- multi_json (~> 1.0)
- factory_girl (4.1.0)
- activesupport (>= 3.0.0)
- factory_girl_rails (4.1.0)
- factory_girl (~> 4.1.0)
- railties (>= 3.0.0)
- ffi (1.2.0)
+ mail (~> 2.7)
+ email_validator (2.2.4)
+ activemodel
+ erubi (1.12.0)
+ factory_bot (6.2.1)
+ activesupport (>= 5.0.0)
+ factory_bot_rails (6.2.0)
+ factory_bot (~> 6.2.0)
+ railties (>= 5.0.0)
+ ffi (1.16.3)
+ ffi-compiler (1.0.1)
+ ffi (>= 1.0.0)
+ rake
foreman (0.62.0)
thor (>= 0.13.6)
- hike (1.2.1)
- i18n (0.6.1)
- journey (1.0.4)
- jquery-rails (2.1.3)
- railties (>= 3.1.0, < 5.0)
- thor (~> 0.14)
- json (1.7.5)
- launchy (2.1.2)
- addressable (~> 2.3)
- libwebsocket (0.1.6.1)
- websocket
- mail (2.4.4)
- i18n (>= 0.4.0)
- mime-types (~> 1.16)
- treetop (~> 1.4.8)
- metaclass (0.0.1)
- mime-types (1.19)
- mocha (0.12.7)
+ globalid (1.2.1)
+ activesupport (>= 6.1)
+ htmlentities (4.3.4)
+ i18n (1.14.1)
+ concurrent-ruby (~> 1.0)
+ importmap-rails (1.2.1)
+ actionpack (>= 6.0.0)
+ railties (>= 6.0.0)
+ launchy (2.5.2)
+ addressable (~> 2.8)
+ listen (3.8.0)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ loofah (2.19.1)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ mail (2.8.1)
+ mini_mime (>= 0.1.1)
+ net-imap
+ net-pop
+ net-smtp
+ marcel (1.0.2)
+ matrix (0.4.2)
+ metaclass (0.0.4)
+ method_source (1.0.0)
+ mini_mime (1.1.5)
+ mini_portile2 (2.8.4)
+ minitest (5.20.0)
+ mocha (0.13.0)
metaclass (~> 0.0.1)
- multi_json (1.3.7)
- nokogiri (1.5.5)
- polyglot (0.3.3)
- rack (1.4.1)
- rack-cache (1.2)
- rack (>= 0.4)
- rack-ssl (1.3.2)
- rack
- rack-test (0.6.2)
- rack (>= 1.0)
- rails (3.2.8)
- actionmailer (= 3.2.8)
- actionpack (= 3.2.8)
- activerecord (= 3.2.8)
- activeresource (= 3.2.8)
- activesupport (= 3.2.8)
- bundler (~> 1.0)
- railties (= 3.2.8)
- railties (3.2.8)
- actionpack (= 3.2.8)
- activesupport (= 3.2.8)
- rack-ssl (~> 1.3.2)
- rake (>= 0.8.7)
- rdoc (~> 3.4)
- thor (>= 0.14.6, < 2.0)
- rake (10.0.2)
- rdoc (3.12)
- json (~> 1.4)
- rspec-core (2.12.0)
- rspec-expectations (2.12.0)
- diff-lcs (~> 1.1.3)
- rspec-mocks (2.12.0)
- rspec-rails (2.12.0)
- actionpack (>= 3.0)
- activesupport (>= 3.0)
- railties (>= 3.0)
- rspec-core (~> 2.12.0)
- rspec-expectations (~> 2.12.0)
- rspec-mocks (~> 2.12.0)
- rubyzip (0.9.9)
- sass (3.2.3)
- sass-rails (3.2.5)
- railties (~> 3.2.0)
- sass (>= 3.1.10)
- tilt (~> 1.3)
- selenium-webdriver (2.26.0)
- childprocess (>= 0.2.5)
- libwebsocket (~> 0.1.3)
- multi_json (~> 1.0)
- rubyzip
- shoulda-matchers (1.4.1)
- activesupport (>= 3.0.0)
- simple_form (2.0.4)
- actionpack (~> 3.0)
- activemodel (~> 3.0)
- sprockets (2.1.3)
- hike (~> 1.2)
- rack (~> 1.0)
- tilt (~> 1.1, != 1.3.0)
- sqlite3 (1.3.6)
- strong_parameters (0.1.5)
- actionpack (~> 3.1)
- activemodel (~> 3.1)
- railties (~> 3.1)
- thin (1.5.0)
- daemons (>= 1.0.9)
- eventmachine (>= 0.12.6)
- rack (>= 1.0.0)
- thor (0.16.0)
- tilt (1.3.3)
- treetop (1.4.12)
- polyglot
- polyglot (>= 0.3.1)
- tzinfo (0.3.35)
- uglifier (1.3.0)
- execjs (>= 0.3.0)
- multi_json (~> 1.0, >= 1.0.2)
- websocket (1.0.4)
- xpath (1.0.0)
- nokogiri (~> 1.3)
+ msgpack (1.7.2)
+ net-imap (0.4.0)
+ date
+ net-protocol
+ net-pop (0.1.2)
+ net-protocol
+ net-protocol (0.2.1)
+ timeout
+ net-smtp (0.4.0)
+ net-protocol
+ nio4r (2.5.9)
+ nokogiri (1.15.4)
+ mini_portile2 (~> 2.8.2)
+ racc (~> 1.4)
+ public_suffix (5.0.3)
+ puma (6.4.0)
+ nio4r (~> 2.0)
+ racc (1.7.1)
+ rack (2.2.8)
+ rack-test (2.1.0)
+ rack (>= 1.3)
+ rails (7.0.8)
+ actioncable (= 7.0.8)
+ actionmailbox (= 7.0.8)
+ actionmailer (= 7.0.8)
+ actionpack (= 7.0.8)
+ actiontext (= 7.0.8)
+ actionview (= 7.0.8)
+ activejob (= 7.0.8)
+ activemodel (= 7.0.8)
+ activerecord (= 7.0.8)
+ activestorage (= 7.0.8)
+ activesupport (= 7.0.8)
+ bundler (>= 1.15.0)
+ railties (= 7.0.8)
+ rails-dom-testing (2.2.0)
+ activesupport (>= 5.0.0)
+ minitest
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.5.0)
+ loofah (~> 2.19, >= 2.19.1)
+ railties (7.0.8)
+ actionpack (= 7.0.8)
+ activesupport (= 7.0.8)
+ method_source
+ rake (>= 12.2)
+ thor (~> 1.0)
+ zeitwerk (~> 2.5)
+ rake (13.0.6)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.10.1)
+ ffi (~> 1.0)
+ regexp_parser (2.8.1)
+ rspec-core (3.12.2)
+ rspec-support (~> 3.12.0)
+ rspec-expectations (3.12.3)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-mocks (3.12.6)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-rails (6.0.3)
+ actionpack (>= 6.1)
+ activesupport (>= 6.1)
+ railties (>= 6.1)
+ rspec-core (~> 3.12)
+ rspec-expectations (~> 3.12)
+ rspec-mocks (~> 3.12)
+ rspec-support (~> 3.12)
+ rspec-support (3.12.1)
+ shoulda-matchers (4.5.1)
+ activesupport (>= 4.2.0)
+ simple_form (5.2.0)
+ actionpack (>= 5.2)
+ activemodel (>= 5.2)
+ sprockets (4.2.1)
+ concurrent-ruby (~> 1.0)
+ rack (>= 2.2.4, < 4)
+ sprockets-rails (3.4.2)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
+ sprockets (>= 3.0.0)
+ sqlite3 (1.6.6)
+ mini_portile2 (~> 2.8.0)
+ thor (1.2.2)
+ timeout (0.4.0)
+ tzinfo (2.0.6)
+ concurrent-ruby (~> 1.0)
+ websocket-driver (0.7.6)
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.5)
+ xpath (3.2.0)
+ nokogiri (~> 1.8)
+ zeitwerk (2.6.12)
PLATFORMS
ruby
DEPENDENCIES
- bourne
+ bootsnap
+ bourne (= 1.3.0)
capybara
clearance
- coffee-rails (~> 3.2)
email_spec
- factory_girl_rails
+ factory_bot_rails
foreman
- jquery-rails
- rails (= 3.2.8)
+ importmap-rails
+ listen
+ puma
+ rails (= 7.0.8)
+ rails-dom-testing
rspec-rails
- sass-rails (~> 3.2)
shoulda-matchers
simple_form
+ sprockets-rails
sqlite3
- strong_parameters
- thin
- uglifier (>= 1.0)
+ tzinfo-data
+
+RUBY VERSION
+ ruby 3.2.2p53
+
+BUNDLED WITH
+ 2.3.26
diff --git a/example_app/app/assets/config/manifest.js b/example_app/app/assets/config/manifest.js
new file mode 100644
index 0000000..ddd546a
--- /dev/null
+++ b/example_app/app/assets/config/manifest.js
@@ -0,0 +1,4 @@
+//= link_tree ../images
+//= link_directory ../stylesheets .css
+//= link_tree ../../javascript .js
+//= link_tree ../../../vendor/javascript .js
diff --git a/example_app/app/assets/images/.keep b/example_app/app/assets/images/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/example_app/app/assets/javascripts/application.js b/example_app/app/assets/javascripts/application.js
index 9097d83..15ebed9 100644
--- a/example_app/app/assets/javascripts/application.js
+++ b/example_app/app/assets/javascripts/application.js
@@ -10,6 +10,4 @@
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
-//= require jquery
-//= require jquery_ujs
//= require_tree .
diff --git a/example_app/app/controllers/application_controller.rb b/example_app/app/controllers/application_controller.rb
index c953024..a6b4593 100644
--- a/example_app/app/controllers/application_controller.rb
+++ b/example_app/app/controllers/application_controller.rb
@@ -1,5 +1,5 @@
class ApplicationController < ActionController::Base
- include Clearance::Authentication
+ include Clearance::Controller
protect_from_forgery
- before_filter :authorize
+ before_action :require_login
end
diff --git a/example_app/app/controllers/completions_controller.rb b/example_app/app/controllers/completions_controller.rb
index 9349e50..df48e65 100644
--- a/example_app/app/controllers/completions_controller.rb
+++ b/example_app/app/controllers/completions_controller.rb
@@ -1,7 +1,7 @@
class CompletionsController < ApplicationController
def create
@survey = Survey.find(params[:survey_id])
- completion_params = params.require(:completion).permit(:answers_attributes)
+ completion_params = params.require(:completion).permit(answers_attributes: [:text])
completion = @survey.completions.new(completion_params)
completion.user = current_user
completion.save!
diff --git a/example_app/app/controllers/questions_controller.rb b/example_app/app/controllers/questions_controller.rb
index 70fb08a..88a81df 100644
--- a/example_app/app/controllers/questions_controller.rb
+++ b/example_app/app/controllers/questions_controller.rb
@@ -20,7 +20,7 @@ def edit
def update
@question = Question.find(params[:id])
- @question.update_attributes(question_params)
+ @question.update(question_params)
if @question.save
redirect_to @question.survey
else
@@ -46,7 +46,7 @@ def question_params
def submittable_params
if submittable_attributes = params[:question][:submittable_attributes]
- submittable_attributes.permit(:minimum, :maximum, :options_attributes)
+ submittable_attributes.permit(:minimum, :maximum, options_attributes: [:text])
else
{}
end
diff --git a/example_app/app/controllers/types_controller.rb b/example_app/app/controllers/types_controller.rb
index 206c7d4..f09bcc2 100644
--- a/example_app/app/controllers/types_controller.rb
+++ b/example_app/app/controllers/types_controller.rb
@@ -21,7 +21,11 @@ def create
private
def submittable_attributes
- params[:question][:submittable_attributes] || {}
+ params.require(:question).permit(
+ submittable_attributes: [
+ :minimum, :maximum, :options_attributes
+ ]
+ ).fetch(:submittable_attributes, {})
end
def type
diff --git a/example_app/app/controllers/unsubscribes_controller.rb b/example_app/app/controllers/unsubscribes_controller.rb
index 0843d4f..16e6498 100644
--- a/example_app/app/controllers/unsubscribes_controller.rb
+++ b/example_app/app/controllers/unsubscribes_controller.rb
@@ -1,13 +1,19 @@
class UnsubscribesController < ApplicationController
- skip_before_filter :authorize
+ skip_before_action :require_login
def new
@unsubscribe = Unsubscribe.new(email: params[:email])
end
def create
- @unsubscribe = Unsubscribe.new(params[:unsubscribe])
+ @unsubscribe = Unsubscribe.new(unsubscribe_params)
@unsubscribe.save!
redirect_to root_url
end
+
+ protected
+
+ def unsubscribe_params
+ params.require(:unsubscribe).permit(:email)
+ end
end
diff --git a/example_app/app/javascript/application.js b/example_app/app/javascript/application.js
new file mode 100644
index 0000000..beff742
--- /dev/null
+++ b/example_app/app/javascript/application.js
@@ -0,0 +1 @@
+// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
diff --git a/example_app/app/mailers/mailer.rb b/example_app/app/mailers/mailer.rb
index 3fb23e4..2a01e24 100644
--- a/example_app/app/mailers/mailer.rb
+++ b/example_app/app/mailers/mailer.rb
@@ -13,8 +13,9 @@ def completion_notification(recipient)
def invitation_notification(invitation, body)
mail(
to: invitation.recipient_email,
- subject: 'You have been invited to take an online survey',
- body: body
- )
+ subject: 'You have been invited to take an online survey'
+ ) do |format|
+ format.html { render html: body }
+ end
end
end
diff --git a/example_app/app/mailers/unsubscribeable_mailer.rb b/example_app/app/mailers/unsubscribeable_mailer.rb
index ecf4081..675cca6 100644
--- a/example_app/app/mailers/unsubscribeable_mailer.rb
+++ b/example_app/app/mailers/unsubscribeable_mailer.rb
@@ -14,7 +14,7 @@ def self.unsubscribed?(invitation)
end
class NullMessage
- def deliver
+ def deliver_now
end
end
end
diff --git a/example_app/app/models/completion.rb b/example_app/app/models/completion.rb
index 33f7e04..556ac37 100644
--- a/example_app/app/models/completion.rb
+++ b/example_app/app/models/completion.rb
@@ -27,6 +27,6 @@ def score
private
def completion_notification
- Mailer.completion_notification(user).deliver
+ Mailer.completion_notification(user).deliver_now
end
end
diff --git a/example_app/app/models/invitation.rb b/example_app/app/models/invitation.rb
index 222658b..2b0a471 100644
--- a/example_app/app/models/invitation.rb
+++ b/example_app/app/models/invitation.rb
@@ -1,5 +1,4 @@
class Invitation < ActiveRecord::Base
- EMAIL_REGEX = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
STATUSES = %w(pending accepted)
belongs_to :sender, class_name: 'User'
@@ -7,7 +6,7 @@ class Invitation < ActiveRecord::Base
before_create :set_token
- validates :recipient_email, presence: true, format: EMAIL_REGEX
+ validates :recipient_email, presence: true, email_address: true
validates :status, inclusion: { in: STATUSES }
def to_param
@@ -16,7 +15,7 @@ def to_param
def deliver(mailer)
body = InvitationMessage.new(self).body
- mailer.invitation_notification(self, body).deliver
+ mailer.invitation_notification(self, body).deliver_now
end
private
diff --git a/example_app/app/models/invitation_message.rb b/example_app/app/models/invitation_message.rb
index 00e80ff..7a680ec 100644
--- a/example_app/app/models/invitation_message.rb
+++ b/example_app/app/models/invitation_message.rb
@@ -1,5 +1,6 @@
class InvitationMessage < AbstractController::Base
include AbstractController::Rendering
+ include ActionView::Rendering
include Rails.application.routes.url_helpers
self.view_paths = 'app/views'
diff --git a/example_app/app/models/multiple_choice_submittable.rb b/example_app/app/models/multiple_choice_submittable.rb
index 4f56914..e25b7a9 100644
--- a/example_app/app/models/multiple_choice_submittable.rb
+++ b/example_app/app/models/multiple_choice_submittable.rb
@@ -6,7 +6,7 @@ class MultipleChoiceSubmittable < ActiveRecord::Base
def breakdown
total = answers.count
- counts = answers.group(:text).order('COUNT(*) DESC').count
+ counts = answers.group(:text).order(Arel.sql('COUNT(*) DESC')).count
percents = counts.map do |text, count|
percent = (100.0 * count / total).round
"#{percent}% #{text}"
diff --git a/example_app/app/models/question.rb b/example_app/app/models/question.rb
index fcaf765..9988fbf 100644
--- a/example_app/app/models/question.rb
+++ b/example_app/app/models/question.rb
@@ -34,7 +34,9 @@ def most_recent_answer_text
def build_submittable(type, attributes)
submittable_class = type.sub('Question', 'Submittable').constantize
- self.submittable = submittable_class.new(attributes.merge(question: self))
+ self.submittable = submittable_class.new(attributes).tap do |submittable|
+ submittable.question = self
+ end
end
def summary_using(summarizer)
diff --git a/example_app/app/models/survey_inviter.rb b/example_app/app/models/survey_inviter.rb
index b0c0ca6..777f659 100644
--- a/example_app/app/models/survey_inviter.rb
+++ b/example_app/app/models/survey_inviter.rb
@@ -9,8 +9,8 @@ class SurveyInviter
validates_with EnumerableValidator,
attributes: [:recipients],
- unless: 'recipients.nil?',
- validator: EmailValidator
+ if: :has_recipients,
+ validator: EmailAddressValidator
def recipients=(recipients)
@recipients = RecipientList.new(recipients)
@@ -24,6 +24,10 @@ def invite
private
+ def has_recipients
+ recipients.present?
+ end
+
def deliver_invitations
create_invitations.each do |invitation|
invitation.deliver(UnsubscribeableMailer)
diff --git a/example_app/app/validators/email_validator.rb b/example_app/app/validators/email_address_validator.rb
similarity index 64%
rename from example_app/app/validators/email_validator.rb
rename to example_app/app/validators/email_address_validator.rb
index 1d0161a..cc88631 100644
--- a/example_app/app/validators/email_validator.rb
+++ b/example_app/app/validators/email_address_validator.rb
@@ -1,8 +1,8 @@
-class EmailValidator < ActiveModel::EachValidator
+class EmailAddressValidator < ActiveModel::EachValidator
EMAIL_REGEX = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
def validate_each(record, attribute, value)
- unless value.match EMAIL_REGEX
+ if value.present? && !value.match(EMAIL_REGEX)
record.errors.add(attribute, "#{value} is not a valid email")
end
end
diff --git a/example_app/app/views/layouts/application.html.erb b/example_app/app/views/layouts/application.html.erb
index 9335474..009b4da 100644
--- a/example_app/app/views/layouts/application.html.erb
+++ b/example_app/app/views/layouts/application.html.erb
@@ -3,7 +3,7 @@
ExampleApp
<%= stylesheet_link_tag "application", :media => "all" %>
- <%= javascript_include_tag "application" %>
+ <%= javascript_importmap_tags %>
<%= csrf_meta_tags %>
diff --git a/example_app/bin/bundle b/example_app/bin/bundle
new file mode 100755
index 0000000..f19acf5
--- /dev/null
+++ b/example_app/bin/bundle
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+load Gem.bin_path('bundler', 'bundle')
diff --git a/example_app/bin/importmap b/example_app/bin/importmap
new file mode 100755
index 0000000..36502ab
--- /dev/null
+++ b/example_app/bin/importmap
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+
+require_relative "../config/application"
+require "importmap/commands"
diff --git a/example_app/bin/rails b/example_app/bin/rails
new file mode 100755
index 0000000..efc0377
--- /dev/null
+++ b/example_app/bin/rails
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+APP_PATH = File.expand_path("../config/application", __dir__)
+require_relative "../config/boot"
+require "rails/commands"
diff --git a/example_app/bin/rake b/example_app/bin/rake
new file mode 100755
index 0000000..4fbf10b
--- /dev/null
+++ b/example_app/bin/rake
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+require_relative "../config/boot"
+require "rake"
+Rake.application.run
diff --git a/example_app/bin/setup b/example_app/bin/setup
new file mode 100755
index 0000000..ec47b79
--- /dev/null
+++ b/example_app/bin/setup
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+require "fileutils"
+
+# path to your application root.
+APP_ROOT = File.expand_path("..", __dir__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+FileUtils.chdir APP_ROOT do
+ # This script is a way to set up or update your development environment automatically.
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
+ # Add necessary setup steps to this file.
+
+ puts "== Installing dependencies =="
+ system! "gem install bundler --conservative"
+ system("bundle check") || system!("bundle install")
+
+ # puts "\n== Copying sample files =="
+ # unless File.exist?("config/database.yml")
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
+ # end
+
+ puts "\n== Preparing database =="
+ system! "bin/rails db:prepare"
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! "bin/rails log:clear tmp:clear"
+
+ puts "\n== Restarting application server =="
+ system! "bin/rails restart"
+end
diff --git a/example_app/bin/update b/example_app/bin/update
new file mode 100755
index 0000000..58bfaed
--- /dev/null
+++ b/example_app/bin/update
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = File.expand_path('..', __dir__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a way to update your development environment automatically.
+ # Add necessary update steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') || system!('bundle install')
+
+ # Install JavaScript dependencies if using Yarn
+ # system('bin/yarn')
+
+ puts "\n== Updating database =="
+ system! 'bin/rails db:migrate'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rails log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rails restart'
+end
diff --git a/example_app/bin/yarn b/example_app/bin/yarn
new file mode 100755
index 0000000..9fab2c3
--- /dev/null
+++ b/example_app/bin/yarn
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path('..', __dir__)
+Dir.chdir(APP_ROOT) do
+ yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
+ select { |dir| File.expand_path(dir) != __dir__ }.
+ product(["yarn", "yarn.cmd", "yarn.ps1"]).
+ map { |dir, file| File.expand_path(file, dir) }.
+ find { |file| File.executable?(file) }
+
+ if yarn
+ exec yarn, *ARGV
+ else
+ $stderr.puts "Yarn executable was not detected in the system."
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ exit 1
+ end
+end
diff --git a/example_app/config.ru b/example_app/config.ru
index 8a63f7a..4a3c09a 100644
--- a/example_app/config.ru
+++ b/example_app/config.ru
@@ -1,4 +1,6 @@
# This file is used by Rack-based servers to start the application.
-require ::File.expand_path('../config/environment', __FILE__)
-run ExampleApp::Application
+require_relative "config/environment"
+
+run Rails.application
+Rails.application.load_server
diff --git a/example_app/config/application.rb b/example_app/config/application.rb
index fc14ed9..4eda091 100644
--- a/example_app/config/application.rb
+++ b/example_app/config/application.rb
@@ -1,65 +1,22 @@
-require File.expand_path('../boot', __FILE__)
+require_relative "boot"
-# Pick the frameworks you want:
-require "active_record/railtie"
-require "action_controller/railtie"
-require "action_mailer/railtie"
-require "active_resource/railtie"
-require "sprockets/railtie"
-# require "rails/test_unit/railtie"
+require "rails/all"
-if defined?(Bundler)
- # If you precompile assets before deploying to production, use this line
- Bundler.require(*Rails.groups(:assets => %w(development test)))
- # If you want your assets lazily compiled in production, use this line
- # Bundler.require(:default, :assets, Rails.env)
-end
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(*Rails.groups)
module ExampleApp
class Application < Rails::Application
- # Settings in config/environments/* take precedence over those specified here.
- # Application configuration should go into files in config/initializers
- # -- all .rb files in that directory are automatically loaded.
-
- # Custom directories with classes and modules you want to be autoloadable.
- config.autoload_paths += %W(#{config.root}/lib)
-
- # Only load the plugins named here, in the order given (default is alphabetical).
- # :all can be used as a placeholder for all plugins not explicitly named.
- # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
-
- # Activate observers that should always be running.
- # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
-
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
- # config.time_zone = 'Central Time (US & Canada)'
-
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
- # config.i18n.default_locale = :de
-
- # Configure the default encoding used in templates for Ruby 1.9.
- config.encoding = "utf-8"
-
- # Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters += [:password]
-
- # Enable escaping HTML in JSON.
- config.active_support.escape_html_entities_in_json = true
-
- # Use SQL instead of Active Record's schema dumper when creating the database.
- # This is necessary if your schema can't be completely dumped by the schema dumper,
- # like if you have constraints or database-specific column types
- # config.active_record.schema_format = :sql
-
- # Enforce whitelist mode for mass assignment.
- # This will create an empty whitelist of attributes available for mass-assignment for all models
- # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
- # parameters by using an attr_accessible or attr_protected declaration.
- config.active_record.whitelist_attributes = false
-
- # Enable the asset pipeline
- config.assets.enabled = true
+ # Initialize configuration defaults for originally generated Rails version.
+ config.load_defaults 7.0
+
+ # Configuration for the application, engines, and railties goes here.
+ #
+ # These settings can be overridden in specific environments using the files
+ # in config/environments, which are processed later.
+ #
+ # config.time_zone = "Central Time (US & Canada)"
+ # config.eager_load_paths << Rails.root.join("extras")
end
end
diff --git a/example_app/config/boot.rb b/example_app/config/boot.rb
index 4489e58..988a5dd 100644
--- a/example_app/config/boot.rb
+++ b/example_app/config/boot.rb
@@ -1,6 +1,4 @@
-require 'rubygems'
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
-# Set up gems listed in the Gemfile.
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-
-require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
+require "bundler/setup" # Set up gems listed in the Gemfile.
+require "bootsnap/setup" # Speed up boot time by caching expensive operations.
diff --git a/example_app/config/cable.yml b/example_app/config/cable.yml
new file mode 100644
index 0000000..d77ba74
--- /dev/null
+++ b/example_app/config/cable.yml
@@ -0,0 +1,10 @@
+development:
+ adapter: async
+
+test:
+ adapter: test
+
+production:
+ adapter: redis
+ url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
+ channel_prefix: example_app_production
diff --git a/example_app/config/environment.rb b/example_app/config/environment.rb
index b5a72d5..cac5315 100644
--- a/example_app/config/environment.rb
+++ b/example_app/config/environment.rb
@@ -1,5 +1,5 @@
-# Load the rails application
-require File.expand_path('../application', __FILE__)
+# Load the Rails application.
+require_relative "application"
-# Initialize the rails application
-ExampleApp::Application.initialize!
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/example_app/config/environments/development.rb b/example_app/config/environments/development.rb
index 91caff2..a263b83 100644
--- a/example_app/config/environments/development.rb
+++ b/example_app/config/environments/development.rb
@@ -1,33 +1,70 @@
-ExampleApp::Application.configure do
- # Settings specified here will take precedence over those in config/application.rb
+require "active_support/core_ext/integer/time"
- # In the development environment your application's code is reloaded on
- # every request. This slows down response time but is perfect for development
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded any time
+ # it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
- # Log error messages when you accidentally call methods on nil.
- config.whiny_nils = true
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable server timing
+ config.server_timing = true
+
+ # Enable/disable caching. By default caching is disabled.
+ # Run rails dev:cache to toggle caching.
+ if Rails.root.join("tmp/caching-dev.txt").exist?
+ config.action_controller.perform_caching = true
+ config.action_controller.enable_fragment_cache_logging = true
+
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ "Cache-Control" => "public, max-age=#{2.days.to_i}"
+ }
+ else
+ config.action_controller.perform_caching = false
+
+ config.cache_store = :null_store
+ end
- # Show full error reports and disable caching
- config.consider_all_requests_local = true
- config.action_controller.perform_caching = false
+ # Store uploaded files on the local file system (see config/storage.yml for options).
+ config.active_storage.service = :local
- # Don't care if the mailer can't send
+ # Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
- # Print deprecation notices to the Rails logger
+ config.action_mailer.perform_caching = false
+
+ # Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
- # Only use best-standards-support built into browsers
- config.action_dispatch.best_standards_support = :builtin
+ # Raise exceptions for disallowed deprecations.
+ config.active_support.disallowed_deprecation = :raise
+
+ # Tell Active Support which deprecation messages to disallow.
+ config.active_support.disallowed_deprecation_warnings = []
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Highlight code that triggered database queries in logs.
+ config.active_record.verbose_query_logs = true
+
+
+ # Raises error for missing translations.
+ # config.i18n.raise_on_missing_translations = true
- # Raise exception on mass assignment protection for Active Record models
- config.active_record.mass_assignment_sanitizer = :strict
+ # Annotate rendered view with file names.
+ # config.action_view.annotate_rendered_view_with_filenames = true
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL)
- config.active_record.auto_explain_threshold_in_seconds = 0.5
+ # Uncomment if you wish to allow Action Cable access from any origin.
+ # config.action_cable.disable_request_forgery_protection = true
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
end
diff --git a/example_app/config/environments/production.rb b/example_app/config/environments/production.rb
index 61c0266..92a0eab 100644
--- a/example_app/config/environments/production.rb
+++ b/example_app/config/environments/production.rb
@@ -1,54 +1,87 @@
-ExampleApp::Application.configure do
- # Settings specified here will take precedence over those in config/application.rb
+require "active_support/core_ext/integer/time"
- # Code is not reloaded between requests
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
config.cache_classes = true
- # Full error reports are disabled and caching is turned on
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
- # Disable Rails's static asset server (Apache or nginx will already do this)
- config.serve_static_assets = false
+ # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
+ # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
+ # config.require_master_key = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.asset_host = "http://assets.example.com"
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
+ # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
+ # Store uploaded files on the local file system (see config/storage.yml for options).
+ config.active_storage.service = :local
- # Specifies the header that your server uses for sending files
- # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
- # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
+ # Mount Action Cable outside main process or domain.
+ # config.action_cable.mount_path = nil
+ # config.action_cable.url = "wss://example.com/cable"
+ # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
- # See everything in the log (default is :info)
- # config.log_level = :debug
+ # Include generic and useful information about system operation, but avoid logging too much
+ # information to avoid inadvertent exposure of personally identifiable information (PII).
+ config.log_level = :info
- # Prepend all log lines with the following tags
- # config.log_tags = [ :subdomain, :uuid ]
+ # Prepend all log lines with the following tags.
+ config.log_tags = [ :request_id ]
- # Use a different logger for distributed setups
- # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
-
- # Use a different cache store in production
+ # Use a different cache store in production.
# config.cache_store = :mem_cache_store
- # Enable serving of images, stylesheets, and JavaScripts from an asset server
- # config.action_controller.asset_host = "http://assets.example.com"
+ # Use a real queuing backend for Active Job (and separate queues per environment).
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "example_app_production"
+ config.action_mailer.perform_caching = false
- # Disable delivery errors, bad email addresses will be ignored
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
- # Enable threaded mode
- # config.threadsafe!
-
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
- # the I18n.default_locale when a translation can not be found)
+ # the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
- # Send deprecation notices to registered listeners
- config.active_support.deprecation = :notify
+ # Don't log any deprecations.
+ config.active_support.report_deprecations = false
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Use a different logger for distributed setups.
+ # require "syslog/logger"
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
+
+ if ENV["RAILS_LOG_TO_STDOUT"].present?
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+ end
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL)
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
end
diff --git a/example_app/config/environments/test.rb b/example_app/config/environments/test.rb
index 6a493c2..ec4a481 100644
--- a/example_app/config/environments/test.rb
+++ b/example_app/config/environments/test.rb
@@ -1,39 +1,62 @@
-ExampleApp::Application.configure do
- # Settings specified here will take precedence over those in config/application.rb
+require "active_support/core_ext/integer/time"
- # The test environment is used exclusively to run your application's
- # test suite. You never need to work with it otherwise. Remember that
- # your test database is "scratch space" for the test suite and is wiped
- # and recreated between test runs. Don't rely on the data there!
+# The test environment is used exclusively to run your application's
+# test suite. You never need to work with it otherwise. Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs. Don't rely on the data there!
+
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Turn false under Spring and add config.action_view.cache_template_loading = true.
config.cache_classes = true
- # Configure static asset server for tests with Cache-Control for performance
- config.serve_static_assets = true
- config.static_cache_control = "public, max-age=3600"
+ # Eager loading loads your whole application. When running a single test locally,
+ # this probably isn't necessary. It's a good idea to do in a continuous integration
+ # system, or in some way before deploying your code.
+ config.eager_load = ENV["CI"].present?
- # Log error messages when you accidentally call methods on nil
- config.whiny_nils = true
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ "Cache-Control" => "public, max-age=#{1.hour.to_i}"
+ }
- # Show full error reports and disable caching
+ # Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
+ config.cache_store = :null_store
- # Raise exceptions instead of rendering exception templates
+ # Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
- # Disable request forgery protection in test environment
- config.action_controller.allow_forgery_protection = false
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+
+ # Store uploaded files on the local file system in a temporary directory.
+ config.active_storage.service = :test
+
+ config.action_mailer.perform_caching = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
- # Raise exception on mass assignment protection for Active Record models
- config.active_record.mass_assignment_sanitizer = :strict
-
- # Print deprecation notices to the stderr
+ # Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
+ # Raise exceptions for disallowed deprecations.
+ config.active_support.disallowed_deprecation = :raise
+
+ # Tell Active Support which deprecation messages to disallow.
+ config.active_support.disallowed_deprecation_warnings = []
+
+ # Raises error for missing translations.
+ # config.i18n.raise_on_missing_translations = true
+
+ # Annotate rendered view with file names.
+ # config.action_view.annotate_rendered_view_with_filenames = true
+
config.action_mailer.default_url_options = { :host => 'example.com' }
end
diff --git a/example_app/config/importmap.rb b/example_app/config/importmap.rb
new file mode 100644
index 0000000..9d84985
--- /dev/null
+++ b/example_app/config/importmap.rb
@@ -0,0 +1,3 @@
+# Pin npm packages by running ./bin/importmap
+
+pin "application", preload: true
diff --git a/example_app/config/initializers/application_controller_renderer.rb b/example_app/config/initializers/application_controller_renderer.rb
new file mode 100644
index 0000000..89d2efa
--- /dev/null
+++ b/example_app/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+# ActiveSupport::Reloader.to_prepare do
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
+# end
diff --git a/example_app/config/initializers/assets.rb b/example_app/config/initializers/assets.rb
new file mode 100644
index 0000000..d6156df
--- /dev/null
+++ b/example_app/config/initializers/assets.rb
@@ -0,0 +1,12 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+# Rails.application.config.assets.version = "1.0"
+
+# Add additional assets to the asset load path.
+# Rails.application.config.assets.paths << Emoji.images_path
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in the app/assets
+# folder are already added.
+# Rails.application.config.assets.precompile += %w( admin.js admin.css )
diff --git a/example_app/config/initializers/backtrace_silencers.rb b/example_app/config/initializers/backtrace_silencers.rb
index 59385cd..33699c3 100644
--- a/example_app/config/initializers/backtrace_silencers.rb
+++ b/example_app/config/initializers/backtrace_silencers.rb
@@ -1,7 +1,8 @@
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
-# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
-# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
-# Rails.backtrace_cleaner.remove_silencers!
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
+# by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
+Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
diff --git a/example_app/config/initializers/content_security_policy.rb b/example_app/config/initializers/content_security_policy.rb
new file mode 100644
index 0000000..54f47cf
--- /dev/null
+++ b/example_app/config/initializers/content_security_policy.rb
@@ -0,0 +1,25 @@
+# Be sure to restart your server when you modify this file.
+
+# Define an application-wide content security policy.
+# See the Securing Rails Applications Guide for more information:
+# https://guides.rubyonrails.org/security.html#content-security-policy-header
+
+# Rails.application.configure do
+# config.content_security_policy do |policy|
+# policy.default_src :self, :https
+# policy.font_src :self, :https, :data
+# policy.img_src :self, :https, :data
+# policy.object_src :none
+# policy.script_src :self, :https
+# policy.style_src :self, :https
+# # Specify URI for violation reports
+# # policy.report_uri "/csp-violation-report-endpoint"
+# end
+#
+# # Generate session nonces for permitted importmap and inline scripts
+# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
+# config.content_security_policy_nonce_directives = %w(script-src)
+#
+# # Report violations without enforcing the policy.
+# # config.content_security_policy_report_only = true
+# end
diff --git a/example_app/config/initializers/cookies_serializer.rb b/example_app/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000..5a6a32d
--- /dev/null
+++ b/example_app/config/initializers/cookies_serializer.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Specify a serializer for the signed and encrypted cookie jars.
+# Valid options are :json, :marshal, and :hybrid.
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/example_app/config/initializers/filter_parameter_logging.rb b/example_app/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000..adc6568
--- /dev/null
+++ b/example_app/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure parameters to be filtered from the log file. Use this to limit dissemination of
+# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported
+# notations and behaviors.
+Rails.application.config.filter_parameters += [
+ :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
+]
diff --git a/example_app/config/initializers/inflections.rb b/example_app/config/initializers/inflections.rb
index 5d8d9be..3860f65 100644
--- a/example_app/config/initializers/inflections.rb
+++ b/example_app/config/initializers/inflections.rb
@@ -1,15 +1,16 @@
# Be sure to restart your server when you modify this file.
-# Add new inflection rules using the following format
-# (all these examples are active by default):
-# ActiveSupport::Inflector.inflections do |inflect|
-# inflect.plural /^(ox)$/i, '\1en'
-# inflect.singular /^(ox)en/i, '\1'
-# inflect.irregular 'person', 'people'
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, "\\1en"
+# inflect.singular /^(ox)en/i, "\\1"
+# inflect.irregular "person", "people"
# inflect.uncountable %w( fish sheep )
# end
-#
+
# These inflection rules are supported but not enabled by default:
-# ActiveSupport::Inflector.inflections do |inflect|
-# inflect.acronym 'RESTful'
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym "RESTful"
# end
diff --git a/example_app/config/initializers/mime_types.rb b/example_app/config/initializers/mime_types.rb
index 72aca7e..dc18996 100644
--- a/example_app/config/initializers/mime_types.rb
+++ b/example_app/config/initializers/mime_types.rb
@@ -2,4 +2,3 @@
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
-# Mime::Type.register_alias "text/html", :iphone
diff --git a/example_app/config/initializers/new_framework_defaults.rb b/example_app/config/initializers/new_framework_defaults.rb
new file mode 100644
index 0000000..d74e1d8
--- /dev/null
+++ b/example_app/config/initializers/new_framework_defaults.rb
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+#
+# This file contains migration options to ease your Rails 5.0 upgrade.
+#
+# Once upgraded flip defaults one by one to migrate to the new default.
+#
+# Read the Guide for Upgrading Ruby on Rails for more info on each option.
+
+# Require `belongs_to` associations by default. Previous versions had false.
+Rails.application.config.active_record.belongs_to_required_by_default = false
diff --git a/example_app/config/initializers/permissions_policy.rb b/example_app/config/initializers/permissions_policy.rb
new file mode 100644
index 0000000..00f64d7
--- /dev/null
+++ b/example_app/config/initializers/permissions_policy.rb
@@ -0,0 +1,11 @@
+# Define an application-wide HTTP permissions policy. For further
+# information see https://developers.google.com/web/updates/2018/06/feature-policy
+#
+# Rails.application.config.permissions_policy do |f|
+# f.camera :none
+# f.gyroscope :none
+# f.microphone :none
+# f.usb :none
+# f.fullscreen :self
+# f.payment :self, "https://secure.example.com"
+# end
diff --git a/example_app/config/initializers/secret_token.rb b/example_app/config/initializers/secret_token.rb
index dfbe32c..10b8a41 100644
--- a/example_app/config/initializers/secret_token.rb
+++ b/example_app/config/initializers/secret_token.rb
@@ -1,7 +1,12 @@
# Be sure to restart your server when you modify this file.
-# Your secret key for verifying the integrity of signed cookies.
+# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
+
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
-ExampleApp::Application.config.secret_token = 'b2fad122a6269eba3bdd2f723ac26a900e599c1a24ec70d900a3546db7b507ff9b9bba5157df699188e342d26b180b114cc4a0e53f7aea4f294af3e9d5353401'
+# You can use `rake secret` to generate a secure secret key.
+
+# Make sure your secret_key_base is kept private
+# if you're sharing your code publicly.
+ExampleApp::Application.config.secret_key_base = 'b2fad122a6269eba3bdd2f723ac26a900e599c1a24ec70d900a3546db7b507ff9b9bba5157df699188e342d26b180b114cc4a0e53f7aea4f294af3e9d5353401'
diff --git a/example_app/config/initializers/session_store.rb b/example_app/config/initializers/session_store.rb
index 99f2e7e..77b846e 100644
--- a/example_app/config/initializers/session_store.rb
+++ b/example_app/config/initializers/session_store.rb
@@ -1,8 +1,3 @@
# Be sure to restart your server when you modify this file.
-ExampleApp::Application.config.session_store :cookie_store, key: '_example_app_session'
-
-# Use the database for sessions instead of the cookie-based default,
-# which shouldn't be used to store highly confidential information
-# (create the session table with "rails generate session_migration")
-# ExampleApp::Application.config.session_store :active_record_store
+Rails.application.config.session_store :cookie_store, key: '_example_app_session'
diff --git a/example_app/config/initializers/wrap_parameters.rb b/example_app/config/initializers/wrap_parameters.rb
index 999df20..bbfc396 100644
--- a/example_app/config/initializers/wrap_parameters.rb
+++ b/example_app/config/initializers/wrap_parameters.rb
@@ -1,5 +1,5 @@
# Be sure to restart your server when you modify this file.
-#
+
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
@@ -8,7 +8,7 @@
wrap_parameters format: [:json]
end
-# Disable root element in JSON by default.
-ActiveSupport.on_load(:active_record) do
- self.include_root_in_json = false
-end
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/example_app/config/puma.rb b/example_app/config/puma.rb
new file mode 100644
index 0000000..1e19380
--- /dev/null
+++ b/example_app/config/puma.rb
@@ -0,0 +1,56 @@
+# Puma can serve each request in a thread from an internal thread pool.
+# The `threads` method setting takes two numbers: a minimum and maximum.
+# Any libraries that use thread pools should be configured to match
+# the maximum value specified for Puma. Default is set to 5 threads for minimum
+# and maximum; this matches the default thread size of Active Record.
+#
+threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+threads threads_count, threads_count
+
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
+#
+port ENV.fetch("PORT") { 3000 }
+
+# Specifies the `environment` that Puma will run in.
+#
+environment ENV.fetch("RAILS_ENV") { "development" }
+
+# Specifies the number of `workers` to boot in clustered mode.
+# Workers are forked webserver processes. If using threads and workers together
+# the concurrency of the application would be max `threads` * `workers`.
+# Workers do not work on JRuby or Windows (both of which do not support
+# processes).
+#
+# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
+
+# Use the `preload_app!` method when specifying a `workers` number.
+# This directive tells Puma to first boot the application and load code
+# before forking the application. This takes advantage of Copy On Write
+# process behavior so workers use less memory. If you use this option
+# you need to make sure to reconnect any threads in the `on_worker_boot`
+# block.
+#
+# preload_app!
+
+# If you are preloading your application and using Active Record, it's
+# recommended that you close any connections to the database before workers
+# are forked to prevent connection leakage.
+#
+# before_fork do
+# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
+# end
+
+# The code in the `on_worker_boot` will be called if you are using
+# clustered mode by specifying a number of `workers`. After each worker
+# process is booted, this block will be run. If you are using the `preload_app!`
+# option, you will want to use this block to reconnect to any threads
+# or connections that may have been created at application boot, as Ruby
+# cannot share connections between processes.
+#
+# on_worker_boot do
+# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
+# end
+#
+
+# Allow puma to be restarted by `rails restart` command.
+plugin :tmp_restart
diff --git a/example_app/config/routes.rb b/example_app/config/routes.rb
index c0c692b..87c01a4 100644
--- a/example_app/config/routes.rb
+++ b/example_app/config/routes.rb
@@ -1,4 +1,5 @@
-ExampleApp::Application.routes.draw do
+Rails.application.routes.draw do
+ # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :completions, only: [:show]
resources :questions, only: [:edit, :update] do
diff --git a/example_app/config/secrets.yml b/example_app/config/secrets.yml
new file mode 100644
index 0000000..4505dd0
--- /dev/null
+++ b/example_app/config/secrets.yml
@@ -0,0 +1,32 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key is used for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rails secret` to generate a secure secret key.
+
+# Make sure the secrets in this file are kept private
+# if you're sharing your code publicly.
+
+# Shared secrets are available across all environments.
+
+# shared:
+# api_key: a1B2c3D4e5F6
+
+# Environmental secrets are only available for that specific environment.
+
+development:
+ secret_key_base: a584a03bedb5de6bc6af039858c16d5cb939bd567c77043ae941df4284cbc5c70fe1fe2dfc42030bac7cefd833cc390ca0626f773b7bf0bd783529ab13fd13ba
+
+test:
+ secret_key_base: f25cdb15163a6486f1492780d83c381ac7220de2f561f459eed9aed36f6ee07cb30947cd65fa4a687211d0665c9cce0b27ce34e781179aeb908407f2b76a59fa
+
+# Do not keep production secrets in the unencrypted secrets file.
+# Instead, either read values from the environment.
+# Or, use `bin/rails secrets:setup` to configure encrypted secrets
+# and move the `production:` environment over there.
+
+production:
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/example_app/config/storage.yml b/example_app/config/storage.yml
new file mode 100644
index 0000000..d32f76e
--- /dev/null
+++ b/example_app/config/storage.yml
@@ -0,0 +1,34 @@
+test:
+ service: Disk
+ root: <%= Rails.root.join("tmp/storage") %>
+
+local:
+ service: Disk
+ root: <%= Rails.root.join("storage") %>
+
+# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
+# amazon:
+# service: S3
+# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
+# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
+# region: us-east-1
+# bucket: your_own_bucket
+
+# Remember not to checkin your GCS keyfile to a repository
+# google:
+# service: GCS
+# project: your_project
+# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
+# bucket: your_own_bucket
+
+# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
+# microsoft:
+# service: AzureStorage
+# storage_account_name: your_account_name
+# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
+# container: your_container_name
+
+# mirror:
+# service: Mirror
+# primary: local
+# mirrors: [ amazon, google, microsoft ]
diff --git a/example_app/db/migrate/20231006184256_add_service_name_to_active_storage_blobs.active_storage.rb b/example_app/db/migrate/20231006184256_add_service_name_to_active_storage_blobs.active_storage.rb
new file mode 100644
index 0000000..a15c6ce
--- /dev/null
+++ b/example_app/db/migrate/20231006184256_add_service_name_to_active_storage_blobs.active_storage.rb
@@ -0,0 +1,22 @@
+# This migration comes from active_storage (originally 20190112182829)
+class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
+ def up
+ return unless table_exists?(:active_storage_blobs)
+
+ unless column_exists?(:active_storage_blobs, :service_name)
+ add_column :active_storage_blobs, :service_name, :string
+
+ if configured_service = ActiveStorage::Blob.service.name
+ ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
+ end
+
+ change_column :active_storage_blobs, :service_name, :string, null: false
+ end
+ end
+
+ def down
+ return unless table_exists?(:active_storage_blobs)
+
+ remove_column :active_storage_blobs, :service_name
+ end
+end
diff --git a/example_app/db/migrate/20231006184257_create_active_storage_variant_records.active_storage.rb b/example_app/db/migrate/20231006184257_create_active_storage_variant_records.active_storage.rb
new file mode 100644
index 0000000..94ac83a
--- /dev/null
+++ b/example_app/db/migrate/20231006184257_create_active_storage_variant_records.active_storage.rb
@@ -0,0 +1,27 @@
+# This migration comes from active_storage (originally 20191206030411)
+class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
+ def change
+ return unless table_exists?(:active_storage_blobs)
+
+ # Use Active Record's configured type for primary key
+ create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
+ t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
+ t.string :variation_digest, null: false
+
+ t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+ end
+
+ private
+ def primary_key_type
+ config = Rails.configuration.generators
+ config.options[config.orm][:primary_key_type] || :primary_key
+ end
+
+ def blobs_primary_key_type
+ pkey_name = connection.primary_key(:active_storage_blobs)
+ pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
+ pkey_column.bigint? ? :bigint : pkey_column.type
+ end
+end
diff --git a/example_app/db/schema.rb b/example_app/db/schema.rb
index bb5c464..8ba2083 100644
--- a/example_app/db/schema.rb
+++ b/example_app/db/schema.rb
@@ -1,123 +1,116 @@
-# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
-# Note that this schema.rb definition is the authoritative source for your
-# database schema. If you need to create the application database on another
-# system, you should be using db:schema:load, not running all the migrations
-# from scratch. The latter is a flawed and unsustainable approach (the more migrations
-# you'll amass, the slower it'll run and the greater likelihood for issues).
+# This file is the source Rails uses to define your schema when running `bin/rails
+# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
+# be faster and is potentially less error prone than running all of your
+# migrations from scratch. Old migrations may fail to apply correctly if those
+# migrations use external dependencies or application code.
#
-# It's strongly recommended to check this file into your version control system.
-
-ActiveRecord::Schema.define(:version => 20130425182110) do
-
- create_table "answers", :force => true do |t|
- t.integer "completion_id", :null => false
- t.integer "question_id", :null => false
- t.string "text", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema[7.0].define(version: 2023_10_06_184257) do
+ create_table "answers", force: :cascade do |t|
+ t.integer "completion_id", null: false
+ t.integer "question_id", null: false
+ t.string "text", limit: 255, null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.index ["completion_id"], name: "index_answers_on_completion_id"
+ t.index ["question_id"], name: "index_answers_on_question_id"
end
- add_index "answers", ["completion_id"], :name => "index_answers_on_completion_id"
- add_index "answers", ["question_id"], :name => "index_answers_on_question_id"
-
- create_table "completions", :force => true do |t|
- t.integer "survey_id", :null => false
- t.integer "user_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "completions", force: :cascade do |t|
+ t.integer "survey_id", null: false
+ t.integer "user_id", null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.index ["survey_id"], name: "index_completions_on_survey_id"
+ t.index ["user_id"], name: "index_completions_on_user_id"
end
- add_index "completions", ["survey_id"], :name => "index_completions_on_survey_id"
- add_index "completions", ["user_id"], :name => "index_completions_on_user_id"
-
- create_table "invitations", :force => true do |t|
- t.integer "sender_id"
- t.integer "survey_id"
- t.string "recipient_email"
- t.string "status", :default => "pending"
- t.string "token"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.text "message", :default => "", :null => false
+ create_table "invitations", force: :cascade do |t|
+ t.integer "sender_id"
+ t.integer "survey_id"
+ t.string "recipient_email", limit: 255
+ t.string "status", limit: 255, default: "pending"
+ t.string "token", limit: 255
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.text "message", default: "", null: false
+ t.index ["survey_id"], name: "index_invitations_on_survey_id"
+ t.index ["token"], name: "index_invitations_on_token", unique: true
end
- add_index "invitations", ["survey_id"], :name => "index_invitations_on_survey_id"
- add_index "invitations", ["token"], :name => "index_invitations_on_token", :unique => true
-
- create_table "messages", :force => true do |t|
- t.integer "sender_id", :null => false
- t.integer "recipient_id", :null => false
- t.text "body", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "messages", force: :cascade do |t|
+ t.integer "sender_id", null: false
+ t.integer "recipient_id", null: false
+ t.text "body", null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
end
- create_table "multiple_choice_submittables", :force => true do |t|
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "multiple_choice_submittables", force: :cascade do |t|
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
end
- create_table "open_submittables", :force => true do |t|
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "open_submittables", force: :cascade do |t|
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
end
- create_table "options", :force => true do |t|
- t.integer "question_id", :null => false
- t.string "text", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "score", :default => 0, :null => false
+ create_table "options", force: :cascade do |t|
+ t.integer "question_id", null: false
+ t.string "text", limit: 255, null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.integer "score", default: 0, null: false
end
- create_table "questions", :force => true do |t|
- t.string "title", :null => false
- t.integer "survey_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "submittable_id"
- t.string "submittable_type"
+ create_table "questions", force: :cascade do |t|
+ t.string "title", limit: 255, null: false
+ t.integer "survey_id", null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.integer "submittable_id"
+ t.string "submittable_type", limit: 255
end
- create_table "scale_submittables", :force => true do |t|
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "minimum"
- t.integer "maximum"
+ create_table "scale_submittables", force: :cascade do |t|
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.integer "minimum"
+ t.integer "maximum"
end
- create_table "surveys", :force => true do |t|
- t.string "title", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "author_id", :null => false
+ create_table "surveys", force: :cascade do |t|
+ t.string "title", limit: 255, null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.integer "author_id", null: false
end
- create_table "unsubscribes", :force => true do |t|
- t.string "email", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "unsubscribes", force: :cascade do |t|
+ t.string "email", limit: 255, null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.index ["email"], name: "index_unsubscribes_on_email"
end
- add_index "unsubscribes", ["email"], :name => "index_unsubscribes_on_email"
-
- create_table "users", :force => true do |t|
- t.string "email"
- t.string "encrypted_password", :limit => 128
- t.string "salt", :limit => 128
- t.string "confirmation_token", :limit => 128
- t.string "remember_token", :limit => 128
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.string "first_name"
- t.string "last_name"
+ create_table "users", force: :cascade do |t|
+ t.string "email", limit: 255
+ t.string "encrypted_password", limit: 128
+ t.string "salt", limit: 128
+ t.string "confirmation_token", limit: 128
+ t.string "remember_token", limit: 128
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.string "first_name", limit: 255
+ t.string "last_name", limit: 255
+ t.index ["email"], name: "index_users_on_email"
+ t.index ["remember_token"], name: "index_users_on_remember_token"
end
- add_index "users", ["email"], :name => "index_users_on_email"
- add_index "users", ["remember_token"], :name => "index_users_on_remember_token"
-
end
diff --git a/example_app/lib/active_model/model.rb b/example_app/lib/active_model/model.rb
deleted file mode 100644
index 62383a0..0000000
--- a/example_app/lib/active_model/model.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-module ActiveModel
-
- # == Active \Model Basic \Model
- #
- # Includes the required interface for an object to interact with
- # ActionPack, using different ActiveModel modules.
- # It includes model name introspections, conversions, translations and
- # validations. Besides that, it allows you to initialize the object with a
- # hash of attributes, pretty much like ActiveRecord does.
- #
- # A minimal implementation could be:
- #
- # class Person
- # include ActiveModel::Model
- # attr_accessor :name, :age
- # end
- #
- # person = Person.new(name: 'bob', age: '18')
- # person.name # => 'bob'
- # person.age # => 18
- #
- # Note that, by default, ActiveModel::Model implements persisted?
- # to return +false+, which is the most common case. You may want to override
- # it in your class to simulate a different scenario:
- #
- # class Person
- # include ActiveModel::Model
- # attr_accessor :id, :name
- #
- # def persisted?
- # self.id == 1
- # end
- # end
- #
- # person = Person.new(id: 1, name: 'bob')
- # person.persisted? # => true
- #
- # Also, if for some reason you need to run code on initialize, make
- # sure you call +super+ if you want the attributes hash initialization to
- # happen.
- #
- # class Person
- # include ActiveModel::Model
- # attr_accessor :id, :name, :omg
- #
- # def initialize(attributes={})
- # super
- # @omg ||= true
- # end
- # end
- #
- # person = Person.new(id: 1, name: 'bob')
- # person.omg # => true
- #
- # For more detailed information on other functionalities available, please
- # refer to the specific modules included in ActiveModel::Model
- # (see below).
- module Model
- def self.included(base) #:nodoc:
- base.class_eval do
- extend ActiveModel::Naming
- extend ActiveModel::Translation
- include ActiveModel::Validations
- include ActiveModel::Conversion
- end
- end
-
- # Initializes a new model with the given +params+.
- #
- # class Person
- # include ActiveModel::Model
- # attr_accessor :name, :age
- # end
- #
- # person = Person.new(name: 'bob', age: '18')
- # person.name # => "bob"
- # person.age # => 18
- def initialize(params={})
- params.each do |attr, value|
- self.public_send("#{attr}=", value)
- end if params
- end
-
- # Indicates if the model is persisted. Default is +false+.
- #
- # class Person
- # include ActiveModel::Model
- # attr_accessor :id, :name
- # end
- #
- # person = Person.new(id: 1, name: 'bob')
- # person.persisted? # => false
- def persisted?
- false
- end
- end
-end
diff --git a/example_app/spec/factories/application.rb b/example_app/spec/factories/application.rb
index 78e5a61..aca387a 100644
--- a/example_app/spec/factories/application.rb
+++ b/example_app/spec/factories/application.rb
@@ -1,8 +1,8 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :answer do
completion
question
- text 'Hello'
+ text { 'Hello' }
end
factory :completion do
@@ -11,13 +11,13 @@
end
factory :multiple_choice_submittable do
- ignore do
+ transient do
options_texts { [] }
end
options do |attributes|
attributes.options_texts.map do |text|
- FactoryGirl.build(:option, text: text, question_id: attributes.id)
+ FactoryBot.build(:option, text: text, question_id: attributes.id)
end
end
end
@@ -26,7 +26,7 @@
end
factory :option do
- text 'Hello'
+ text { 'Hello' }
end
factory :question do
@@ -48,8 +48,8 @@
end
factory :scale_submittable do
- minimum 1
- maximum 2
+ minimum { 1 }
+ maximum { 2 }
end
factory :survey do
diff --git a/example_app/spec/factories/invitation.rb b/example_app/spec/factories/invitation.rb
index 8857719..d2ec312 100644
--- a/example_app/spec/factories/invitation.rb
+++ b/example_app/spec/factories/invitation.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :invitation do
sender
recipient_email { generate(:email) }
diff --git a/example_app/spec/factories/user.rb b/example_app/spec/factories/user.rb
index 65a896b..5209574 100644
--- a/example_app/spec/factories/user.rb
+++ b/example_app/spec/factories/user.rb
@@ -1,12 +1,12 @@
-FactoryGirl.define do
+FactoryBot.define do
sequence :email do |n|
"user#{n}@example.com"
end
factory :user, aliases: [:sender] do
- first_name 'Ron'
- last_name 'Burgundy'
+ first_name { 'Ron' }
+ last_name { 'Burgundy' }
email
- password 'password'
+ password { 'password' }
end
end
diff --git a/example_app/spec/features/user_edits_open_question_spec.rb b/example_app/spec/features/user_edits_open_question_spec.rb
index 6df5cf6..e3f7246 100644
--- a/example_app/spec/features/user_edits_open_question_spec.rb
+++ b/example_app/spec/features/user_edits_open_question_spec.rb
@@ -6,7 +6,7 @@
sign_in
update_question(question, 'Updated title')
- expect(page).to have_content('Updated title')
+ page.should have_content('Updated title')
end
def update_question(question, title)
diff --git a/example_app/spec/features/user_views_survey_results_spec.rb b/example_app/spec/features/user_views_survey_results_spec.rb
index 8dea8a0..6157a6f 100644
--- a/example_app/spec/features/user_views_survey_results_spec.rb
+++ b/example_app/spec/features/user_views_survey_results_spec.rb
@@ -14,7 +14,10 @@
taker.complete 'Billy', 'Blue', '5'
summary_for_question('Name?').should eq('Brian, Billy')
- summary_for_question('Favorite color?').should eq('50% Blue, 50% Red')
+ summary_for_question('Favorite color?').split(', ').should contain_exactly(
+ '50% Blue',
+ '50% Red'
+ )
summary_for_question('Airspeed velocity?').should eq('Average: 7.50')
view_completions survey
diff --git a/example_app/spec/features/user_views_survey_score_spec.rb b/example_app/spec/features/user_views_survey_score_spec.rb
index 77e712e..0a4bb99 100644
--- a/example_app/spec/features/user_views_survey_score_spec.rb
+++ b/example_app/spec/features/user_views_survey_score_spec.rb
@@ -22,7 +22,7 @@ def make_scored_survey
def answer_survey(survey)
sign_in
taker = SurveyTaker.new(survey)
- taker.complete 'Y', 6
+ taker.complete 'Y', '6'
end
def have_survey_score
diff --git a/example_app/spec/mailers/unsubscribeable_mailer_spec.rb b/example_app/spec/mailers/unsubscribeable_mailer_spec.rb
index ca11c13..371b10b 100644
--- a/example_app/spec/mailers/unsubscribeable_mailer_spec.rb
+++ b/example_app/spec/mailers/unsubscribeable_mailer_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe UnsubscribeableMailer, '#deliver' do
+describe UnsubscribeableMailer, '#deliver_now' do
it 'sends email notifications to new users' do
invitation = create(:invitation)
@@ -19,6 +19,6 @@
end
def deliver_invitation(invitation, body)
- UnsubscribeableMailer.invitation_notification(invitation, body).deliver
+ UnsubscribeableMailer.invitation_notification(invitation, body).deliver_now
end
end
diff --git a/example_app/spec/models/completion_spec.rb b/example_app/spec/models/completion_spec.rb
index 9f86648..1f92ada 100644
--- a/example_app/spec/models/completion_spec.rb
+++ b/example_app/spec/models/completion_spec.rb
@@ -46,7 +46,7 @@
describe Completion, '#save' do
it 'delivers a completion notification' do
- Mailer.stubs(completion_notification: stub(deliver: true))
+ Mailer.stubs(completion_notification: stub(deliver_now: true))
user = create(:user)
completion = create(:completion, user: user)
diff --git a/example_app/spec/models/invitation_spec.rb b/example_app/spec/models/invitation_spec.rb
index 5239fe3..32be588 100644
--- a/example_app/spec/models/invitation_spec.rb
+++ b/example_app/spec/models/invitation_spec.rb
@@ -9,7 +9,7 @@
it { should validate_presence_of(:recipient_email) }
it { should allow_value('user@example.com').for(:recipient_email) }
it { should_not allow_value('invalid_email').for(:recipient_email) }
- it { should ensure_inclusion_of(:status).in_array(Invitation::STATUSES) }
+ it { should validate_inclusion_of(:status).in_array(Invitation::STATUSES) }
end
describe Invitation, '#deliver' do
diff --git a/example_app/spec/models/multiple_choice_submittable_spec.rb b/example_app/spec/models/multiple_choice_submittable_spec.rb
index 12ebb90..e27c9a5 100644
--- a/example_app/spec/models/multiple_choice_submittable_spec.rb
+++ b/example_app/spec/models/multiple_choice_submittable_spec.rb
@@ -40,13 +40,15 @@
describe MultipleChoiceSubmittable, '#score' do
it 'returns the score for the option with the given text' do
- question = build_stubbed(:multiple_choice_question)
- submittable = MultipleChoiceSubmittable.new(question: question)
- submittable.options.target.stubs(score: 2)
+ question = create(:multiple_choice_question)
+ submittable = create(
+ :multiple_choice_submittable,
+ question: question
+ )
+ create(:option, text: 'two', score: 2, question_id: submittable.id)
- result = submittable.score('two')
+ result = question.reload.submittable.score('two')
- submittable.options.target.should have_received(:score).with('two')
result.should eq 2
end
end
diff --git a/example_app/spec/models/question_spec.rb b/example_app/spec/models/question_spec.rb
index d6d3775..445b34b 100644
--- a/example_app/spec/models/question_spec.rb
+++ b/example_app/spec/models/question_spec.rb
@@ -7,7 +7,7 @@
it { should allow_value(type).for(:submittable_type) }
end
- it { should_not allow_value('Other').for(:submittable_type) }
+ it { should_not allow_value('Question').for(:submittable_type) }
it { should validate_presence_of :title }
diff --git a/example_app/spec/models/survey_inviter_spec.rb b/example_app/spec/models/survey_inviter_spec.rb
index 866d001..27ef3c6 100644
--- a/example_app/spec/models/survey_inviter_spec.rb
+++ b/example_app/spec/models/survey_inviter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe SurveyInviter, 'Validations' do
- it { should ensure_length_of(:recipients).is_at_least(1) }
+ it { should validate_length_of(:recipients).is_at_least(1) }
it { should validate_presence_of(:message) }
it { should validate_presence_of(:sender) }
it { should validate_presence_of(:survey) }
@@ -17,7 +17,7 @@
end
it 'returns false for an invalid recipient' do
- SurveyInviter.new(invalid_params).invite.should be_false
+ SurveyInviter.new(invalid_params).invite.should be_falsey
end
it "doesn't send emails if any invitations fail to save" do
@@ -27,7 +27,7 @@
params = valid_params(recipients: 'one@example.com,two@example.com')
inviter = SurveyInviter.new(params)
- expect { inviter.invite }.to raise_error(StandardError, 'failure')
+ lambda { inviter.invite }.should raise_error(StandardError, 'failure')
invitation.should have_received(:deliver).never
end
diff --git a/example_app/spec/models/user_spec.rb b/example_app/spec/models/user_spec.rb
index ba5e3d3..ac6afa8 100644
--- a/example_app/spec/models/user_spec.rb
+++ b/example_app/spec/models/user_spec.rb
@@ -30,6 +30,6 @@
it 'returns the users full name' do
user = User.new(first_name: 'First', last_name: 'Last')
- expect(user.full_name).to eq 'First Last'
+ user.full_name.should eq 'First Last'
end
end
diff --git a/example_app/spec/spec_helper.rb b/example_app/spec/spec_helper.rb
index 7b28caf..7be0cf9 100644
--- a/example_app/spec/spec_helper.rb
+++ b/example_app/spec/spec_helper.rb
@@ -2,7 +2,6 @@
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
-require 'rspec/autorun'
require 'capybara/rspec'
# Requires supporting ruby files with custom matchers and macros, etc,
@@ -12,9 +11,19 @@
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
+ config.infer_spec_type_from_file_location!
config.order = 'random'
config.mock_framework = :mocha
- config.include FactoryGirl::Syntax::Methods
+ config.raise_errors_for_deprecations!
+ config.include FactoryBot::Syntax::Methods
config.include Features, type: :feature
+ config.expect_with(:rspec) { |c| c.syntax = :should }
config.before(:each) { ActionMailer::Base.deliveries.clear }
end
+
+Shoulda::Matchers.configure do |config|
+ config.integrate do |with|
+ with.test_framework :rspec
+ with.library :rails
+ end
+end
diff --git a/example_app/spec/support/answer_creator.rb b/example_app/spec/support/answer_creator.rb
index 016541b..dd69b99 100644
--- a/example_app/spec/support/answer_creator.rb
+++ b/example_app/spec/support/answer_creator.rb
@@ -1,5 +1,5 @@
class AnswerCreator
- include FactoryGirl::Syntax::Methods
+ include FactoryBot::Syntax::Methods
def initialize(survey, options = {})
@survey = survey
diff --git a/example_app/spec/support/survey_maker.rb b/example_app/spec/support/survey_maker.rb
index 9f2093b..a37d5e1 100644
--- a/example_app/spec/support/survey_maker.rb
+++ b/example_app/spec/support/survey_maker.rb
@@ -1,5 +1,5 @@
class SurveyMaker
- include FactoryGirl::Syntax::Methods
+ include FactoryBot::Syntax::Methods
def open_question(title)
create :open_question, title: title, survey: survey
diff --git a/example_app/spec/validators/email_validator_spec.rb b/example_app/spec/validators/email_address_validator_spec.rb
similarity index 77%
rename from example_app/spec/validators/email_validator_spec.rb
rename to example_app/spec/validators/email_address_validator_spec.rb
index 7c9a3df..2bf69ac 100644
--- a/example_app/spec/validators/email_validator_spec.rb
+++ b/example_app/spec/validators/email_address_validator_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-describe EmailValidator, '#validate_each' do
+describe EmailAddressValidator, '#validate_each' do
it 'adds errors when given invalid email' do
record = build_record('invalid_email')
- EmailValidator.new(attributes: :email).validate(record)
+ EmailAddressValidator.new(attributes: :email).validate(record)
record.errors.full_messages.should eq [
'Email invalid_email is not a valid email'
@@ -12,7 +12,7 @@
it 'does not add errors when given valid email' do
record = build_record('valid@example.com')
- EmailValidator.new(attributes: :email).validate(record)
+ EmailAddressValidator.new(attributes: :email).validate(record)
record.errors.should be_empty
end
diff --git a/example_app/spec/validators/enumerable_validator_spec.rb b/example_app/spec/validators/enumerable_validator_spec.rb
index 9800225..0287d90 100644
--- a/example_app/spec/validators/enumerable_validator_spec.rb
+++ b/example_app/spec/validators/enumerable_validator_spec.rb
@@ -6,7 +6,7 @@
EnumerableValidator.new(
attributes: :emails,
- validator: EmailValidator
+ validator: EmailAddressValidator
).validate(record)
record.errors.full_messages.should eq [
diff --git a/example_app/vendor/javascript/.keep b/example_app/vendor/javascript/.keep
new file mode 100644
index 0000000..e69de29