diff --git a/Dockerfile b/Dockerfile index 015d06804..da1b8a43a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.6.7-buster +FROM ruby:3.1-buster # DEBIAN_FRONTEND=noninteractive is required to install tzdata in non interactive way ENV DEBIAN_FRONTEND noninteractive diff --git a/Gemfile b/Gemfile index d0791686b..6ab930c76 100644 --- a/Gemfile +++ b/Gemfile @@ -2,10 +2,10 @@ source 'https://rubygems.org' # Ruby versions for various enviornments ruby_versions = { - development: '~>2.6.7', - test: '~>2.6.7', - staging: '~>2.6.7', - production: '~>2.6.7' + development: '~>3.1.0', + test: '~>3.1.0', + staging: '~>3.1.0', + production: '~>3.1.0' } # Get the ruby version for the current enviornment ruby ruby_versions[(ENV['RAILS_ENV'] || 'development').to_sym] @@ -89,3 +89,5 @@ gem 'roo-xls' gem 'icalendar', '~> 2.5', '>= 2.5.3' gem 'rest-client', '~> 2.0' + +gem 'net-smtp', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 9e59a5607..001c86638 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,40 +1,40 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + actioncable (6.1.4.4) + actionpack (= 6.1.4.4) + activesupport (= 6.1.4.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionmailbox (6.1.4.4) + actionpack (= 6.1.4.4) + activejob (= 6.1.4.4) + activerecord (= 6.1.4.4) + activestorage (= 6.1.4.4) + activesupport (= 6.1.4.4) mail (>= 2.7.1) - actionmailer (6.1.4.1) - actionpack (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionmailer (6.1.4.4) + actionpack (= 6.1.4.4) + actionview (= 6.1.4.4) + activejob (= 6.1.4.4) + activesupport (= 6.1.4.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.1.4.1) - actionview (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionpack (6.1.4.4) + actionview (= 6.1.4.4) + activesupport (= 6.1.4.4) rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.4.1) - actionpack (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + actiontext (6.1.4.4) + actionpack (= 6.1.4.4) + activerecord (= 6.1.4.4) + activestorage (= 6.1.4.4) + activesupport (= 6.1.4.4) nokogiri (>= 1.8.5) - actionview (6.1.4.1) - activesupport (= 6.1.4.1) + actionview (6.1.4.4) + activesupport (= 6.1.4.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -44,22 +44,22 @@ GEM activemodel (>= 4.1, < 6.2) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (6.1.4.1) - activesupport (= 6.1.4.1) + activejob (6.1.4.4) + activesupport (= 6.1.4.4) globalid (>= 0.3.6) - activemodel (6.1.4.1) - activesupport (= 6.1.4.1) - activerecord (6.1.4.1) - activemodel (= 6.1.4.1) - activesupport (= 6.1.4.1) - activestorage (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activesupport (= 6.1.4.1) + activemodel (6.1.4.4) + activesupport (= 6.1.4.4) + activerecord (6.1.4.4) + activemodel (= 6.1.4.4) + activesupport (= 6.1.4.4) + activestorage (6.1.4.4) + actionpack (= 6.1.4.4) + activejob (= 6.1.4.4) + activerecord (= 6.1.4.4) + activesupport (= 6.1.4.4) marcel (~> 1.0.0) mini_mime (>= 1.1.0) - activesupport (6.1.4.1) + activesupport (6.1.4.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -77,7 +77,7 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) bindata (2.4.10) - bootsnap (1.9.1) + bootsnap (1.9.3) msgpack (~> 1.0) builder (3.2.4) bunny (2.19.0) @@ -112,6 +112,7 @@ GEM devise_ldap_authenticatable (0.8.7) devise (>= 3.4.1) net-ldap (>= 0.16.0) + digest (3.1.0) docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -148,9 +149,9 @@ GEM faker (2.19.0) i18n (>= 1.6, < 2) ffi (1.15.4) - globalid (0.5.2) + globalid (1.0.0) activesupport (>= 5.0) - grape (1.5.3) + grape (1.6.2) activesupport builder dry-types (>= 1.1) @@ -160,10 +161,10 @@ GEM grape-active_model_serializers (1.3.2) active_model_serializers (>= 0.9.0) grape - grape-entity (0.10.0) + grape-entity (0.10.1) activesupport (>= 3.0.0) multi_json (>= 1.3.2) - grape-swagger (1.4.1) + grape-swagger (1.4.2) grape (~> 1.3) grape-swagger-rails (0.3.1) railties (>= 3.2.12) @@ -172,12 +173,13 @@ GEM http-accept (1.7.0) http-cookie (1.0.4) domain_name (~> 0.5) - i18n (1.8.10) + i18n (1.8.11) concurrent-ruby (~> 1.0) icalendar (2.7.1) ice_cube (~> 0.16) - ice_cube (0.16.3) - json (2.5.1) + ice_cube (0.16.4) + io-wait (0.2.1) + json (2.6.1) json-jwt (1.7.0) activesupport bindata @@ -188,18 +190,18 @@ GEM listen (3.7.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.12.0) + loofah (2.13.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) marcel (1.0.2) method_source (1.0.0) - mime-types (3.3.1) + mime-types (3.4.1) mime-types-data (~> 3.2015) - mime-types-data (3.2021.0901) - mini_mime (1.1.1) - minitest (5.14.4) + mime-types-data (3.2022.0105) + mini_mime (1.1.2) + minitest (5.15.0) minitest-around (0.5.0) minitest (~> 5.0) minitest-rails (6.1.0) @@ -214,18 +216,25 @@ GEM mustermann (>= 1.0.0) mysql2 (0.5.3) net-ldap (0.17.0) + net-protocol (0.1.2) + io-wait + timeout + net-smtp (0.3.1) + digest + net-protocol + timeout netrc (0.11.0) nio4r (2.5.8) - nokogiri (1.12.4) + nokogiri (1.13.0-x86_64-linux) racc (~> 1.4) orm_adapter (0.5.0) parallel (1.21.0) - parser (3.0.2.0) + parser (3.1.0.0) ast (~> 2.4.1) public_suffix (4.0.6) - puma (5.5.1) + puma (5.5.2) nio4r (~> 2.0) - racc (1.5.2) + racc (1.6.0) rack (2.2.3) rack-accept (0.4.5) rack (>= 0.4) @@ -233,29 +242,29 @@ GEM rack (>= 2.0.0) rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.1.4.1) - actioncable (= 6.1.4.1) - actionmailbox (= 6.1.4.1) - actionmailer (= 6.1.4.1) - actionpack (= 6.1.4.1) - actiontext (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activemodel (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + rails (6.1.4.4) + actioncable (= 6.1.4.4) + actionmailbox (= 6.1.4.4) + actionmailer (= 6.1.4.4) + actionpack (= 6.1.4.4) + actiontext (= 6.1.4.4) + actionview (= 6.1.4.4) + activejob (= 6.1.4.4) + activemodel (= 6.1.4.4) + activerecord (= 6.1.4.4) + activestorage (= 6.1.4.4) + activesupport (= 6.1.4.4) bundler (>= 1.15.0) - railties (= 6.1.4.1) + railties (= 6.1.4.4) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.4.2) loofah (~> 2.3) - rails-latex (2.3.3) - rails (>= 3.0.0, < 7) - rails_best_practices (1.21.0) + rails-latex (2.3.4) + rails (>= 3.0.0, < 8) + rails_best_practices (1.22.1) activesupport code_analyzer (>= 0.5.2) erubis @@ -263,9 +272,9 @@ GEM json require_all (~> 3.0) ruby-progressbar - railties (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + railties (6.1.4.4) + actionpack (= 6.1.4.4) + activesupport (= 6.1.4.4) method_source rake (>= 0.13) thor (~> 1.0) @@ -275,7 +284,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) rbtree (0.4.4) - regexp_parser (2.1.1) + regexp_parser (2.2.0) require_all (3.0.0) responders (3.0.1) actionpack (>= 5.0) @@ -286,7 +295,7 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) rexml (3.2.5) - rmagick (4.2.2) + rmagick (4.2.4) roo (2.7.1) nokogiri (~> 1) rubyzip (~> 1.1, < 2.0.0) @@ -294,21 +303,21 @@ GEM nokogiri roo (>= 2.0.0, < 3) spreadsheet (> 0.9.0) - rubocop (1.21.0) + rubocop (1.24.1) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.9.1, < 2.0) + rubocop-ast (>= 1.15.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.11.0) + rubocop-ast (1.15.1) parser (>= 3.0.1.1) rubocop-faker (1.1.0) faker (>= 2.12.0) rubocop (>= 0.82.0) - rubocop-rails (2.12.2) + rubocop-rails (2.13.0) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.7.0, < 2.0) @@ -322,7 +331,7 @@ GEM rubyzip (1.3.0) securecompare (1.0.0) set (1.0.2) - sexp_processor (4.15.3) + sexp_processor (4.16.0) simplecov (0.21.2) docile (~> 1.1) simplecov-html (~> 0.11) @@ -337,11 +346,12 @@ GEM sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) sprockets (>= 3.0.0) - thor (1.1.0) + thor (1.2.1) + timeout (0.2.0) tzinfo (2.0.4) concurrent-ruby (~> 1.0) unf (0.1.4) @@ -358,10 +368,10 @@ GEM websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.4.2) + zeitwerk (2.5.3) PLATFORMS - ruby + x86_64-linux DEPENDENCIES active_model_serializers (~> 0.10.0) @@ -393,6 +403,7 @@ DEPENDENCIES minitest-rails moss_ruby (>= 1.1.2) mysql2 (~> 0.5.0) + net-smtp puma (~> 5.5) rack-cors rails (~> 6.1.0) @@ -413,7 +424,7 @@ DEPENDENCIES webmock RUBY VERSION - ruby 2.6.7p197 + ruby 3.1.0p0 BUNDLED WITH - 2.2.31 + 2.3.3 diff --git a/app/helpers/file_helper.rb b/app/helpers/file_helper.rb index c4d904ef4..8267a611a 100644 --- a/app/helpers/file_helper.rb +++ b/app/helpers/file_helper.rb @@ -425,7 +425,7 @@ def write_entries_to_zip(entries, disk_root_path, zip_root_path, path, zip) # puts "subdir: #{subdir}" write_entries_to_zip(subdir, disk_root_path, zip_root_path, file_path, zip) else - # puts "Adding file: #{disk_file_path} -- #{File.exists? disk_file_path}" + # puts "Adding file: #{disk_file_path} -- #{File.exist? disk_file_path}" zip.get_output_stream(zip_file_path) do |f| f.puts(File.open(disk_file_path, 'rb').read) end diff --git a/app/models/overseer_assessment.rb b/app/models/overseer_assessment.rb index eb67beb30..3219ee033 100644 --- a/app/models/overseer_assessment.rb +++ b/app/models/overseer_assessment.rb @@ -51,7 +51,7 @@ def self.create_for(task) end def has_submission_files? - File.exists? submission_zip_file_name + File.exist? submission_zip_file_name end def submission_zip_file_name @@ -155,7 +155,7 @@ def send_to_overseer() return nil end - unless File.exists? submission_zip_file_name + unless File.exist? submission_zip_file_name puts "ERROR: Student submission history zip file doesn't exist #{submission_zip_file_name}. Unable to send - OverseerAssessment #{id}" return nil end @@ -166,7 +166,7 @@ def send_to_overseer() return nil end - unless File.exists? assessment_resources_path + unless File.exist? assessment_resources_path puts "ERROR: Unable to fine assessment resources - OverseerAssessment #{id}" return nil end diff --git a/app/models/task.rb b/app/models/task.rb index 38cf9d805..42a466feb 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -799,7 +799,7 @@ def has_new_files? end def has_done_file? - File.exists? zip_file_path_for_done_task + File.exist? zip_file_path_for_done_task end # @@ -1017,7 +1017,7 @@ def portfolio_evidence_path def portfolio_evidence_path=(value) # Strip the student work directory to store in database as relative path self.portfolio_evidence = value.present? ? value.sub(FileHelper.student_work_dir,'') : nil - end + end def final_pdf_path if group_task? @@ -1248,7 +1248,7 @@ def accept_submission(current_user, files, _student, ui, contributions, trigger, enqueued_dir = File.join(FileHelper.student_work_dir(:new, nil, true), id.to_s) # Move files into place, deleting existing files if present. - if not File.exists? enqueued_dir + if not File.exist? enqueued_dir logger.debug "Creating student work new dir #{enqueued_dir}" FileUtils.mkdir_p enqueued_dir end @@ -1259,7 +1259,7 @@ def accept_submission(current_user, files, _student, ui, contributions, trigger, # Delete the tmp dir logger.debug "Deleting student work dir: #{tmp_dir}" - FileUtils.rm_rf tmp_dir if File.exists? tmp_dir + FileUtils.rm_rf tmp_dir if File.exist? tmp_dir logger.info "Submission accepted! Status for task #{id} is now #{trigger}" end @@ -1270,10 +1270,10 @@ def delete_associated_files group_submission.destroy else zip_file = zip_file_path_for_done_task() - if zip_file && File.exists?(zip_file) + if zip_file && File.exist?(zip_file) FileUtils.rm zip_file end - if portfolio_evidence_path.present? && File.exists?(portfolio_evidence_path) + if portfolio_evidence_path.present? && File.exist?(portfolio_evidence_path) FileUtils.rm portfolio_evidence_path end diff --git a/app/models/task_definition.rb b/app/models/task_definition.rb index 08a1c1218..cb5e5584e 100644 --- a/app/models/task_definition.rb +++ b/app/models/task_definition.rb @@ -116,15 +116,15 @@ def remove_old_group_submissions def move_files_on_abbreviation_change old_abbr = saved_change_to_abbreviation[0] # 0 is original abbreviation - if File.exists? task_sheet_with_abbreviation(old_abbr) + if File.exist? task_sheet_with_abbreviation(old_abbr) FileUtils.mv(task_sheet_with_abbreviation(old_abbr), task_sheet()) end - if File.exists? task_resources_with_abbreviation(old_abbr) + if File.exist? task_resources_with_abbreviation(old_abbr) FileUtils.mv(task_resources_with_abbreviation(old_abbr), task_resources()) end - if File.exists? task_assessment_resources_with_abbreviation(old_abbr) + if File.exist? task_assessment_resources_with_abbreviation(old_abbr) FileUtils.mv(task_assessment_resources_with_abbreviation(old_abbr), task_assessment_resources()) end end @@ -340,8 +340,8 @@ def clear_related_plagiarism end end - def self.to_csv(task_definitions, options = {}) - CSV.generate(options) do |csv| + def self.to_csv(task_definitions) + CSV.generate() do |csv| csv << csv_columns task_definitions.each do |task_definition| csv << task_definition.to_csv_row diff --git a/app/models/unit.rb b/app/models/unit.rb index 4e742d435..d0de91794 100644 --- a/app/models/unit.rb +++ b/app/models/unit.rb @@ -717,7 +717,7 @@ def update_student_enrolments(changes, import_settings, result) last_name = last_name || first_name nickname = nickname || first_name - if !email =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i + if email !~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i errors << { row: row, message: "Invalid email address (#{email})" } next end @@ -1338,12 +1338,12 @@ def tutorial_stream_abbr tutorial_streams.map{|ts| ts.abbreviation } end - def task_completion_csv(options = {}) + def task_completion_csv task_def_by_grade = task_definitions_by_grade streams = tutorial_streams grp_sets = group_sets - CSV.generate(options) do |csv| + CSV.generate() do |csv| # Add header row csv << [ 'Student ID', @@ -1407,7 +1407,7 @@ def task_completion_csv(options = {}) "#{row['first_name']} #{row['last_name']}", GradeHelper.grade_for(row['target_grade']), row['email'], - row['portfolio_production_date'].present? && !row['compile_portfolio'] && File.exists?(FileHelper.student_portfolio_path(self, row['username'], true)), + row['portfolio_production_date'].present? && !row['compile_portfolio'] && File.exist?(FileHelper.student_portfolio_path(self, row['username'], true)), row['grade'] > 0 ? row['grade'] : nil, row['grade_rationale'] ] + [1].map do @@ -1437,7 +1437,7 @@ def get_portfolio_zip(current_user) filename = FileHelper.sanitized_filename("portfolios-#{code}-#{current_user.username}") result = "#{FileHelper.tmp_file(filename)}.zip" - return result if File.exists?(result) + return result if File.exist?(result) # Create a new zip Zip::File.open(result, Zip::File::CREATE) do |zip| active_projects.each do |project| @@ -1462,7 +1462,7 @@ def get_task_resources_zip # Get a temp file path result = FileHelper.tmp_file("task-resources-#{code}.zip") - return result if File.exists?(result) + return result if File.exist?(result) # Create a new zip Zip::File.open(result, Zip::File::CREATE) do |zip| @@ -1490,7 +1490,7 @@ def get_task_submissions_pdf_zip(current_user, td) tasks_with_files = td.related_tasks_with_files - return result if File.exists?(result) + return result if File.exist?(result) # Create a new zip Zip::File.open(result, Zip::File::CREATE) do |zip| @@ -1523,7 +1523,7 @@ def get_task_submissions_zip(current_user, td) tasks_with_files = td.related_tasks_with_files - return result if File.exists?(result) + return result if File.exist?(result) # Create a new zip Zip::File.open(result, Zip::File::CREATE) do |zip| @@ -2275,8 +2275,8 @@ def student_grades_csv end # Used to calculate the number of assessment each tutor has performed - def tutor_assessment_csv(options = {}) - CSV.generate(options) do |csv| + def tutor_assessment_csv + CSV.generate() do |csv| csv << [ 'Username', 'Tutor Name', @@ -2322,7 +2322,7 @@ def generate_batch_task_zip(user, tasks) output_zip = FileHelper.tmp_file("batch_ready_for_feedback_#{code}_#{user.username}.zip") - return result if File.exists?(output_zip) + return result if File.exist?(output_zip) # Create a new zip Zip::File.open(output_zip, Zip::File::CREATE) do |zip| diff --git a/config/secrets.yml b/config/secrets.yml index e59d46c66..71241f314 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -1,13 +1,21 @@ -development: &development +development: &development-settings secret_key_base: <%= ENV['DF_SECRET_KEY_BASE'] || '9e010ee2f52af762916406fd2ac488c5694a6cc784777136e657511f8bbc7a73f96d59c0a9a778a0d7cf6406f8ecbf77efe4701dfbd63d8248fc7cc7f32dea97' %> secret_key_attr: <%= ENV['DF_SECRET_KEY_ATTR'] || 'e69fc5960ca0e8700844a3a25fe80373b41c0a265d342eba06950113f3766fd983bad9ec51bf36eb615d9711bfe1dd90b8e35f01841b323f604ffee857e32055' %> secret_key_devise: <%= ENV['DF_SECRET_KEY_DEVISE'] || 'f4e23c4388dc600e503a09ad057b8271d8fcf4c2cd6723b44f33db638e49075fe96bc545eed9110ded0c5df505625d4e1c838b718349eecf1d39270d0829d5b9' %> secret_key_aaf: <%= ENV['DF_SECRET_KEY_AAF'] || 'secretsecret12345' %> secret_key_moss: <%= ENV['DF_SECRET_KEY_MOSS'] %> test: - <<: *development + secret_key_base: <%= ENV['DF_SECRET_KEY_BASE'] || '9e010ee2f52af762916406fd2ac488c5694a6cc784777136e657511f8bbc7a73f96d59c0a9a778a0d7cf6406f8ecbf77efe4701dfbd63d8248fc7cc7f32dea97' %> + secret_key_attr: <%= ENV['DF_SECRET_KEY_ATTR'] || 'e69fc5960ca0e8700844a3a25fe80373b41c0a265d342eba06950113f3766fd983bad9ec51bf36eb615d9711bfe1dd90b8e35f01841b323f604ffee857e32055' %> + secret_key_devise: <%= ENV['DF_SECRET_KEY_DEVISE'] || 'f4e23c4388dc600e503a09ad057b8271d8fcf4c2cd6723b44f33db638e49075fe96bc545eed9110ded0c5df505625d4e1c838b718349eecf1d39270d0829d5b9' %> + secret_key_aaf: <%= ENV['DF_SECRET_KEY_AAF'] || 'secretsecret12345' %> + secret_key_moss: <%= ENV['DF_SECRET_KEY_MOSS'] %> staging: - <<: *development + secret_key_base: <%= ENV['DF_SECRET_KEY_BASE'] || '9e010ee2f52af762916406fd2ac488c5694a6cc784777136e657511f8bbc7a73f96d59c0a9a778a0d7cf6406f8ecbf77efe4701dfbd63d8248fc7cc7f32dea97' %> + secret_key_attr: <%= ENV['DF_SECRET_KEY_ATTR'] || 'e69fc5960ca0e8700844a3a25fe80373b41c0a265d342eba06950113f3766fd983bad9ec51bf36eb615d9711bfe1dd90b8e35f01841b323f604ffee857e32055' %> + secret_key_devise: <%= ENV['DF_SECRET_KEY_DEVISE'] || 'f4e23c4388dc600e503a09ad057b8271d8fcf4c2cd6723b44f33db638e49075fe96bc545eed9110ded0c5df505625d4e1c838b718349eecf1d39270d0829d5b9' %> + secret_key_aaf: <%= ENV['DF_SECRET_KEY_AAF'] || 'secretsecret12345' %> + secret_key_moss: <%= ENV['DF_SECRET_KEY_MOSS'] %> production: secret_key_base: <%= ENV['DF_SECRET_KEY_BASE'] %> secret_key_attr: <%= ENV['DF_SECRET_KEY_ATTR'] %> diff --git a/deployApi.Dockerfile b/deployApi.Dockerfile index 73ccb0e1b..936a89b1c 100644 --- a/deployApi.Dockerfile +++ b/deployApi.Dockerfile @@ -1,7 +1,7 @@ # # deployApi.Dockerfile - the container used to host the API only # -FROM ruby:2.6.7-buster +FROM ruby:3.1-buster # Setup dependencies ARG DEBIAN_FRONTEND=noninteractive diff --git a/deployAppSvr.Dockerfile b/deployAppSvr.Dockerfile index ca6856be1..78a2c2caf 100644 --- a/deployAppSvr.Dockerfile +++ b/deployAppSvr.Dockerfile @@ -1,7 +1,7 @@ # # deployAppSrc.Dockerfile - the container used for back end processing # -FROM ruby:2.6.7-buster +FROM ruby:3.1-buster # Setup dependencies ARG DEBIAN_FRONTEND=noninteractive diff --git a/lib/helpers/database_populator.rb b/lib/helpers/database_populator.rb index 304630e4e..a7aa80b64 100644 --- a/lib/helpers/database_populator.rb +++ b/lib/helpers/database_populator.rb @@ -473,7 +473,7 @@ def self.assess_task(proj, task, tutor, status, complete_date) end pdf_path = task.final_pdf_path - if pdf_path && !File.exists?(pdf_path) + if pdf_path && !File.exist?(pdf_path) FileUtils.ln_s(Rails.root.join('test_files', 'unit_files', 'sample-student-submission.pdf'), pdf_path) end @@ -486,7 +486,7 @@ def self.generate_portfolio(project) FileUtils.mkdir_p(portfolio_tmp_dir) lsr_path = File.join(portfolio_tmp_dir, "000-document-LearningSummaryReport.pdf") - FileUtils.ln_s(Rails.root.join('test_files', 'unit_files', 'sample-learning-summary.pdf'), lsr_path) unless File.exists? lsr_path + FileUtils.ln_s(Rails.root.join('test_files', 'unit_files', 'sample-learning-summary.pdf'), lsr_path) unless File.exist? lsr_path project.compile_portfolio = true project.create_portfolio end @@ -508,7 +508,7 @@ def echo_line *args def generate_tasks_for_unit(unit, unit_details) echo "----> Generating #{unit_details[:num_tasks]} tasks" - if File.exists? Rails.root.join('test_files',"#{unit.code}-Tasks.csv") + if File.exist? Rails.root.join('test_files',"#{unit.code}-Tasks.csv") unit.import_tasks_from_csv File.open(Rails.root.join('test_files',"#{unit.code}-Tasks.csv")) unit.import_task_files_from_zip Rails.root.join('test_files',"#{unit.code}-Tasks.zip") return @@ -546,7 +546,7 @@ def generate_and_align_ilos_for_unit(unit, unit_details) # Create the ILOs echo "----> Adding #{unit_details[:ilos]} ILOs" - if File.exists? Rails.root.join('test_files',"#{unit.code}-Outcomes.csv") + if File.exist? Rails.root.join('test_files',"#{unit.code}-Outcomes.csv") unit.import_outcomes_from_csv File.open(Rails.root.join('test_files',"#{unit.code}-Outcomes.csv")) unit.import_task_alignment_from_csv File.open(Rails.root.join('test_files',"#{unit.code}-Alignment.csv")), nil return diff --git a/test/api/campuses_test.rb b/test/api/campuses_test.rb index 3a0da3534..638d490df 100644 --- a/test/api/campuses_test.rb +++ b/test/api/campuses_test.rb @@ -50,12 +50,7 @@ def test_student_cannot_post_campuses } # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(user_student), - username: user_student.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: auth_token(user_student), username: user_student.username) post_json "/api/campuses", data_to_post assert_equal 403, last_response.status @@ -85,12 +80,7 @@ def test_student_cannot_put_campuses } # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(user_student), - username: user_student.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: auth_token(user_student), username: user_student.username) put_json "/api/campuses/#{data_to_put[:campus].id}", data_to_put assert_equal 403, last_response.status @@ -122,12 +112,7 @@ def test_student_delete_campus campus = FactoryBot.create(:campus, mode: 'timetable') # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(user_student), - username: user_student.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: auth_token(user_student), username: user_student.username) # perform the delete delete_json "/api/campuses/#{campus.id}" diff --git a/test/api/csv_test.rb b/test/api/csv_test.rb index c7ccb1c7b..053a9166e 100644 --- a/test/api/csv_test.rb +++ b/test/api/csv_test.rb @@ -94,11 +94,7 @@ def test_download_csv_all_task_definitions_unit_with_incorrect_auth_token unit_id_to_test = 'string' # auth_token and username added to header - auth_data_to_header = { - auth_token: "wrong_token" - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: "wrong token", username: 'aadmin') # perform the get get "/api/csv/task_definitions?unit_id=#{unit_id_to_test}" @@ -157,11 +153,7 @@ def test_csv_upload_all_task_definitions_unit_incorrect_auth_token } # auth_token and username added to header - auth_data_to_header = { - auth_token: "wrong_token" - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: "wrong_token") # perform the POST post "/api/csv/task_definitions", data_to_post @@ -365,13 +357,8 @@ def test_download_csv_all_students_in_unit_with_incorrect_auth_token unit_id_to_test = '1' - # auth_token to header - auth_data_to_header = { - auth_token: 'wrong_token' - } - # auth_token and username added to header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: 'wrong_token') # perform the get get "/api/csv/units/#{unit_id_to_test}" @@ -411,12 +398,7 @@ def test_csv_upload_all_students_in_unit } # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(unit.main_convenor_user), - username: unit.main_convenor_user.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: auth_token(unit.main_convenor_user), username: unit.main_convenor_user.username) # perform the POST post "/api/csv/units/#{unit.id}", data_to_post @@ -439,12 +421,8 @@ def test_csv_upload_all_students_in_unit_incorrect_auth_token file: upload_file_csv('test_files/csv_test_files/COS10001-Students.csv') } - # auth_token and username added to header - auth_data_to_header = { - auth_token: "wrong_token" - } - - add_auth_header_for(auth_data_to_header) + # auth_token and username added to header + add_auth_header_for(username: 'aadmin', auth_token: "wrong_token") # perform the POST post "/api/csv/units/#{unit.id}", data_to_post @@ -499,12 +477,7 @@ def test_csv_upload_all_students_in_unit_incorrect_file_pdf } # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(unit.main_convenor_user), - username: unit.main_convenor_user.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(user: unit.main_convenor_user) # perform the POST post "/api/csv/units/#{unit.id}", data_to_post @@ -522,12 +495,7 @@ def test_csv_upload_all_students_in_unit_no_file } # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(unit.main_convenor_user), - username: unit.main_convenor_user.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: auth_token(unit.main_convenor_user), username: unit.main_convenor_user.username) # perform the POST post "/api/csv/units/#{unit.id}", data_to_post @@ -567,12 +535,7 @@ def test_csv_upload_students_un_enroll_in_unit } # auth_token and username added to header - auth_data_to_header = { - auth_token: auth_token(unit.main_convenor_user), - username: unit.main_convenor_user.username - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: auth_token(unit.main_convenor_user), username: unit.main_convenor_user.username) user_id_check = unit.projects.last.user_id @@ -598,11 +561,7 @@ def test_csv_upload_students_un_enroll_in_unit_incorrect_auth_token } # auth_token and username added to header - auth_data_to_header = { - auth_token: 'wrong_token' - } - - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: "wrong_token") # perform the POST to withdraw user from the unit post "/api/csv/units/#{unit.id}/withdraw", data_to_post @@ -620,8 +579,9 @@ def test_csv_upload_students_un_enroll_in_unit_incorrect_auth_token #POST /api/csv/units/{id}/withdraw def test_csv_upload_students_un_enroll_in_unit_empty_auth_token - unit = FactoryBot.create(:unit, code: 'COS10001', with_students: false, stream_count: 0) - unit.import_users_from_csv test_file_path 'csv_test_files/COS10001-Students.csv' + unit = FactoryBot.create(:unit, code: 'COS10001', with_students: false, stream_count: 0) + response = unit.import_users_from_csv test_file_path 'csv_test_files/COS10001-Students.csv' + assert_equal 1, unit.projects.count, response unit_id_to_test = '1' @@ -689,7 +649,7 @@ def test_csv_upload_students_un_enroll_in_unit_empty_unit_id } # auth_token and username added to header - add_auth_header_for(user: User.first) + add_auth_header_for(user: unit.main_convenor_user) user_id_check = unit.projects.last.user_id @@ -886,12 +846,8 @@ def test_download_csv_all_student_tasks_in_unit_with_incorrect_auth_token unit_id_to_test = '1' - auth_data_to_header = { - auth_token: 'wrong_token' - } - # Add authentication token to header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: 'wrong_token') # perform the get get "/api/csv/units/#{unit_id_to_test}/task_completion" @@ -993,12 +949,8 @@ def test_download_csv_stats_tutor_assessed_with_incorrect_auth_token unit_id_to_test = '1' - auth_data_to_header = { - auth_token: 'wrong_token' - } - # Add authentication token to header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: 'wrong_token') # perform the get get "/api/csv/units/#{unit_id_to_test}/tutor_assessments" @@ -1049,12 +1001,8 @@ def test_download_csv_all_users #GET /api/csv/users def test_download_csv_all_users_with_incorrect_auth_token - auth_data_to_header = { - auth_token: 'wrong_token' - } - # Add authentication token to header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: 'wrong_token') # perform the get get "/api/csv/users" @@ -1109,12 +1057,8 @@ def test_csv_upload_users_incorrect_auth_token file: upload_file_csv('test_files/csv_test_files/doubtfire_users.csv') } - auth_data_to_header = { - auth_token: 'wrong_token' - } - # Add authentication token to header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: 'wrong_token') # perform the POST to withdraw user from the unit post "/api/csv/users", data_to_post diff --git a/test/api/groups_api_test.rb b/test/api/groups_api_test.rb index b39f8c948..1e7459d1f 100644 --- a/test/api/groups_api_test.rb +++ b/test/api/groups_api_test.rb @@ -168,7 +168,7 @@ def test_pdf_comment_on_group_task post "/api/projects/#{project.id}/task_def_id/#{td.id}/comments", comment_data assert_equal 201, last_response.status - assert File.exists?(TaskComment.last.attachment_path) + assert File.exist?(TaskComment.last.attachment_path) td.destroy group.destroy @@ -295,7 +295,7 @@ def test_create_group # Add username and auth_token to Header add_auth_header_for(user: unit.main_convenor_user) - + # Try again as convenor post "/api/units/#{unit.id}/group_sets/#{gs_response['id']}/groups/#{group_response['id']}/members", {project_id: project.id} @@ -318,7 +318,7 @@ def test_group_student_count def test_group_switch_tutorial unit = FactoryBot.create :unit, group_sets: 1, groups: [{gs: 0, students: 0}] - + gs = unit.group_sets.first gs.update keep_groups_in_same_class: true, allow_students_to_manage_groups: true group1 = gs.groups.first @@ -330,15 +330,15 @@ def test_group_switch_tutorial group1.add_member p2 tutorial = FactoryBot.create :tutorial, unit: unit, campus: nil - + refute p1.enrolled_in? tutorial refute p2.enrolled_in? tutorial # Add username and auth_token to Header add_auth_header_for(user: unit.main_convenor_user) - + put "/api/units/#{unit.id}/group_sets/#{gs.id}/groups/#{group1.id}", { group: {tutorial_id: tutorial.id} } - + assert 201, last_response.status p1.reload @@ -353,7 +353,7 @@ def test_group_switch_tutorial def test_group_switch_tutorial_no_student_management unit = FactoryBot.create :unit, group_sets: 1, groups: [{gs: 0, students: 0}] - + gs = unit.group_sets.first gs.update keep_groups_in_same_class: true, allow_students_to_manage_groups: false group1 = gs.groups.first @@ -365,10 +365,10 @@ def test_group_switch_tutorial_no_student_management group1.add_member p2 tutorial = FactoryBot.create :tutorial, unit: unit, campus: nil - + refute p1.enrolled_in? tutorial refute p2.enrolled_in? tutorial - + add_auth_header_for(user: unit.main_convenor_user) put "/api/units/#{unit.id}/group_sets/#{gs.id}/groups/#{group1.id}", { group: {tutorial_id: tutorial.id} } @@ -386,7 +386,7 @@ def test_group_switch_tutorial_no_student_management def test_group_switch_tutorial_unenrolled_students unit = FactoryBot.create :unit, group_sets: 1, groups: [{gs: 0, students: 0}] - + gs = unit.group_sets.first gs.update keep_groups_in_same_class: true, allow_students_to_manage_groups: false, capacity: 2 group1 = gs.groups.first @@ -400,7 +400,7 @@ def test_group_switch_tutorial_unenrolled_students assert group1.at_capacity? tutorial = FactoryBot.create :tutorial, unit: unit, campus: nil - + refute p1.enrolled_in? tutorial refute p2.enrolled_in? tutorial diff --git a/test/api/tutorials_test.rb b/test/api/tutorials_test.rb index c3b951122..379c0e301 100644 --- a/test/api/tutorials_test.rb +++ b/test/api/tutorials_test.rb @@ -171,12 +171,8 @@ def test_post_tutorial_with_incorrect_auth_token tutorial: tutorial } - auth_data_to_header = { - auth_token: 'Incorrect_Auth_Token' - } - # Add username and auth_token to Header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: 'Incorrect_Auth_Token', username: 'aadmin') # Number of tutorials before POST number_of_tutorials = Tutorial.all.length @@ -963,12 +959,8 @@ def test_put_tutorials_with_incorrect_auth_token tutorial: tutorial } - auth_data_to_header = { - auth_token: 'Incorrect auth token' - } - # Add username and auth_token to Header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: 'Incorrect auth token') # perform the PUT with incorrect auth token put_json "/api/tutorials/#{tutorial_old.id}", data_to_put @@ -1239,12 +1231,8 @@ def test_delete_tutorials_with_incorrect_auth_token # Create a dummy tutorial tutorial = FactoryBot.create(:tutorial) - auth_data_to_header = { - auth_token: 'incorrect_auth_token' - } - # Add username and auth_token to Header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(username: 'aadmin', auth_token: 'incorrect_auth_token') # Number of tutorials before DELETE number_of_tutorials = Tutorial.all.length diff --git a/test/api/units/task_definitions_api_test.rb b/test/api/units/task_definitions_api_test.rb index 88cac7470..2f2ab2b17 100644 --- a/test/api/units/task_definitions_api_test.rb +++ b/test/api/units/task_definitions_api_test.rb @@ -199,7 +199,7 @@ def test_submission_creates_folders task = project.task_for_task_definition(td) assert File.directory? FileHelper.student_work_dir(:new, task, false) - assert File.exists? File.join(FileHelper.student_work_dir(:new, task, false), '000-document.pdf') + assert File.exist? File.join(FileHelper.student_work_dir(:new, task, false), '000-document.pdf') task.destroy @@ -247,7 +247,7 @@ def test_change_to_group_after_submissions assert task.convert_submission_to_pdf path = task.zip_file_path_for_done_task assert path - assert File.exists? path + assert File.exist? path # Change it to a group task @@ -261,10 +261,10 @@ def test_change_to_group_after_submissions task.task_definition = td path = task.zip_file_path_for_done_task assert path - assert File.exists? path + assert File.exist? path td.destroy - assert_not File.exists? path + assert_not File.exist? path end def test_task_related_to_task_def diff --git a/test/api/units_api_test.rb b/test/api/units_api_test.rb index d1ee1b99d..bb9293a49 100644 --- a/test/api/units_api_test.rb +++ b/test/api/units_api_test.rb @@ -401,18 +401,15 @@ def test_fail_units_get_by_id assert_equal 404, last_response.status end - def test_put_update_unit_custom_token(token='abcdef') + def test_put_update_unit_custom_token() unit= Unit.first + token='abcdef' data_to_put = { unit: unit } - auth_data_to_header = { - auth_token: token - } - # Add username and auth_token to Header - add_auth_header_for(auth_data_to_header) + add_auth_header_for(auth_token: token, username: 'aadmin') put_json '/api/units/1', data_to_put assert_equal 419, last_response.status diff --git a/test/helpers/json_helper.rb b/test/helpers/json_helper.rb index 383874ef6..555e83d74 100644 --- a/test/helpers/json_helper.rb +++ b/test/helpers/json_helper.rb @@ -12,14 +12,15 @@ module JsonHelper # def post_json(endpoint, data) header 'CONTENT_TYPE', 'application/json' - post URI.encode(endpoint), data.to_json + + post URI::Parser.new.escape(endpoint), data.to_json end # # PUTs a hash data as JSON with content-type "application/json" # def put_json(endpoint, data) - put URI.encode(endpoint), data.to_json, 'CONTENT_TYPE' => 'application/json' + put URI::Parser.new.escape(endpoint), data.to_json, 'CONTENT_TYPE' => 'application/json' end # @@ -27,7 +28,7 @@ def put_json(endpoint, data) # def delete_json(endpoint) header 'CONTENT_TYPE', 'application/json' - delete URI.encode(endpoint) + delete URI::Parser.new.escape(endpoint) end # diff --git a/test/models/file_helper_test.rb b/test/models/file_helper_test.rb index 73920123d..717149f35 100644 --- a/test/models/file_helper_test.rb +++ b/test/models/file_helper_test.rb @@ -7,7 +7,7 @@ def test_convert_use_with_gif Dir.mktmpdir do |dir| dest_file = "#{dir}#{File.basename(in_file, ".*")}.jpg" assert FileHelper.compress_image_to_dest(in_file, dest_file, true) - assert File.exists? dest_file + assert File.exist? dest_file end end end diff --git a/test/models/task_test.rb b/test/models/task_test.rb index c1f213f41..36387451c 100644 --- a/test/models/task_test.rb +++ b/test/models/task_test.rb @@ -76,11 +76,11 @@ def test_pdf_creation_with_gif assert task.convert_submission_to_pdf path = task.zip_file_path_for_done_task assert path - assert File.exists? path - assert File.exists? task.final_pdf_path + assert File.exist? path + assert File.exist? task.final_pdf_path td.destroy - assert_not File.exists? path + assert_not File.exist? path end def test_image_upload @@ -120,7 +120,7 @@ def test_image_upload task = project.task_for_task_definition(td) task.move_files_to_in_process(FileHelper.student_work_dir(:new)) - assert File.exists? "#{Doubtfire::Application.config.student_work_dir}/in_process/#{task.id}/000-image.jpg" + assert File.exist? "#{Doubtfire::Application.config.student_work_dir}/in_process/#{task.id}/000-image.jpg" td.destroy end @@ -163,11 +163,11 @@ def test_pdf_creation_with_jpg assert task.convert_submission_to_pdf path = task.zip_file_path_for_done_task assert path - assert File.exists? path - assert File.exists? task.final_pdf_path + assert File.exist? path + assert File.exist? task.final_pdf_path td.destroy - assert_not File.exists? path + assert_not File.exist? path end def test_pdf_with_quotes_in_task_title @@ -207,10 +207,10 @@ def test_pdf_with_quotes_in_task_title task.convert_submission_to_pdf path = task.final_pdf_path - assert File.exists? path + assert File.exist? path td.destroy - assert_not File.exists? path + assert_not File.exist? path end def test_copy_draft_learning_summary @@ -247,10 +247,10 @@ def test_copy_draft_learning_summary project.reload assert project.uses_draft_learning_summary path = File.join(project.portfolio_temp_path, '000-document-LearningSummaryReport.pdf') - assert File.exists? path + assert File.exist? path unit.destroy - assert_not File.exists? path + assert_not File.exist? path end def test_draft_learning_summary_wont_copy @@ -265,7 +265,7 @@ def test_draft_learning_summary_wont_copy FileUtils.mkdir_p(project.portfolio_temp_path) FileUtils.cp Rails.root.join('test_files/unit_files/sample-learning-summary.pdf'), path - assert File.exists? path + assert File.exist? path data_to_post = { trigger: 'ready_for_feedback' @@ -289,6 +289,6 @@ def test_draft_learning_summary_wont_copy assert_not project.uses_draft_learning_summary unit.destroy - assert_not File.exists? path + assert_not File.exist? path end end diff --git a/test/models/unit_model_test.rb b/test/models/unit_model_test.rb index a4dccf8b5..51ab18458 100644 --- a/test/models/unit_model_test.rb +++ b/test/models/unit_model_test.rb @@ -25,10 +25,10 @@ class UnitModelTest < ActiveSupport::TestCase @unit.import_task_files_from_zip Rails.root.join('test_files',"#{@unit.code}-Tasks.zip") @unit.task_definitions.each do |td| - assert File.exists?(td.task_sheet), "#{td.abbreviation} task sheet missing" + assert File.exist?(td.task_sheet), "#{td.abbreviation} task sheet missing" end - assert File.exists? @unit.task_definitions.first.task_resources + assert File.exist? @unit.task_definitions.first.task_resources end test 'rollover of task files' do @@ -38,10 +38,10 @@ class UnitModelTest < ActiveSupport::TestCase unit2 = @unit.rollover TeachingPeriod.find(2), nil, nil unit2.task_definitions.each do |td| - assert File.exists?(td.task_sheet), 'task sheet is absent' + assert File.exist?(td.task_sheet), 'task sheet is absent' end - assert File.exists?(unit2.task_definitions.first.task_resources), 'task resource is absent' + assert File.exist?(unit2.task_definitions.first.task_resources), 'task resource is absent' unit2.destroy end @@ -596,12 +596,12 @@ def test_portfolio_zip unit.active_projects.each do |p| DatabasePopulator.generate_portfolio(p) assert p.has_portfolio - assert File.exists?(p.portfolio_path) + assert File.exist?(p.portfolio_path) paths << p.portfolio_path end filename = unit.get_portfolio_zip(unit.main_convenor_user) - assert File.exists? filename + assert File.exist? filename Zip::File.open(filename) do |zip_file| assert_equal unit.active_projects.count, zip_file.count end @@ -610,7 +610,7 @@ def test_portfolio_zip unit.destroy! paths.each do |path| - refute File.exists?(path) + refute File.exist?(path) end end