diff --git a/.gitignore b/.gitignore index 835ffa9f..fe656c15 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ # Ignore bundler config /.bundle +# Ignore installed gems +vendor + # Ignore the default SQLite database and yaml_db database files /db/*.sqlite3* /db/data.yml* diff --git a/.travis.yml b/.travis.yml index 7389ef7f..182e6f5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,3 +21,5 @@ before_script: before_install: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" + - gem install bundler:2.0.1 + diff --git a/Gemfile b/Gemfile index 8072d24d..7b82ab16 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,7 @@ git_source(:github) do |repo_name| end # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '4.2.11' +gem 'rails', '~> 5.2.3' # Bootstrap gem 'bootstrap-sass' @@ -24,7 +24,7 @@ gem 'compass-rails' gem 'uglifier', '>= 1.3.0' # Use CoffeeScript for .coffee assets and views -gem 'coffee-rails', '~> 4.1.0' +gem 'coffee-rails', '~> 4.2.2' gem 'mini_racer' @@ -59,26 +59,24 @@ gem 'openstax_utilities' gem 'whenever' # Talks to Accounts (latest version is broken) -gem 'omniauth-oauth2', '~> 1.3.1' +gem 'omniauth-oauth2' # OpenStax Accounts integration -gem 'openstax_accounts', '~> 7.12.0' +gem 'openstax_accounts' + # Access control for API's gem 'doorkeeper' # API versioning and documentation gem 'representable', '~> 3.0.0' -gem 'openstax_api', '~> 8.3.0' +gem 'openstax_api' gem 'apipie-rails' gem 'maruku' # Lev framework gem 'lev' -# Ruby dsl for SQL queries -gem 'squeel' - # Contract management gem 'fine_print' @@ -94,8 +92,7 @@ gem 'mimemagic' gem 'mini_magick' # Markdown parsing -# Pinned for Rails 4.X -gem 'kramdown', '1.6.0' +gem 'kramdown' # Read Excel xlsx spreadsheet files gem 'roo' @@ -122,8 +119,7 @@ gem 'acts_as_votable' gem 'scout_apm', '~> 3.0.x' # PostgreSQL database -# Pinned for rails 4.X -gem 'pg', '~> 0.15' +gem 'pg' # HTTP requests gem 'httparty' @@ -134,7 +130,7 @@ gem 'a15k_client', branch: 'master' # Notify developers of Exceptions in production -gem 'openstax_rescue_from', '~> 3.0.0' +gem 'openstax_rescue_from' # Sentry integration (the require disables automatic Rails integration since we use rescue_from) gem 'sentry-raven', require: 'raven/base' @@ -156,6 +152,9 @@ gem 'redis-rails' # Respond to ELB healthchecks in /ping and /ping/ gem 'openstax_healthcheck' +# Reduces boot times through caching; required in config/boot.rb +gem 'bootsnap', '~> 1.4.0', require: false + group :development, :test do # Get env variables from .env file gem 'dotenv-rails' @@ -172,19 +171,19 @@ group :development, :test do # Call 'debugger' anywhere in the code to stop execution and get a debugger console gem 'byebug' + # Some controller test support + gem 'rails-controller-testing' + # Use RSpec for tests gem 'rspec-rails' # Mute asset pipeline log messages - gem 'quiet_assets' # Fixture replacement - # Pinned for rails 4.X - gem 'factory_bot_rails', '< 5.0.0' + gem 'factory_bot_rails' # Lorem Ipsum - # Pinned to avoid 18n problem - gem 'faker', '1.6.6' + gem 'faker' # Database cleaning functionality for tests gem 'database_cleaner' @@ -224,8 +223,9 @@ group :test do # Codecov integration gem 'codecov', require: false - # Test after-commit hooks - gem 'test_after_commit' + # Rubocop + gem 'rubocop-rails' + end group :production do diff --git a/Gemfile.lock b/Gemfile.lock index a7f06a34..51e1e10e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,64 +11,73 @@ GIT GEM remote: https://rubygems.org/ specs: - action_interceptor (1.1.1) - rails (>= 3.1, < 5.0) - actionmailer (4.2.11) - actionpack (= 4.2.11) - actionview (= 4.2.11) - activejob (= 4.2.11) + action_interceptor (1.1.2) + rails (>= 3.1) + actioncable (5.2.3) + actionpack (= 5.2.3) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailer (5.2.3) + actionpack (= 5.2.3) + actionview (= 5.2.3) + activejob (= 5.2.3) mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.11) - actionview (= 4.2.11) - activesupport (= 4.2.11) - rack (~> 1.6) - rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.5) + rails-dom-testing (~> 2.0) + actionpack (5.2.3) + actionview (= 5.2.3) + activesupport (= 5.2.3) + rack (~> 2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.11) - activesupport (= 4.2.11) + actionview (5.2.3) + activesupport (= 5.2.3) builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.5) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - active_attr (0.12.0) - activemodel (>= 3.0.2, < 6.0) - activesupport (>= 3.0.2, < 6.0) - activejob (4.2.11) - activesupport (= 4.2.11) - globalid (>= 0.3.0) - activemodel (4.2.11) - activesupport (= 4.2.11) - builder (~> 3.1) - activerecord (4.2.11) - activemodel (= 4.2.11) - activesupport (= 4.2.11) - arel (~> 6.0) - activesupport (4.2.11) - i18n (~> 0.7) + active_attr (0.13.1) + activemodel (>= 3.0.2, < 6.1) + activesupport (>= 3.0.2, < 6.1) + activejob (5.2.3) + activesupport (= 5.2.3) + globalid (>= 0.3.6) + activemodel (5.2.3) + activesupport (= 5.2.3) + activerecord (5.2.3) + activemodel (= 5.2.3) + activesupport (= 5.2.3) + arel (>= 9.0) + activestorage (5.2.3) + actionpack (= 5.2.3) + activerecord (= 5.2.3) + marcel (~> 0.3.1) + activesupport (5.2.3) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) acts_as_votable (0.12.0) addressable (2.6.0) public_suffix (>= 2.0.2, < 4.0) - apipie-rails (0.5.15) + apipie-rails (0.5.16) rails (>= 4.1) - arel (6.0.4) - autoprefixer-rails (9.5.0) + arel (9.0.0) + ast (2.4.0) + autoprefixer-rails (9.6.0) execjs aws-ses (0.6.0) builder mail (> 2.2.5) mime-types xml-simple - baby_squeel (0.3.1) - activerecord (>= 4.0.0) + bindex (0.7.0) + bootsnap (1.4.4) + msgpack (~> 1.0) bootstrap-sass (3.4.1) autoprefixer-rails (>= 5.2.1) sassc (>= 2.0.0) - brakeman (4.5.0) + brakeman (4.5.1) builder (3.2.3) byebug (11.0.1) carrierwave (1.3.1) @@ -86,18 +95,18 @@ GEM json simplecov url - coffee-rails (4.1.1) + coffee-rails (4.2.2) coffee-script (>= 2.2.0) - railties (>= 4.0.0, < 5.1.x) + railties (>= 4.0.0) coffee-rails-source-maps (1.4.0) coffee-script-source (>= 1.6.1) coffee-script (2.4.1) coffee-script-source execjs coffee-script-source (1.12.2) - commontator (4.11.1) + commontator (5.1.0) jquery-rails - rails (>= 3.1) + rails (>= 5.0) compass (1.0.3) chunky_png (~> 1.2) compass-core (~> 1.0.2) @@ -118,15 +127,14 @@ GEM crass (1.0.4) daemons (1.3.1) database_cleaner (1.7.0) - debug_inspector (0.0.3) declarative (0.0.10) deep_cloneable (2.4.0) activerecord (>= 3.1.0, < 6) diff-lcs (1.3) diffy (3.3.0) docile (1.1.5) - doorkeeper (5.0.2) - railties (>= 4.2) + doorkeeper (5.1.0) + railties (>= 5) dotenv (2.7.2) dotenv-rails (2.7.2) dotenv (= 2.7.2) @@ -137,32 +145,31 @@ GEM execjs eco-source (1.1.0.rc.1) ejs (1.1.1) - erubis (2.7.0) + erubi (1.8.0) ethon (0.12.0) ffi (>= 1.3.0) eventmachine (1.2.7) exception_notification (4.3.0) actionmailer (>= 4.0, < 6) activesupport (>= 4.0, < 6) - excon (0.62.0) + excon (0.64.0) execjs (2.7.0) - factory_bot (4.11.1) - activesupport (>= 3.0.0) - factory_bot_rails (4.11.1) - factory_bot (~> 4.11.1) - railties (>= 3.0.0) - faker (1.6.6) - i18n (~> 0.5) + factory_bot (5.0.2) + activesupport (>= 4.2.0) + factory_bot_rails (5.0.2) + factory_bot (~> 5.0.2) + railties (>= 4.2.0) + faker (1.9.3) + i18n (>= 0.7) faraday (0.15.4) multipart-post (>= 1.2, < 3) - ffi (1.10.0) - fine_print (4.0.1) - action_interceptor (>= 1.0) - baby_squeel + ffi (1.11.1) + fine_print (5.0.0) + action_interceptor jquery-rails - rails (>= 4.2) + rails responders - fog-aws (3.4.0) + fog-aws (3.5.0) fog-core (~> 2.1) fog-json (~> 1.1) fog-xml (~> 0.1) @@ -183,12 +190,13 @@ GEM globalid (0.4.2) activesupport (>= 4.2.0) hashie (3.6.0) - httparty (0.16.4) + httparty (0.17.0) mime-types (~> 3.0) multi_xml (>= 0.5.2) - i18n (0.9.5) + i18n (1.6.0) concurrent-ruby (~> 1.0) ipaddress (0.8.3) + jaro_winkler (1.5.2) jquery-rails (4.3.3) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -196,10 +204,10 @@ GEM jquery-ui-rails (6.0.1) railties (>= 3.2.16) json (2.2.0) - jwt (2.1.0) + jwt (2.2.1) keyword_search (1.5.0) kgio (2.11.2) - kramdown (1.6.0) + kramdown (2.1.0) lev (9.0.1) actionpack (>= 4.2) active_attr @@ -209,8 +217,8 @@ GEM hashie transaction_isolation transaction_retry - libv8 (6.7.288.46.1) - lograge (0.10.0) + libv8 (7.3.492.27.1) + lograge (0.11.1) actionpack (>= 4) activesupport (>= 4) railties (>= 4) @@ -220,22 +228,27 @@ GEM nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) + marcel (0.3.3) + mimemagic (~> 0.3.2) maruku (0.7.3) + method_source (0.9.2) mime-types (3.2.2) mime-types-data (~> 3.2015) - mime-types-data (3.2018.0812) + mime-types-data (3.2019.0331) mimemagic (0.3.3) mini_magick (4.9.3) mini_mime (1.0.1) mini_portile2 (2.4.0) - mini_racer (0.2.4) - libv8 (>= 6.3) + mini_racer (0.2.6) + libv8 (>= 6.9.411) minitest (5.11.3) + msgpack (1.2.10) multi_json (1.13.1) multi_xml (0.6.0) - multipart-post (2.0.0) + multipart-post (2.1.1) nifty-generators (0.4.6) - nokogiri (1.10.2) + nio4r (2.3.1) + nokogiri (1.10.3) mini_portile2 (~> 2.4.0) nokogumbo (2.0.1) nokogiri (~> 1.8, >= 1.8.4) @@ -245,33 +258,33 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - oj (3.7.11) + oj (3.7.12) oj_mimic_json (1.0.1) omniauth (1.9.0) hashie (>= 3.4.6, < 3.7.0) rack (>= 1.6.2, < 3) - omniauth-oauth2 (1.3.1) - oauth2 (~> 1.0) - omniauth (~> 1.2) - openstax_accounts (7.12.0) - action_interceptor (>= 1.0) - keyword_search (>= 1.0.0) - lev (>= 2.2.1) - oauth2 (>= 0.5.0) - omniauth (>= 1.1) - omniauth-oauth2 (>= 1.1) - openstax_api (>= 3.1.0) - openstax_utilities (>= 4.1.0) + omniauth-oauth2 (1.6.0) + oauth2 (~> 1.1) + omniauth (~> 1.9) + openstax_accounts (8.0.0) + action_interceptor + keyword_search + lev + oauth2 + omniauth + omniauth-oauth2 + openstax_api + openstax_utilities pg - rails (>= 4.1, < 5.0) - representable (>= 2.0) - roar (>= 1.0) - openstax_api (8.3.2) + rails + representable + roar + openstax_api (9.0.0) doorkeeper (>= 2.0) exception_notification (>= 4.0) lev (>= 1.0.0) openstax_utilities (>= 4.2.0) - rails (>= 3.1, < 5.0) + rails (>= 3.1) representable (>= 2.4, < 4.0) responders roar (>= 1.0) @@ -279,61 +292,64 @@ GEM uber (< 0.1.0) openstax_healthcheck (0.0.3) rails (>= 3.0) - openstax_rescue_from (3.0.0) + openstax_rescue_from (4.0.0) rails (>= 3.1, < 6.0) openstax_utilities (4.2.3) keyword_search lev rails (>= 3.1) pager (1.0.1) - parallel (1.16.0) - parallel_tests (2.28.0) + parallel (1.17.0) + parallel_tests (2.29.0) parallel - pg (0.21.0) - polyamorous (1.1.0) - activerecord (>= 3.0) - public_suffix (3.0.3) - quiet_assets (1.1.0) - railties (>= 3.1, < 5.0) - rack (1.6.11) - rack-test (0.6.3) - rack (>= 1.0) + parser (2.6.3.0) + ast (~> 2.4.0) + pg (1.1.4) + public_suffix (3.1.0) + rack (2.0.7) + rack-test (1.1.0) + rack (>= 1.0, < 3) railroady (1.5.3) - rails (4.2.11) - actionmailer (= 4.2.11) - actionpack (= 4.2.11) - actionview (= 4.2.11) - activejob (= 4.2.11) - activemodel (= 4.2.11) - activerecord (= 4.2.11) - activesupport (= 4.2.11) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.11) - sprockets-rails - rails-deprecated_sanitizer (1.0.3) - activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.9) - activesupport (>= 4.2.0, < 5.0) - nokogiri (~> 1.6) - rails-deprecated_sanitizer (>= 1.0.1) - rails-erd (1.5.2) - activerecord (>= 3.2) - activesupport (>= 3.2) + rails (5.2.3) + actioncable (= 5.2.3) + actionmailer (= 5.2.3) + actionpack (= 5.2.3) + actionview (= 5.2.3) + activejob (= 5.2.3) + activemodel (= 5.2.3) + activerecord (= 5.2.3) + activestorage (= 5.2.3) + activesupport (= 5.2.3) + bundler (>= 1.3.0) + railties (= 5.2.3) + sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.4) + actionpack (>= 5.0.1.x) + actionview (>= 5.0.1.x) + activesupport (>= 5.0.1.x) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-erd (1.6.0) + activerecord (>= 4.2) + activesupport (>= 4.2) choice (~> 0.2.0) ruby-graphviz (~> 1.2) rails-html-sanitizer (1.0.4) loofah (~> 2.2, >= 2.2.2) - railties (4.2.11) - actionpack (= 4.2.11) - activesupport (= 4.2.11) + railties (5.2.3) + actionpack (= 5.2.3) + activesupport (= 5.2.3) + method_source rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) + thor (>= 0.19.0, < 2.0) + rainbow (3.0.0) raindrops (0.19.0) rake (12.3.2) rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.0) - redis (4.1.0) + redis (4.1.2) redis-actionpack (5.0.2) actionpack (>= 4.0, < 6) redis-rack (>= 1, < 3) @@ -350,7 +366,7 @@ GEM redis-store (>= 1.2, < 2) redis-store (1.6.0) redis (>= 2.2, < 5) - remotipart (1.4.2) + remotipart (1.4.3) representable (3.0.0) declarative (~> 0.0.5) uber (~> 0.0.15) @@ -359,7 +375,7 @@ GEM responders (2.4.1) actionpack (>= 4.2.0, < 6.0) railties (>= 4.2.0, < 6.0) - rinku (2.0.5) + rinku (2.0.6) roar (1.0.3) representable (>= 2.0.1, <= 3.0.0) roar-rails (1.0.1) @@ -378,7 +394,7 @@ GEM rspec-mocks (~> 3.8.0) rspec-core (3.8.0) rspec-support (~> 3.8.0) - rspec-expectations (3.8.2) + rspec-expectations (3.8.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-instafail (1.0.0) @@ -395,8 +411,19 @@ GEM rspec-mocks (~> 3.8.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) + rubocop (0.71.0) + jaro_winkler (~> 1.5.1) + parallel (~> 1.10) + parser (>= 2.6) + rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 1.7) + rubocop-rails (2.0.0) + rack (>= 2.0) + rubocop (>= 0.70.0) ruby-graphviz (1.2.4) - rubyzip (1.2.2) + ruby-progressbar (1.10.1) + rubyzip (1.2.3) sanitize (5.0.0) crass (~> 1.0.2) nokogiri (>= 1.8.0) @@ -421,9 +448,8 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - sortability (0.1.0) + sortability (1.0.0) rails - squeel sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -431,12 +457,6 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - squeel (1.2.3) - activerecord (>= 3.0) - activesupport (>= 3.0) - polyamorous (~> 1.1.0) - test_after_commit (1.2.2) - activerecord (>= 3.2, < 5.0) test_xml (0.1.8) diffy (~> 3.0) nokogiri (>= 1.3.2) @@ -463,18 +483,23 @@ GEM uber (0.0.15) uglifier (4.1.20) execjs (>= 0.3.0, < 3) - unicorn (5.5.0) + unicode-display_width (1.6.0) + unicorn (5.5.1) kgio (~> 2.6) raindrops (~> 0.7) unicorn-worker-killer (0.4.4) get_process_mem (~> 0) unicorn (>= 4, < 6) url (0.3.2) - web-console (3.3.0) - activemodel (>= 4.2) - debug_inspector - railties (>= 4.2) - whenever (0.10.0) + web-console (3.7.0) + actionview (>= 5.0) + activemodel (>= 5.0) + bindex (>= 0.4.0) + railties (>= 5.0) + websocket-driver (0.7.0) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.3) + whenever (0.11.0) chronic (>= 0.6.3) xml-simple (1.1.5) @@ -488,6 +513,7 @@ DEPENDENCIES apipie-rails autoprefixer-rails aws-ses (~> 0.6.0) + bootsnap (~> 1.4.0) bootstrap-sass brakeman byebug @@ -495,7 +521,7 @@ DEPENDENCIES cheat codeclimate-test-reporter codecov - coffee-rails (~> 4.1.0) + coffee-rails (~> 4.2.2) coffee-rails-source-maps commontator compass-rails @@ -505,15 +531,15 @@ DEPENDENCIES dotenv-rails eco ejs - factory_bot_rails (< 5.0.0) - faker (= 1.6.6) + factory_bot_rails + faker fine_print fog-aws httparty jquery-rails jquery-ui-rails keyword_search - kramdown (= 1.6.0) + kramdown lev lograge maruku @@ -523,17 +549,17 @@ DEPENDENCIES nifty-generators oj oj_mimic_json - omniauth-oauth2 (~> 1.3.1) - openstax_accounts (~> 7.12.0) - openstax_api (~> 8.3.0) + omniauth-oauth2 + openstax_accounts + openstax_api openstax_healthcheck - openstax_rescue_from (~> 3.0.0) + openstax_rescue_from openstax_utilities parallel_tests - pg (~> 0.15) - quiet_assets + pg railroady - rails (= 4.2.11) + rails (~> 5.2.3) + rails-controller-testing rails-erd rails-html-sanitizer redis-rails @@ -544,14 +570,13 @@ DEPENDENCIES roo rspec-instafail rspec-rails + rubocop-rails sanitize sass-rails (~> 5.0) scout_apm (~> 3.0.x) sentry-raven shoulda-matchers sortability - squeel - test_after_commit thin timecop turbolinks @@ -562,4 +587,4 @@ DEPENDENCIES whenever BUNDLED WITH - 1.17.3 + 2.0.1 diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 0f8e972d..516932ec 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -1,8 +1,8 @@ module Admin class BaseController < ApplicationController - skip_before_filter :authenticate_user! if Rails.env.development? - before_filter :authenticate_admin! unless Rails.env.development? + skip_before_action :authenticate_user! if Rails.env.development? + before_action :authenticate_admin! unless Rails.env.development? fine_print_skip :general_terms_of_use, :privacy_policy diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 341bff8f..9a0404dc 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,7 +1,7 @@ module Admin class UsersController < BaseController - before_filter :set_user, only: [:show, :edit, :update, :become, :delete, :undelete] + before_action :set_user, only: [:show, :edit, :update, :become, :delete, :undelete] # GET /users def index diff --git a/app/controllers/api/v1/attachments_controller.rb b/app/controllers/api/v1/attachments_controller.rb index 44b76988..2117ca65 100644 --- a/app/controllers/api/v1/attachments_controller.rb +++ b/app/controllers/api/v1/attachments_controller.rb @@ -3,8 +3,8 @@ class AttachmentsController < OpenStax::Api::V1::ApiController include Exercises::Finders - before_filter :find_exercise_or_create_draft, only: [:create] - before_filter :find_exercise, only: [:destroy] + before_action :find_exercise_or_create_draft, only: [:create] + before_action :find_exercise, only: [:destroy] ########## # create # diff --git a/app/controllers/api/v1/community_solutions_controller.rb b/app/controllers/api/v1/community_solutions_controller.rb index 717cabd9..1d3ebe4b 100644 --- a/app/controllers/api/v1/community_solutions_controller.rb +++ b/app/controllers/api/v1/community_solutions_controller.rb @@ -1,7 +1,7 @@ module Api::V1 class CommunitySolutionsController < OpenStax::Api::V1::ApiController - before_filter :get_community_solution, only: [:show, :update, :destroy] + before_action :get_community_solution, only: [:show, :update, :destroy] resource_description do api_versions "v1" diff --git a/app/controllers/api/v1/exercises_controller.rb b/app/controllers/api/v1/exercises_controller.rb index 1872f130..2cc1334a 100644 --- a/app/controllers/api/v1/exercises_controller.rb +++ b/app/controllers/api/v1/exercises_controller.rb @@ -3,8 +3,8 @@ class ExercisesController < OpenStax::Api::V1::ApiController include ::Exercises::Finders - before_filter :find_exercise_or_create_draft, only: [:show, :update] - before_filter :find_exercise, only: [:destroy] + before_action :find_exercise_or_create_draft, only: [:show, :update] + before_action :find_exercise, only: [:destroy] resource_description do api_versions "v1" @@ -119,10 +119,12 @@ def create OSU::AccessPolicy.require_action_allowed!(:create, current_api_user, exercise) - if exercise.save && publication_group.save + if publication.save && publication_group.save && exercise.save respond_with exercise, create_options.merge(represent_with_options) else - render_api_errors(publication_group.errors) || render_api_errors(exercise.errors) + render_api_errors(publication.errors) || + render_api_errors(publication_group.errors) || + render_api_errors(exercise.errors) raise ActiveRecord::Rollback end end diff --git a/app/controllers/api/v1/publications_controller.rb b/app/controllers/api/v1/publications_controller.rb index 14a44469..a39fed99 100644 --- a/app/controllers/api/v1/publications_controller.rb +++ b/app/controllers/api/v1/publications_controller.rb @@ -7,7 +7,7 @@ class PublicationsController < OpenStax::Api::V1::ApiController 'VocabTerm' => 'Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter', } - before_filter :get_publishable + before_action :get_publishable resource_description do api_versions "v1" diff --git a/app/controllers/api/v1/vocab_terms_controller.rb b/app/controllers/api/v1/vocab_terms_controller.rb index 33c9aa18..19182b1b 100644 --- a/app/controllers/api/v1/vocab_terms_controller.rb +++ b/app/controllers/api/v1/vocab_terms_controller.rb @@ -1,8 +1,8 @@ module Api::V1 class VocabTermsController < OpenStax::Api::V1::ApiController - before_filter :get_vocab_term_or_create_draft, only: [:show, :update] - before_filter :get_vocab_term, only: :destroy + before_action :get_vocab_term_or_create_draft, only: [:show, :update] + before_action :get_vocab_term, only: :destroy resource_description do api_versions "v1" diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c83f6fb7..207f1ea3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,7 +7,7 @@ class ApplicationController < ActionController::Base include Lev::HandleWith - before_filter :authenticate_user!, :valid_user! + before_action :authenticate_user!, :valid_user! fine_print_require :general_terms_of_use, :privacy_policy diff --git a/app/controllers/dev/base_controller.rb b/app/controllers/dev/base_controller.rb index 312de862..12027b9f 100644 --- a/app/controllers/dev/base_controller.rb +++ b/app/controllers/dev/base_controller.rb @@ -4,9 +4,9 @@ module Dev class BaseController < ApplicationController - before_filter :development_or_test! + before_action :development_or_test! - skip_before_filter :authenticate_user! + skip_before_action :authenticate_user! fine_print_skip :general_terms_of_use, :privacy_policy protected diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index 57f2b1d4..9a911150 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -2,7 +2,7 @@ class StaticPagesController < ApplicationController respond_to :html - skip_before_filter :authenticate_user!, + skip_before_action :authenticate_user!, only: [:about, :contact, :copyright, :developers, :help, :privacy, :share, :status, :terms] fine_print_skip :general_terms_of_use, :privacy_policy, diff --git a/app/controllers/webview_controller.rb b/app/controllers/webview_controller.rb index 72fa2ba7..3012b842 100644 --- a/app/controllers/webview_controller.rb +++ b/app/controllers/webview_controller.rb @@ -4,7 +4,7 @@ class WebviewController < ApplicationController layout :resolve_layout - skip_before_filter :authenticate_user!, only: :home + skip_before_action :authenticate_user!, only: :home fine_print_skip :general_terms_of_use, :privacy_policy, only: :home def home diff --git a/app/models/administrator.rb b/app/models/administrator.rb index c09bc8a1..85cc0f73 100644 --- a/app/models/administrator.rb +++ b/app/models/administrator.rb @@ -1,7 +1,7 @@ -class Administrator < ActiveRecord::Base +class Administrator < ApplicationRecord belongs_to :user - validates :user, presence: true, uniqueness: true + validates :user, uniqueness: true end diff --git a/app/models/answer.rb b/app/models/answer.rb index a4e2ae17..8201884b 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -1,4 +1,4 @@ -class Answer < ActiveRecord::Base +class Answer < ApplicationRecord attr_accessor :temp_id @@ -10,7 +10,6 @@ class Answer < ActiveRecord::Base has_many :combo_choice_answers, dependent: :destroy - validates :question, presence: true validates :content, presence: true end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 00000000..10a4cba8 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/attachment.rb b/app/models/attachment.rb index cfcaa6cd..9003a520 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -1,4 +1,4 @@ -class Attachment < ActiveRecord::Base +class Attachment < ApplicationRecord mount_uploader :asset, AssetUploader @@ -7,7 +7,6 @@ class Attachment < ActiveRecord::Base before_update { raise ActiveRecord::ReadOnlyRecord } # Prevent updates validates :asset, presence: true - validates :parent, presence: true validate :unique_asset def filename @@ -22,11 +21,9 @@ def remove_asset! def unique_asset return if asset.nil? || parent.nil? - return unless Attachment.where {(id != my{id}) & \ - (parent == my{parent}) & \ - (asset == my{asset.identifier})}.exists? + return unless Attachment.where.not(id: id).where({parent_id: parent.id, asset: asset.identifier}).exists? errors.add(:asset, 'has already been associated with this resource') - false + throw(:abort) end end diff --git a/app/models/author.rb b/app/models/author.rb index 7b503c53..a2ef81c9 100644 --- a/app/models/author.rb +++ b/app/models/author.rb @@ -1,11 +1,10 @@ -class Author < ActiveRecord::Base +class Author < ApplicationRecord sortable_belongs_to :publication, inverse_of: :authors belongs_to :user - validates :publication, presence: true - validates :user, presence: true, uniqueness: { scope: :publication_id } + validates :user, uniqueness: { scope: :publication_id } delegate :name, to: :user diff --git a/app/models/class_license.rb b/app/models/class_license.rb index eeb6a364..9467d771 100644 --- a/app/models/class_license.rb +++ b/app/models/class_license.rb @@ -1,4 +1,4 @@ -class ClassLicense < ActiveRecord::Base +class ClassLicense < ApplicationRecord sortable_class scope: :class_name @@ -7,7 +7,6 @@ class ClassLicense < ActiveRecord::Base has_many :same_class, class_name: 'ClassLicense', foreign_key: :class_name, primary_key: :class_name - validates :license, presence: true validates :class_name, presence: true, uniqueness: { scope: :license_id } end diff --git a/app/models/collaborator_solution.rb b/app/models/collaborator_solution.rb index 18d733a3..16086c7d 100644 --- a/app/models/collaborator_solution.rb +++ b/app/models/collaborator_solution.rb @@ -1,3 +1,3 @@ -class CollaboratorSolution < ActiveRecord::Base +class CollaboratorSolution < ApplicationRecord solution end diff --git a/app/models/combo_choice.rb b/app/models/combo_choice.rb index 417cc357..cbf6811c 100644 --- a/app/models/combo_choice.rb +++ b/app/models/combo_choice.rb @@ -1,10 +1,9 @@ -class ComboChoice < ActiveRecord::Base +class ComboChoice < ApplicationRecord belongs_to :stem has_many :combo_choice_answers, dependent: :destroy - validates :stem, presence: true validates :correctness, presence: true, numericality: { greater_than_or_equal_to: 0.0, less_than_or_equal_to: 1.0 } diff --git a/app/models/combo_choice_answer.rb b/app/models/combo_choice_answer.rb index 5768674a..091354ae 100644 --- a/app/models/combo_choice_answer.rb +++ b/app/models/combo_choice_answer.rb @@ -1,10 +1,9 @@ -class ComboChoiceAnswer < ActiveRecord::Base +class ComboChoiceAnswer < ApplicationRecord belongs_to :combo_choice belongs_to :answer - validates :combo_choice, presence: true - validates :answer, presence: true, uniqueness: { scope: :combo_choice_id } + validates :answer, uniqueness: { scope: :combo_choice_id } validate :same_question protected diff --git a/app/models/community_solution.rb b/app/models/community_solution.rb index b18278bb..fd57a8ce 100644 --- a/app/models/community_solution.rb +++ b/app/models/community_solution.rb @@ -1,4 +1,4 @@ -class CommunitySolution < ActiveRecord::Base +class CommunitySolution < ApplicationRecord publishable solution end diff --git a/app/models/copyright_holder.rb b/app/models/copyright_holder.rb index e0ec514a..4027d4e4 100644 --- a/app/models/copyright_holder.rb +++ b/app/models/copyright_holder.rb @@ -1,11 +1,10 @@ -class CopyrightHolder < ActiveRecord::Base +class CopyrightHolder < ApplicationRecord sortable_belongs_to :publication, inverse_of: :copyright_holders belongs_to :user - validates :publication, presence: true - validates :user, presence: true, uniqueness: { scope: :publication_id } + validates :user, uniqueness: { scope: :publication_id } delegate :name, to: :user diff --git a/app/models/deputization.rb b/app/models/deputization.rb index 3ba86426..de1cf05c 100644 --- a/app/models/deputization.rb +++ b/app/models/deputization.rb @@ -1,10 +1,8 @@ -class Deputization < ActiveRecord::Base +class Deputization < ApplicationRecord belongs_to :deputizer, class_name: 'User', inverse_of: :child_deputizations belongs_to :deputy, polymorphic: true - validates :deputy, presence: true - validates :deputizer, presence: true, - uniqueness: { scope: [:deputy_type, :deputy_id] } + validates :deputizer, uniqueness: { scope: [:deputy_type, :deputy_id] } end diff --git a/app/models/derivation.rb b/app/models/derivation.rb index dded3686..8597cbb2 100644 --- a/app/models/derivation.rb +++ b/app/models/derivation.rb @@ -1,9 +1,8 @@ -class Derivation < ActiveRecord::Base +class Derivation < ApplicationRecord sortable_belongs_to :derived_publication, class_name: 'Publication', inverse_of: :sources - belongs_to :source_publication, class_name: 'Publication', inverse_of: :derivations + belongs_to :source_publication, class_name: 'Publication', inverse_of: :derivations, optional: true - validates :derived_publication, presence: true validates :source_publication, uniqueness: { scope: :derived_publication_id, allow_nil: true } validate :different_publications, :source_or_custom diff --git a/app/models/exercise.rb b/app/models/exercise.rb index 7256b588..d7856889 100644 --- a/app/models/exercise.rb +++ b/app/models/exercise.rb @@ -1,4 +1,4 @@ -class Exercise < ActiveRecord::Base +class Exercise < ApplicationRecord EQUALITY_ASSOCIATIONS = [ :attachments, @@ -87,7 +87,7 @@ class Exercise < ActiveRecord::Base sortable_has_many :questions, dependent: :destroy, autosave: true, inverse_of: :exercise - belongs_to :vocab_term, touch: true + belongs_to :vocab_term, touch: true, optional: true scope :can_release_to_a15k, -> { where(release_to_a15k: true) } scope :not_released_to_a15k, -> { where(a15k_identifier: nil) } @@ -138,11 +138,9 @@ def before_publication question.stems.each do |stem| next if stem.stem_answers.empty? || stem.stem_answers.any?{ |sa| sa.is_correct? } errors.add(:base, 'has a question with only incorrect answers') - return false + throw(:abort) end end - - true end protected @@ -150,7 +148,7 @@ def before_publication def has_questions return unless questions.first.nil? errors.add(:questions, "can't be blank") - false + throw(:abort) end end diff --git a/app/models/exercise_tag.rb b/app/models/exercise_tag.rb index 0c6a9c7b..d284542f 100644 --- a/app/models/exercise_tag.rb +++ b/app/models/exercise_tag.rb @@ -1,7 +1,6 @@ -class ExerciseTag < ActiveRecord::Base +class ExerciseTag < ApplicationRecord belongs_to :exercise belongs_to :tag - validates :exercise, presence: true - validates :tag, presence: true, uniqueness: { scope: :exercise_id } + validates :tag, uniqueness: { scope: :exercise_id } end diff --git a/app/models/hint.rb b/app/models/hint.rb index 66d87a1f..c1e6bee7 100644 --- a/app/models/hint.rb +++ b/app/models/hint.rb @@ -1,8 +1,7 @@ -class Hint < ActiveRecord::Base +class Hint < ApplicationRecord sortable_belongs_to :question, inverse_of: :hints - validates :question, presence: true validates :content, presence: true end diff --git a/app/models/license.rb b/app/models/license.rb index 46ee31df..1f1667a2 100644 --- a/app/models/license.rb +++ b/app/models/license.rb @@ -1,4 +1,4 @@ -class License < ActiveRecord::Base +class License < ApplicationRecord has_many :publications, dependent: :destroy has_many :class_licenses, dependent: :destroy diff --git a/app/models/license_compatibility.rb b/app/models/license_compatibility.rb index 3c3cc7b7..9c9b6db9 100644 --- a/app/models/license_compatibility.rb +++ b/app/models/license_compatibility.rb @@ -1,11 +1,10 @@ -class LicenseCompatibility < ActiveRecord::Base +class LicenseCompatibility < ApplicationRecord belongs_to :original_license, class_name: 'License', inverse_of: :combined_license_compatibilities belongs_to :combined_license, class_name: 'License', inverse_of: :original_license_compatibilities - validates :original_license, presence: true - validates :combined_license, presence: true, uniqueness: { scope: :original_license_id } + validates :combined_license, uniqueness: { scope: :original_license_id } end diff --git a/app/models/list.rb b/app/models/list.rb index c776dc46..1b552e1c 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -1,4 +1,4 @@ -class List < ActiveRecord::Base +class List < ApplicationRecord publishable diff --git a/app/models/list_editor.rb b/app/models/list_editor.rb index 8312e4a9..d2e356b1 100644 --- a/app/models/list_editor.rb +++ b/app/models/list_editor.rb @@ -1,9 +1,8 @@ -class ListEditor < ActiveRecord::Base +class ListEditor < ApplicationRecord belongs_to :list, inverse_of: :list_editors belongs_to :editor, polymorphic: true - validates :list, presence: true, uniqueness: { scope: [:editor_type, :editor_id] } - validates :editor, presence: true + validates :list, uniqueness: { scope: [:editor_type, :editor_id] } end diff --git a/app/models/list_nesting.rb b/app/models/list_nesting.rb index 781cdcf3..ffb153a0 100644 --- a/app/models/list_nesting.rb +++ b/app/models/list_nesting.rb @@ -1,9 +1,8 @@ -class ListNesting < ActiveRecord::Base +class ListNesting < ApplicationRecord belongs_to :parent_list, class_name: 'List', inverse_of: :child_list_nestings belongs_to :child_list, class_name: 'List', inverse_of: :parent_list_nesting - validates :parent_list, presence: true - validates :child_list, presence: true, uniqueness: true + validates :child_list, uniqueness: true end diff --git a/app/models/list_owner.rb b/app/models/list_owner.rb index c3f75480..9bea0f80 100644 --- a/app/models/list_owner.rb +++ b/app/models/list_owner.rb @@ -1,9 +1,8 @@ -class ListOwner < ActiveRecord::Base +class ListOwner < ApplicationRecord belongs_to :list, inverse_of: :list_owners belongs_to :owner, polymorphic: true - validates :list, presence: true, uniqueness: { scope: [:owner_type, :owner_id] } - validates :owner, presence: true + validates :list, uniqueness: { scope: [:owner_type, :owner_id] } end diff --git a/app/models/list_publication_group.rb b/app/models/list_publication_group.rb index e47c6be5..4a788bb4 100644 --- a/app/models/list_publication_group.rb +++ b/app/models/list_publication_group.rb @@ -1,9 +1,8 @@ -class ListPublicationGroup < ActiveRecord::Base +class ListPublicationGroup < ApplicationRecord sortable_belongs_to :list, inverse_of: :list_publication_groups belongs_to :publication_group, inverse_of: :list_publication_groups - validates :list, presence: true - validates :publication_group, presence: true, uniqueness: { scope: :list_id } + validates :publication_group, uniqueness: { scope: :list_id } end diff --git a/app/models/list_reader.rb b/app/models/list_reader.rb index cc7e7e7a..bfb5b0d3 100644 --- a/app/models/list_reader.rb +++ b/app/models/list_reader.rb @@ -1,9 +1,8 @@ -class ListReader < ActiveRecord::Base +class ListReader < ApplicationRecord belongs_to :list, inverse_of: :list_readers belongs_to :reader, polymorphic: true - validates :list, presence: true, uniqueness: { scope: [:reader_type, :reader_id] } - validates :reader, presence: true + validates :list, uniqueness: { scope: [:reader_type, :reader_id] } end diff --git a/app/models/logic.rb b/app/models/logic.rb index d6386608..8763d53f 100644 --- a/app/models/logic.rb +++ b/app/models/logic.rb @@ -1,10 +1,9 @@ -class Logic < ActiveRecord::Base +class Logic < ApplicationRecord belongs_to :parent, polymorphic: true, inverse_of: :logic has_many :logic_variables, dependent: :destroy - validates :parent, presence: true validates :parent_id, uniqueness: { scope: :parent_type } validates :language, presence: true, inclusion: { in: Language.all } validates :code, presence: true diff --git a/app/models/logic_variable.rb b/app/models/logic_variable.rb index e93a32aa..181306fc 100644 --- a/app/models/logic_variable.rb +++ b/app/models/logic_variable.rb @@ -1,4 +1,4 @@ -class LogicVariable < ActiveRecord::Base +class LogicVariable < ApplicationRecord VARIABLE_REGEX = /\A[_a-zA-Z]{1}\w*\z/ @@ -14,7 +14,6 @@ class LogicVariable < ActiveRecord::Base has_many :logic_variable_values, dependent: :destroy - validates :logic, presence: true validates :variable, presence: true, uniqueness: { scope: :logic_id }, format: { with: VARIABLE_REGEX }, exclusion: { in: RESERVED_WORDS } diff --git a/app/models/logic_variable_value.rb b/app/models/logic_variable_value.rb index 0ca85403..52856cfc 100644 --- a/app/models/logic_variable_value.rb +++ b/app/models/logic_variable_value.rb @@ -1,8 +1,7 @@ -class LogicVariableValue < ActiveRecord::Base +class LogicVariableValue < ApplicationRecord belongs_to :logic_variable - validates :logic_variable, presence: true validates :seed, presence: true, uniqueness: { scope: :logic_variable_id } validates :value, presence: true diff --git a/app/models/publication.rb b/app/models/publication.rb index 26eb3d9f..3c4dbfe5 100644 --- a/app/models/publication.rb +++ b/app/models/publication.rb @@ -1,8 +1,8 @@ -class Publication < ActiveRecord::Base +class Publication < ApplicationRecord belongs_to :publication_group, inverse_of: :publications belongs_to :publishable, polymorphic: true, inverse_of: :publication, touch: true - belongs_to :license + belongs_to :license, optional: true sortable_has_many :authors, dependent: :destroy, inverse_of: :publication sortable_has_many :copyright_holders, dependent: :destroy, inverse_of: :publication @@ -17,7 +17,6 @@ class Publication < ActiveRecord::Base delegate :group_uuid, :number, :nickname, :nickname=, to: :publication_group - validates :publication_group, :publishable, presence: true validates :publishable_id, uniqueness: { scope: :publishable_type } validates :uuid, presence: true, uniqueness: true validates :version, presence: true, uniqueness: { scope: :publication_group_id }, @@ -39,20 +38,23 @@ class Publication < ActiveRecord::Base scope :with_id, ->(id) do nn, vv = id.to_s.split('@') - joins(:publication_group).where do - wheres = (publication_group.uuid == nn) | (publication_group.number == nn) + pub = Publication.arel_table + pubg = PublicationGroup.arel_table - case vv + wheres = pubg[:uuid].eq(nn).or(pubg[:number].eq(nn)) + case vv when NilClass - (wheres | (uuid == nn)) & (published_at != nil) + wheres = wheres.or(pub[:uuid].eq(nn)).and(pub[:published_at].not_eq(nil)) when 'draft', 'd' - wheres & (published_at == nil) + wheres = wheres.and(pub[:published_at].eq(nil)) when 'latest' wheres else - wheres & (version == vv) - end - end.order {[publication_group.number.asc, version.desc]} + wheres = wheres.and(pub[:version].eq(vv)) + end + + joins(publication: :publication_group).where(wheres + ).order( [pubg[:number].asc, pub[:version].desc] ) end scope :visible_for, ->(options) do @@ -64,22 +66,33 @@ class Publication < ActiveRecord::Base next all if user.administrator user_id = user.id - joins do - [ - authors, - copyright_holders, - publication_group.list_publication_groups.outer.list.outer.list_owners, - publication_group.list_publication_groups.outer.list.outer.list_editors, - publication_group.list_publication_groups.outer.list.outer.list_readers - ].map(&:outer) - end.where do - (published_at != nil ) | - (authors.user_id == user_id ) | - (copyright_holders.user_id == user_id ) | - ((list_owners.owner_id == user_id) & (list_owners.owner_type == 'User')) | - ((list_editors.editor_id == user_id) & (list_editors.editor_type == 'User')) | - ((list_readers.reader_id == user_id) & (list_readers.reader_type == 'User')) - end + pub = Publication.arel_table + au = Author.arel_table + cw = CopyrightHolder.arel_table + pubg = PublicationGroup.arel_table + lpg = ListPublicationGroup.arel_table + l_own = ListOwner.arel_table + l_edit = ListEditor.arel_table + l_read = ListReader.arel_table + + me = self.arel_table + + joins(me.join(pub).on(pub[:publishable_id].eq(me[:id]), pub[:publishable_type].eq(self.name)) + .join(au).on(au[:publication_id].eq(pub[:id])) + .join(cw).on(cw[:publication_id].eq(pub[:id])) + .join(pubg).on(pub[:publication_group_id].eq(pubg[:id])) + .outer_join(lpg).on(lpg[:publication_group_id].eq(pubg[:id])) + .outer_join(l_own).on(l_own[:list_id].eq(lpg[:id])) + .outer_join(l_edit).on(l_edit[:list_id].eq(lpg[:id])) + .outer_join(l_read).on(l_read[:list_id].eq(lpg[:id])).join_sources + ).where( + pub[:published_at].not_eq(nil) + .or(au[:user_id].eq(user_id)) + .or(cw[:user_id].eq(user_id)) + .or(l_own[:owner_id].eq(user_id).and(l_own[:owner_type].eq('User'))) + .or(l_edit[:editor_id].eq(user_id).and(l_edit[:editor_type].eq('User'))) + .or(l_read[:reader_id].eq(user_id).and(l_read[:reader_type].eq('User')))) + end # By default, returns both the latest published version and the latest draft, if any @@ -206,7 +219,7 @@ def valid_license return if license.nil? || license.valid_for?(publishable_type) errors.add(:license, "is invalid for #{publishable_type}") - false + throw(:abort) end def valid_publication_group @@ -219,20 +232,23 @@ def valid_publication_group errors.add(:publication_group, "is invalid for #{publishable_type}") \ if publication_group.publishable_type != publishable_type publication_group.errors.each { |attribute, error| errors.add attribute, error } - false + throw(:abort) end def before_publication return if published_at.nil? || publishable.nil? - publishable.before_publication + catch(:abort) do + publishable.before_publication + end + return if publishable.errors.empty? publishable.errors.full_messages.each do |message| errors.add(publishable_type.underscore.to_sym, message) end - false + throw(:abort) end def after_publication @@ -251,7 +267,7 @@ def after_publication errors.add(publishable_type.underscore.to_sym, message) end - false + throw(:abort) end end diff --git a/app/models/publication_group.rb b/app/models/publication_group.rb index e3eb0149..2d21b792 100644 --- a/app/models/publication_group.rb +++ b/app/models/publication_group.rb @@ -1,4 +1,4 @@ -class PublicationGroup < ActiveRecord::Base +class PublicationGroup < ApplicationRecord has_many :publications, dependent: :destroy, inverse_of: :publication_group, autosave: true diff --git a/app/models/question.rb b/app/models/question.rb index 3580a73f..de79ee4a 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -1,4 +1,4 @@ -class Question < ActiveRecord::Base +class Question < ApplicationRecord attr_accessor :temp_id @@ -8,7 +8,7 @@ class Question < ActiveRecord::Base has_many :stems, dependent: :destroy - sortable_has_many :answers, dependent: :destroy, inverse_of: :question + sortable_has_many :answers, dependent: :destroy, inverse_of: :question, autosave: true has_many :collaborator_solutions, dependent: :destroy has_many :community_solutions, dependent: :destroy @@ -23,6 +23,4 @@ class Question < ActiveRecord::Base foreign_key: :parent_question_id, dependent: :destroy, inverse_of: :parent_question - validates :exercise, presence: true - end diff --git a/app/models/question_dependency.rb b/app/models/question_dependency.rb index 732698a1..7755176f 100644 --- a/app/models/question_dependency.rb +++ b/app/models/question_dependency.rb @@ -1,13 +1,11 @@ -class QuestionDependency < ActiveRecord::Base +class QuestionDependency < ApplicationRecord belongs_to :parent_question, class_name: 'Question', inverse_of: :child_dependencies belongs_to :dependent_question, class_name: 'Question', inverse_of: :parent_dependencies - validates :parent_question, presence: true - validates :dependent_question, presence: true, - uniqueness: { scope: :parent_question_id } + validates :dependent_question, uniqueness: { scope: :parent_question_id } validate :same_exercise diff --git a/app/models/stem.rb b/app/models/stem.rb index b9ae8905..722eec8c 100644 --- a/app/models/stem.rb +++ b/app/models/stem.rb @@ -1,4 +1,4 @@ -class Stem < ActiveRecord::Base +class Stem < ApplicationRecord user_html :content stylable @@ -12,7 +12,6 @@ class Stem < ActiveRecord::Base has_many :combo_choices, dependent: :destroy validates :stylings, presence: true - validates :question, presence: true validates :content, presence: true end diff --git a/app/models/stem_answer.rb b/app/models/stem_answer.rb index 58c755fe..bcf0e5f9 100644 --- a/app/models/stem_answer.rb +++ b/app/models/stem_answer.rb @@ -1,10 +1,9 @@ -class StemAnswer < ActiveRecord::Base +class StemAnswer < ApplicationRecord belongs_to :stem belongs_to :answer - validates :stem, presence: true - validates :answer, presence: true, uniqueness: { scope: :stem_id } + validates :answer, uniqueness: { scope: :stem_id } validates :correctness, presence: true, numericality: { greater_than_or_equal_to: 0.0, less_than_or_equal_to: 1.0 } diff --git a/app/models/styling.rb b/app/models/styling.rb index ff947d81..3a3dae1c 100644 --- a/app/models/styling.rb +++ b/app/models/styling.rb @@ -1,8 +1,7 @@ -class Styling < ActiveRecord::Base +class Styling < ApplicationRecord belongs_to :stylable, polymorphic: true, inverse_of: :stylings - validates :stylable, presence: true validates :style, presence: true, inclusion: { in: Style.all }, uniqueness: { scope: [:stylable_type, :stylable_id] } diff --git a/app/models/tag.rb b/app/models/tag.rb index 1fdf3964..ab70975b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,4 +1,4 @@ -class Tag < ActiveRecord::Base +class Tag < ApplicationRecord has_many :exercise_tags, dependent: :destroy has_many :vocab_term_tags, dependent: :destroy diff --git a/app/models/trusted_application.rb b/app/models/trusted_application.rb index 7934d137..6c3a1085 100644 --- a/app/models/trusted_application.rb +++ b/app/models/trusted_application.rb @@ -1,8 +1,8 @@ -class TrustedApplication < ActiveRecord::Base +class TrustedApplication < ApplicationRecord belongs_to :application, class_name: 'Doorkeeper::Application', inverse_of: :trusted_application - validates :application, presence: true, uniqueness: true + validates :application, uniqueness: true end diff --git a/app/models/user.rb b/app/models/user.rb index dd3d8122..b7406c6a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,4 @@ -class User < ActiveRecord::Base +class User < ApplicationRecord USERNAME_FORBIDDEN_CHAR_REGEX = /[^\w-]/ @@ -33,7 +33,7 @@ class User < ActiveRecord::Base has_many :sortings, dependent: :destroy - validates :account, presence: true, uniqueness: true + validates :account, uniqueness: true delegate :username, :first_name, :last_name, :full_name, :title, :name, :casual_name, :first_name=, :last_name=, :full_name=, diff --git a/app/models/vocab_distractor.rb b/app/models/vocab_distractor.rb index 51789ba9..4a6cb696 100644 --- a/app/models/vocab_distractor.rb +++ b/app/models/vocab_distractor.rb @@ -1,10 +1,9 @@ -class VocabDistractor < ActiveRecord::Base +class VocabDistractor < ApplicationRecord belongs_to :vocab_term, inverse_of: :vocab_distractors belongs_to :distractor_publication_group, class_name: 'PublicationGroup', inverse_of: :vocab_distractors - validates :vocab_term, presence: true - validates :distractor_publication_group, presence: true, uniqueness: { scope: :vocab_term_id } + validates :distractor_publication_group, uniqueness: { scope: :vocab_term_id } validates :distractor_term, presence: true validate :vocab_term_publication_group, :different_terms diff --git a/app/models/vocab_term.rb b/app/models/vocab_term.rb index 0ef599a6..22656d72 100644 --- a/app/models/vocab_term.rb +++ b/app/models/vocab_term.rb @@ -1,4 +1,4 @@ -class VocabTerm < ActiveRecord::Base +class VocabTerm < ApplicationRecord EQUALITY_ASSOCIATIONS = [ :tags, @@ -51,7 +51,7 @@ class VocabTerm < ActiveRecord::Base has_tags - has_many :vocab_distractors, dependent: :destroy + has_many :vocab_distractors, dependent: :destroy, autosave: true has_many :exercises, dependent: :destroy, autosave: true @@ -111,21 +111,20 @@ def before_publication errors.add(:base, 'must have at least 1 distractor') \ if distractor_literals.empty? && vocab_distractors.empty? - return false if errors.any? + throw(:abort) if errors.any? # Publish exercises latest_exercises.each do |exercise| exercise.publication.update_attribute :published_at, published_at end - true end def after_publication last_def = VocabTerm.joins(publication: :publication_group) .where(publication: {publication_group: {number: number}}) - .where {id != my{id}} - .order {publication.version.desc} + .where.not(id: id) + .order(Publication.arel_table[:version].desc) .limit(1) .pluck(:definition) return if definition == last_def diff --git a/app/models/vocab_term_tag.rb b/app/models/vocab_term_tag.rb index 58f9cfab..b0071f47 100644 --- a/app/models/vocab_term_tag.rb +++ b/app/models/vocab_term_tag.rb @@ -1,7 +1,6 @@ -class VocabTermTag < ActiveRecord::Base +class VocabTermTag < ApplicationRecord belongs_to :vocab_term belongs_to :tag - validates :vocab_term, presence: true - validates :tag, presence: true, uniqueness: { scope: :vocab_term_id } + validates :tag, uniqueness: { scope: :vocab_term_id } end diff --git a/app/representers/api/v1/exercises/representer.rb b/app/representers/api/v1/exercises/representer.rb index 61305cf4..20e3ceb3 100644 --- a/app/representers/api/v1/exercises/representer.rb +++ b/app/representers/api/v1/exercises/representer.rb @@ -67,7 +67,7 @@ def self.cache_key_types_for(represented, options = {}) end def self.cache_key_for(represented, type) - "#{represented.cache_key}/#{type}" + "#{represented.cache_key}/#{represented.cache_version}/#{type}" end def self.all_cache_keys_for(represented, options = {}) diff --git a/app/representers/api/v1/vocabs/term_with_distractors_and_exercise_ids_representer.rb b/app/representers/api/v1/vocabs/term_with_distractors_and_exercise_ids_representer.rb index 668946c8..bbbdcc03 100644 --- a/app/representers/api/v1/vocabs/term_with_distractors_and_exercise_ids_representer.rb +++ b/app/representers/api/v1/vocabs/term_with_distractors_and_exercise_ids_representer.rb @@ -53,7 +53,7 @@ def self.cache_key_types_for(represented, options = {}) end def self.cache_key_for(represented, type) - "#{represented.cache_key}/#{type}" + "#{represented.cache_key}/#{represented.cache_version}/#{type}" end def self.all_cache_keys_for(represented, options = {}) diff --git a/app/routines/exercises/import/old/row_importer.rb b/app/routines/exercises/import/old/row_importer.rb index 89731324..b9c4d675 100644 --- a/app/routines/exercises/import/old/row_importer.rb +++ b/app/routines/exercises/import/old/row_importer.rb @@ -17,7 +17,7 @@ def parse(text, exercise) return nil if text.blank? text = text.to_s - kd = Kramdown::Document.new(text.to_s.strip, attachable: exercise) + kd = Kramdown::Document.new(text.to_s.strip, math_engine: :openstax, attachable: exercise) # If only one
tag, remove it and just return the nodes below kd.root.children = kd.root.children.first.children \ if kd.root.children.length == 1 && kd.root.children.first.type == :p @@ -69,10 +69,10 @@ def perform_row_import(row, index) dok_tag, time_tag, display_type_tags, blooms_tag].flatten.uniq ex.tags = tags - latest_exercise = Exercise - .joins([{publication: :publication_group}, {exercise_tags: :tag}]) - .where(exercise_tags: {tag: {name: exercise_id_tag}}) - .order {[publication.publication_group.number.desc, publication.version.desc]}.first + pub = Publication.arel_table + pubg = PublicationGroup.arel_table + + latest_exercise = Exercise.joins([{publication: :publication_group}, {exercise_tags: :tag}]).where(exercise_tags: {tag: {name: exercise_id_tag}}).order([pubg[:number].desc, pub[:version].desc]).first unless latest_exercise.nil? ex.publication.publication_group = latest_exercise.publication.publication_group diff --git a/app/routines/exercises/import/quadbase.rb b/app/routines/exercises/import/quadbase.rb index a9e543f3..3427fb58 100644 --- a/app/routines/exercises/import/quadbase.rb +++ b/app/routines/exercises/import/quadbase.rb @@ -36,10 +36,12 @@ def import_question(hash) end id_tag = "exid:qb:#{hash['id']}" + pub = Publication.arel_table + pubg = PublicationGroup.arel_table latest_exercise = Exercise .joins([{publication: :publication_group}, {exercise_tags: :tag}]) .where(exercise_tags: {tag: {name: id_tag}}) - .order {[publication.publication_group.number.desc, publication.version.desc]}.first + .order(pubg[:number].desc, pub[:version].desc).first publication = import_metadata(exercise.publication, hash['attribution']) diff --git a/app/routines/search_exercises.rb b/app/routines/search_exercises.rb index a61c689d..f76e82b4 100644 --- a/app/routines/search_exercises.rb +++ b/app/routines/search_exercises.rb @@ -32,9 +32,20 @@ def exec(params = {}, options = {}) # this "latest_visible" condition is disabled. latest_visible = true + ex = Exercise.arel_table + qu = Question.arel_table + st = Stem.arel_table + ans = Answer.arel_table + pub = Publication.arel_table + pubg = PublicationGroup.arel_table + acct = OpenStax::Accounts::Account.arel_table + # NB encapsulates magic knowledge of how ActiveRecord will alias second join + acct_author = OpenStax::Accounts::Account.arel_table + acct_copyright = OpenStax::Accounts::Account.arel_table.alias('accounts_users') + run(:search, relation: relation, sortable_fields: SORTABLE_FIELDS, params: params) do |with| with.default_keyword :content - + with.keyword :id do |ids| ids.each do |id| sanitized_ids = to_number_array(id) @@ -61,25 +72,22 @@ def exec(params = {}, options = {}) elsif sanitized_versions.empty? @items = @items.where(publication_groups: { number: sanitized_numbers }) else - # Combine the id's one at a time using Squeel - @items = @items.where do - only_numbers = sanitized_uids.select { |suid| suid.second.blank? }.map(&:first) - only_versions = sanitized_uids.select { |suid| suid.first.blank? }.map(&:second) - full_uids = sanitized_uids.reject { |suid| suid.first.blank? || suid.second.blank? } - - cumulative_query = publication.publication_group.number.in(only_numbers) | - publication.version.in(only_versions) - - full_uids.each do |full_uid| - sanitized_number = full_uid.first - sanitized_version = full_uid.second - query = (publication.publication_group.number == sanitized_number) & - (publication.version == sanitized_version) - cumulative_query = cumulative_query | query + only_numbers = sanitized_uids.select { |suid| suid.second.blank? }.map(&:first) + only_versions = sanitized_uids.select { |suid| suid.first.blank? }.map(&:second) + full_uids = sanitized_uids.reject { |suid| suid.first.blank? || suid.second.blank? } + + cumulative_query = pubg[:number].in(only_numbers).or( + pub[:version].in(only_versions)) + + full_uids.each do |full_uid| + sanitized_number = full_uid.first + sanitized_version = full_uid.second + query = pubg[:number].eq(sanitized_number).and( + pub[:version].eq(sanitized_version)) + cumulative_query = cumulative_query.or(query) end - cumulative_query - end + @items = @items.where(cumulative_query) end end end @@ -149,7 +157,7 @@ def exec(params = {}, options = {}) sanitized_titles = to_string_array(ttl, append_wildcard: true, prepend_wildcard: true) next @items = @items.none if sanitized_titles.empty? - @items = @items.where { title.like_any sanitized_titles } + @items = @items.where(ex[:title].matches_any(sanitized_titles)) end end @@ -159,14 +167,12 @@ def exec(params = {}, options = {}) prepend_wildcard: true) next @items = @items.none if sanitized_contents.empty? - @items = @items.joins {[questions.outer.stems.outer, questions.outer.answers.outer]} - .where do - title.like_any(sanitized_contents) | - stimulus.like_any(sanitized_contents) | - questions.stimulus.like_any(sanitized_contents) | - questions.stems.content.like_any(sanitized_contents) | - questions.answers.content.like_any(sanitized_contents) - end + @items = @items.left_joins(questions: [:stems, :answers]).where( + ex[:title].matches_any(sanitized_contents) + .or(ex[:stimulus].matches_any(sanitized_contents)) + .or(qu[:stimulus].matches_any(sanitized_contents)) + .or(st[:content].matches_any(sanitized_contents)) + .or(ans[:content].matches_any(sanitized_contents))) end end @@ -175,12 +181,11 @@ def exec(params = {}, options = {}) sn = to_string_array(name, append_wildcard: true) next @items = @items.none if sn.empty? - @items = @items.joins(publication: { authors: { user: :account } }).where do - openstax_accounts_accounts.username.like_any(sn) | - openstax_accounts_accounts.first_name.like_any(sn) | - openstax_accounts_accounts.last_name.like_any(sn) | - openstax_accounts_accounts.full_name.like_any(sn) - end + @items = @items.joins(publication: { authors: { user: :account } }).where( + acct[:username].matches_any(sn) + .or(acct[:first_name].matches_any(sn)) + .or(acct[:last_name].matches_any(sn)) + .or(acct[:full_name].matches_any(sn))) end end @@ -189,12 +194,11 @@ def exec(params = {}, options = {}) sn = to_string_array(name, append_wildcard: true) next @items = @items.none if sn.empty? - @items = @items.joins(publication: { copyright_holders: { user: :account } }).where do - openstax_accounts_accounts.username.like_any(sn) | - openstax_accounts_accounts.first_name.like_any(sn) | - openstax_accounts_accounts.last_name.like_any(sn) | - openstax_accounts_accounts.full_name.like_any(sn) - end + @items = @items.joins(publication: { copyright_holders: { user: :account } }).where( + acct[:username].matches_any(sn) + .or(acct[:first_name].matches_any(sn)) + .or(acct[:last_name].matches_any(sn)) + .or(acct[:full_name].matches_any(sn))) end end @@ -203,38 +207,33 @@ def exec(params = {}, options = {}) sn = to_string_array(name, append_wildcard: true) next @items = @items.none if sn.empty? - @items = @items.joins {publication.authors.outer.user.outer.account.outer} - .joins {publication.copyright_holders.outer.user.outer.account.outer} - .where do - publication.authors.user.account.username.like_any(sn) | - publication.authors.user.account.first_name.like_any(sn) | - publication.authors.user.account.last_name.like_any(sn) | - publication.authors.user.account.full_name.like_any(sn) | - publication.copyright_holders.user.account.username.like_any(sn) | - publication.copyright_holders.user.account.first_name.like_any(sn) | - publication.copyright_holders.user.account.last_name.like_any(sn) | - publication.copyright_holders.user.account.full_name.like_any(sn) - end + @items = @items.joins(publication: { authors: { user: :account } }) + .joins(publication: { copyright_holders: { user: :account } }).where( + acct_author[:username].matches_any(sn) + .or(acct_author[:first_name].matches_any(sn)) + .or(acct_author[:last_name].matches_any(sn)) + .or(acct_author[:full_name].matches_any(sn)) + .or(acct_copyright[:username].matches_any(sn)) + .or(acct_copyright[:first_name].matches_any(sn)) + .or(acct_copyright[:last_name].matches_any(sn)) + .or(acct_copyright[:full_name].matches_any(sn))) end end end - pg = PublicationGroup.arel_table - pb = Publication.arel_table - outputs.items = outputs.items.select( [ - Exercise.arel_table[ Arel.star ], - pg[:uuid], - pg[:number], - pb[:version], - pb[:published_at] + ex[ Arel.star ], + pubg[:uuid], + pubg[:number], + pub[:version], + pub[:published_at] ] ).distinct return unless latest_visible outputs.items = outputs.items.chainable_latest - outputs.total_count = outputs.items.limit(nil).offset(nil).reorder(nil).count + outputs.total_count = outputs.items.limit(nil).offset(nil).reorder(nil).count(:all) end end diff --git a/app/routines/search_vocab_terms.rb b/app/routines/search_vocab_terms.rb index ae9449b4..60907d6b 100644 --- a/app/routines/search_vocab_terms.rb +++ b/app/routines/search_vocab_terms.rb @@ -33,14 +33,24 @@ def exec(params = {}, options = {}) # this "latest_visible" condition is disabled. latest_visible = true + vt = VocabTerm.arel_table + pubg = PublicationGroup.arel_table + pub = Publication.arel_table + acct = OpenStax::Accounts::Account.arel_table + # NB: this encapsulates magic knowledge of how ActiveRecord will alias the second join of accounts + acct_author = OpenStax::Accounts::Account.arel_table + acct_copyright = OpenStax::Accounts::Account.arel_table.alias('accounts_users') + + run(:search, relation: relation, sortable_fields: SORTABLE_FIELDS, params: params) do |with| # Block to be used for searches by name or term + name_search_block = lambda do |names| names.each do |nm| sanitized_names = to_string_array(nm, append_wildcard: true, prepend_wildcard: true) next @items = @items.none if sanitized_names.empty? - @items = @items.where { name.like_any sanitized_names } + @items = @items.where( vt[:name].matches_any(sanitized_names )) end end @@ -72,25 +82,22 @@ def exec(params = {}, options = {}) elsif sanitized_versions.empty? @items = @items.where(publication_groups: { number: sanitized_numbers }) else - # Combine the id's one at a time using Squeel - @items = @items.where do - only_numbers = sanitized_uids.select { |suid| suid.second.blank? }.map(&:first) - only_versions = sanitized_uids.select { |suid| suid.first.blank? }.map(&:second) - full_uids = sanitized_uids.reject { |suid| suid.first.blank? || suid.second.blank? } - - cumulative_query = publication.publication_group.number.in(only_numbers) | - publication.version.in(only_versions) - - full_uids.each do |full_uid| - sanitized_number = full_uid.first - sanitized_version = full_uid.second - query = (publication.publication_group.number == sanitized_number) & - (publication.version == sanitized_version) - cumulative_query = cumulative_query | query + only_numbers = sanitized_uids.select { |suid| suid.second.blank? }.map(&:first) + only_versions = sanitized_uids.select { |suid| suid.first.blank? }.map(&:second) + full_uids = sanitized_uids.reject { |suid| suid.first.blank? || suid.second.blank? } + + cumulative_query = pubg[:number].in(only_numbers).or( + pub[:version].in(only_versions)) + + full_uids.each do |full_uid| + sanitized_number = full_uid.first + sanitized_version = full_uid.second + query = pubg[:number].eq(sanitized_number).and( + pub[:version].eq(sanitized_version)) + cumulative_query = cumulative_query.or(query) end - cumulative_query - end + @items = @items.where(cumulative_query) end end end @@ -164,7 +171,7 @@ def exec(params = {}, options = {}) sanitized_definitions = to_string_array(df, append_wildcard: true, prepend_wildcard: true) next @items = @items.none if sanitized_definitions.empty? - @items = @items.where { definition.like_any sanitized_definitions } + @items = @items.where( vt[:definition].matches_any(sanitized_definitions) ) end end @@ -174,9 +181,8 @@ def exec(params = {}, options = {}) prepend_wildcard: true) next @items = @items.none if sanitized_contents.empty? - @items = @items.where do - name.like_any(sanitized_contents) | definition.like_any(sanitized_contents) - end + @items = @items.where(vt[:name].matches_any(sanitized_contents).or( + vt[:definition].matches_any(sanitized_contents))) end end @@ -185,12 +191,11 @@ def exec(params = {}, options = {}) sn = to_string_array(name, append_wildcard: true) next @items = @items.none if sn.empty? - @items = @items.joins(publication: { authors: { user: :account } }).where do - openstax_accounts_accounts.username.like_any(sn) | - openstax_accounts_accounts.first_name.like_any(sn) | - openstax_accounts_accounts.last_name.like_any(sn) | - openstax_accounts_accounts.full_name.like_any(sn) - end + @items = @items.joins(publication: { authors: { user: :account } }).where( + acct[:username].matches_any(sn) + .or(acct[:first_name].matches_any(sn)) + .or(acct[:last_name].matches_any(sn)) + .or(acct[:full_name].matches_any(sn))) end end @@ -199,12 +204,11 @@ def exec(params = {}, options = {}) sn = to_string_array(name, append_wildcard: true) next @items = @items.none if sn.empty? - @items = @items.joins(publication: { copyright_holders: { user: :account } }).where do - openstax_accounts_accounts.username.like_any(sn) | - openstax_accounts_accounts.first_name.like_any(sn) | - openstax_accounts_accounts.last_name.like_any(sn) | - openstax_accounts_accounts.full_name.like_any(sn) - end + @items = @items.joins(publication: { copyright_holders: { user: :account } }).where( + acct[:username].matches_any(sn) + .or(acct[:first_name].matches_any(sn)) + .or(acct[:last_name].matches_any(sn)) + .or(acct[:full_name].matches_any(sn))) end end @@ -213,38 +217,33 @@ def exec(params = {}, options = {}) sn = to_string_array(name, append_wildcard: true) next @items = @items.none if sn.empty? - @items = @items.joins {publication.authors.outer.user.outer.account.outer} - .joins {publication.copyright_holders.outer.user.outer.account.outer} - .where do - publication.authors.user.account.username.like_any(sn) | - publication.authors.user.account.first_name.like_any(sn) | - publication.authors.user.account.last_name.like_any(sn) | - publication.authors.user.account.full_name.like_any(sn) | - publication.copyright_holders.user.account.username.like_any(sn) | - publication.copyright_holders.user.account.first_name.like_any(sn) | - publication.copyright_holders.user.account.last_name.like_any(sn) | - publication.copyright_holders.user.account.full_name.like_any(sn) - end + @items = @items.joins(publication: { authors: { user: :account } }) + .joins(publication: { copyright_holders: { user: :account } }).where( + acct_author[:username].matches_any(sn) + .or(acct_author[:first_name].matches_any(sn)) + .or(acct_author[:last_name].matches_any(sn)) + .or(acct_author[:full_name].matches_any(sn)) + .or(acct_copyright[:username].matches_any(sn)) + .or(acct_copyright[:first_name].matches_any(sn)) + .or(acct_copyright[:last_name].matches_any(sn)) + .or(acct_copyright[:full_name].matches_any(sn))) end end end - pg = PublicationGroup.arel_table - pb = Publication.arel_table - outputs.items = outputs.items.select( [ - VocabTerm.arel_table[ Arel.star ], - pg[:uuid], - pg[:number], - pb[:version], - pb[:published_at] + vt[ Arel.star ], + pubg[:uuid], + pubg[:number], + pub[:version], + pub[:published_at] ] ).distinct return unless latest_visible outputs.items = outputs.items.chainable_latest - outputs.total_count = outputs.items.limit(nil).offset(nil).reorder(nil).count + outputs.total_count = outputs.items.limit(nil).offset(nil).reorder(nil).count(:all) end end diff --git a/app/views/admin/console/_misc.html.erb b/app/views/admin/console/_misc.html.erb index 02bb22b4..1b036575 100644 --- a/app/views/admin/console/_misc.html.erb +++ b/app/views/admin/console/_misc.html.erb @@ -7,8 +7,6 @@
tag, remove it and just return the nodes below kd.root.children = kd.root.children.first.children \ if kd.root.children.length == 1 && kd.root.children.first.type == :p @@ -51,10 +51,14 @@ def import_row(row, row_number) ex.tags = book_tags + lo_tags + type_tags + \ [id_tag, cnxmod_tag, dok_tag, blooms_tag, time_tag] + pub = Publication.arel_table + pubg = PublicationGroup.arel_table + latest_exercise = Exercise - .joins([{publication: :publication_group}, {exercise_tags: :tag}]) - .where(exercise_tags: {tag: {name: id_tag}}) - .order {[publication.publication_group.number.desc, publication.version.desc]}.first + .joins([{publication: :publication_group}, {exercise_tags: :tag}]) + .where(exercise_tags: {tag: {name: id_tag}}) + .order([pubg[:number].desc, pub[:version].desc]).first + unless latest_exercise.nil? ex.publication.publication_group = latest_exercise.publication.publication_group diff --git a/lib/publishable/active_record.rb b/lib/publishable/active_record.rb index 5a400ee6..af2abf54 100644 --- a/lib/publishable/active_record.rb +++ b/lib/publishable/active_record.rb @@ -20,29 +20,31 @@ def publishable(options = {}) to: :publication scope :published, -> do - joins(:publication).where.not(publications: { published_at: nil }) + joins(:publication).where.not(publication: { published_at: nil }) end - scope :unpublished, -> { joins(:publication).where(publications: { published_at: nil }) } + scope :unpublished, -> { joins(:publication).where(publication: { published_at: nil }) } scope :with_id, ->(id) do nn, vv = id.to_s.split('@') - joins(publication: :publication_group).where do - wheres = (publication.publication_group.uuid == nn) | - (publication.publication_group.number == nn) + pub = Publication.arel_table + pubg = PublicationGroup.arel_table - case vv + wheres = pubg[:uuid].eq(nn).or(pubg[:number].eq(nn)) + case vv when NilClass - (wheres | (publication.uuid == nn)) & (publication.published_at != nil) + wheres = wheres.or(pub[:uuid].eq(nn)).and(pub[:published_at].not_eq(nil)) when 'draft', 'd' - wheres & (publication.published_at == nil) + wheres = wheres.and(pub[:published_at].eq(nil)) when 'latest' wheres else - wheres & (publication.version == vv) - end - end.order { [publication.publication_group.number.asc, publication.version.desc] } + wheres = wheres.and(pub[:version].eq(vv)) + end + + joins(publication: :publication_group).where(wheres + ).order( [pubg[:number].asc, pub[:version].desc] ) end scope :visible_for, ->(options) do @@ -54,22 +56,33 @@ def publishable(options = {}) next all if user.administrator user_id = user.id - joins do - [ - publication.authors, - publication.copyright_holders, - publication.publication_group.list_publication_groups.outer.list.outer.list_owners, - publication.publication_group.list_publication_groups.outer.list.outer.list_editors, - publication.publication_group.list_publication_groups.outer.list.outer.list_readers - ].map(&:outer) - end.where do - (publication.published_at != nil ) | - (authors.user_id == user_id ) | - (copyright_holders.user_id == user_id ) | - ((list_owners.owner_id == user_id) & (list_owners.owner_type == 'User')) | - ((list_editors.editor_id == user_id) & (list_editors.editor_type == 'User')) | - ((list_readers.reader_id == user_id) & (list_readers.reader_type == 'User')) - end + pub = Publication.arel_table + au = Author.arel_table + cw = CopyrightHolder.arel_table + pubg = PublicationGroup.arel_table + lpg = ListPublicationGroup.arel_table + l_own = ListOwner.arel_table + l_edit = ListEditor.arel_table + l_read = ListReader.arel_table + + me = self.arel_table + + joins(me.join(pub).on(pub[:publishable_id].eq(me[:id]), pub[:publishable_type].eq(self.name)) + .join(au).on(au[:publication_id].eq(pub[:id])) + .join(cw).on(cw[:publication_id].eq(pub[:id])) + .join(pubg).on(pub[:publication_group_id].eq(pubg[:id])) + .outer_join(lpg).on(lpg[:publication_group_id].eq(pubg[:id])) + .outer_join(l_own).on(l_own[:list_id].eq(lpg[:id])) + .outer_join(l_edit).on(l_edit[:list_id].eq(lpg[:id])) + .outer_join(l_read).on(l_read[:list_id].eq(lpg[:id])).join_sources + ).where( + pub[:published_at].not_eq(nil) + .or(au[:user_id].eq(user_id)) + .or(cw[:user_id].eq(user_id)) + .or(l_own[:owner_id].eq(user_id).and(l_own[:owner_type].eq('User'))) + .or(l_edit[:editor_id].eq(user_id).and(l_edit[:editor_type].eq('User'))) + .or(l_read[:reader_id].eq(user_id).and(l_read[:reader_type].eq('User')))) + end # By default, returns both the latest published version and the latest draft, if any diff --git a/lib/tasks/vocab_terms/unlink.rake b/lib/tasks/vocab_terms/unlink.rake index ad787cfa..2acdef66 100644 --- a/lib/tasks/vocab_terms/unlink.rake +++ b/lib/tasks/vocab_terms/unlink.rake @@ -1,7 +1,7 @@ namespace :vocab_terms do desc 'unlinks all vocab terms that are currently linked to other terms' task unlink: :environment do - VocabTerm.joins(:vocab_distractors).latest.uniq.find_in_batches do |vocab_terms| + VocabTerm.joins(:vocab_distractors).latest.distinct.find_in_batches do |vocab_terms| VocabTerm.transaction do vocab_terms.each do |vocab_term| vocab_term = vocab_term.new_version if vocab_term.is_published? diff --git a/lib/user_html.rb b/lib/user_html.rb index 54e6572a..5768cd89 100644 --- a/lib/user_html.rb +++ b/lib/user_html.rb @@ -10,7 +10,7 @@ module ActiveRecord module Base def user_html(*attributes) attributes.each do |attribute| - filter_name = "link_and_sanitize_#{attribute.to_s}" + filter_name = :"link_and_sanitize_#{attribute.to_s}" class_exec do before_validation filter_name diff --git a/lib/vocab_terms/importer.rb b/lib/vocab_terms/importer.rb index cb009e52..39a1f323 100644 --- a/lib/vocab_terms/importer.rb +++ b/lib/vocab_terms/importer.rb @@ -54,11 +54,10 @@ def import_row(row, row_number) @latest_term_map ||= Hash.new do |hash, chapter| hash[chapter] = Hash.new do |hash, name| lo_like = "lo:stax-#{book}:#{chapter}-%" - hash[name] = VocabTerm - .joins([{publication: :publication_group}, {vocab_term_tags: :tag}]) - .where(name: name) - .where {vocab_term_tags.tag.name.like lo_like} - .order {[publication.publication_group.number.desc, publication.version.desc]}.first + ta = Tag.arel_table + pub = Publication.arel_table + pubg = PublicationGroup.arel_table + hash[name] = VocabTerm.joins([{publication: :publication_group}, {vocab_term_tags: :tag}]).where(name: name).where(ta[:name].matches lo_like).order([pubg[:number].desc, pub[:version].desc]).first end end diff --git a/spec/access_policies/doorkeeper/application_access_policy_spec.rb b/spec/access_policies/doorkeeper/application_access_policy_spec.rb index 9ce63228..142bdf43 100644 --- a/spec/access_policies/doorkeeper/application_access_policy_spec.rb +++ b/spec/access_policies/doorkeeper/application_access_policy_spec.rb @@ -4,7 +4,7 @@ let(:anon) { AnonymousUser.instance } let(:user) { FactoryBot.create(:user) } let(:admin) { FactoryBot.create(:user, :administrator) } - let(:app) { FactoryBot.build(:doorkeeper_application) } + let(:app) { FactoryBot.create(:doorkeeper_application) } let(:another_app) { FactoryBot.create(:doorkeeper_application) } context 'read, update' do diff --git a/spec/controllers/admin/administrators_controller_spec.rb b/spec/controllers/admin/administrators_controller_spec.rb index e7b6f367..49f4e370 100644 --- a/spec/controllers/admin/administrators_controller_spec.rb +++ b/spec/controllers/admin/administrators_controller_spec.rb @@ -35,7 +35,7 @@ module Admin context 'for anonymous' do it 'redirects to the login page' do - expect{ post :create, valid_params }.not_to change{ Administrator.count } + expect{ post :create, params: valid_params }.not_to change{ Administrator.count } expect(response).to redirect_to(controller.openstax_accounts.login_path) end end @@ -43,14 +43,14 @@ module Admin context 'for non-admins' do it 'raises SecurityTransgression' do controller.sign_in user - expect{ post :create, valid_params }.to raise_error(SecurityTransgression) + expect{ post :create, params: valid_params }.to raise_error(SecurityTransgression) end end context 'for admins' do it 'makes the given user an administrator' do controller.sign_in admin - expect{ post :create, valid_params }.to change{ Administrator.count }.by(1) + expect{ post :create, params: valid_params }.to change{ Administrator.count }.by(1) expect(response).to redirect_to(admin_administrators_path) expect(user.reload.is_administrator?).to eq true end @@ -62,7 +62,7 @@ module Admin context 'for anonymous' do it 'redirects to the login page' do - expect{ delete :destroy, valid_params }.not_to change{ Administrator.count } + expect{ delete :destroy, params: valid_params }.not_to change{ Administrator.count } expect(response).to redirect_to(controller.openstax_accounts.login_path) end end @@ -70,14 +70,14 @@ module Admin context 'for non-admins' do it 'raises SecurityTransgression' do controller.sign_in user - expect{ delete :destroy, valid_params }.to raise_error(SecurityTransgression) + expect{ delete :destroy, params: valid_params }.to raise_error(SecurityTransgression) end end context 'for admins' do it 'deletes the given administrator' do controller.sign_in admin - expect{ delete :destroy, valid_params }.to change{ Administrator.count }.by(-1) + expect{ delete :destroy, params: valid_params }.to change{ Administrator.count }.by(-1) expect(response).to redirect_to(admin_administrators_path) expect(admin.reload.is_administrator?).to eq false end diff --git a/spec/controllers/admin/console_controller_spec.rb b/spec/controllers/admin/console_controller_spec.rb index 57e9cc72..388cd948 100644 --- a/spec/controllers/admin/console_controller_spec.rb +++ b/spec/controllers/admin/console_controller_spec.rb @@ -9,7 +9,7 @@ module Admin describe 'GET index' do context 'for anonymous' do it 'redirects to the login page' do - xhr :get, :index + get :index, xhr: true expect(response).to have_http_status(:forbidden) end end @@ -17,14 +17,14 @@ module Admin context 'for non-admins' do it 'raises SecurityTransgression' do controller.sign_in user - expect{ xhr :get, :index }.to raise_error(SecurityTransgression) + expect{ get :index, xhr: true }.to raise_error(SecurityTransgression) end end context 'for admins' do it 'returns http ok' do controller.sign_in admin - xhr :get, :index + get :index, xhr: true expect(response).to have_http_status(:ok) end end diff --git a/spec/controllers/admin/exceptions_controller_spec.rb b/spec/controllers/admin/exceptions_controller_spec.rb index f70204bb..dc6f31e8 100644 --- a/spec/controllers/admin/exceptions_controller_spec.rb +++ b/spec/controllers/admin/exceptions_controller_spec.rb @@ -6,7 +6,6 @@ module Admin EXCEPTIONS = [[SecurityTransgression], [ActiveRecord::RecordNotFound], [ActionController::RoutingError, '/blah/blah/blah'.inspect], - [ActionController::UnknownController], [AbstractController::ActionNotFound], [ActionView::MissingTemplate, [['a', 'b'], 'path', ['pre1', 'pre2'], @@ -21,7 +20,7 @@ module Admin context 'for anonymous' do it 'returns 403 forbidden' do EXCEPTIONS.each do |klass, args| - xhr :get, :show, id: klass.name, args: args + get :show, params: { id: klass.name, args: args }, xhr: true expect(response).to have_http_status(:forbidden) end end @@ -31,7 +30,7 @@ module Admin it 'raises SecurityTransgression' do controller.sign_in user EXCEPTIONS.each do |klass, args| - expect{ xhr :get, :show, id: klass.name, args: args }.to( + expect{ get :show, params: { id: klass.name, args: args }, xhr: true }.to( raise_error(SecurityTransgression) ) end @@ -42,7 +41,7 @@ module Admin it 'raises the given exception' do controller.sign_in admin EXCEPTIONS.each do |klass, args| - expect{ xhr :get, :show, id: klass.name, args: args }.to raise_error(klass) + expect{ get :show, params: { id: klass.name, args: args }, xhr: true }.to raise_error(klass) end end end diff --git a/spec/controllers/api/v1/attachments_controller_spec.rb b/spec/controllers/api/v1/attachments_controller_spec.rb index 3afc0fdd..24321051 100644 --- a/spec/controllers/api/v1/attachments_controller_spec.rb +++ b/spec/controllers/api/v1/attachments_controller_spec.rb @@ -28,6 +28,9 @@ module Api::V1 exercise.publication.authors << FactoryBot.build( :author, user: user, publication: exercise.publication ) + exercise.publication.copyright_holders << FactoryBot.build( + :copyright_holder, user: user, publication: exercise.publication + ) exercise.save! exercise end @@ -37,16 +40,13 @@ module Api::V1 context "POST create" do let(:image) { - image = ActionDispatch::Http::UploadedFile.new( - filename: 'test_photo_1.jpg', - type: 'image/jpeg', - tempfile: File.new("#{Rails.root}/spec/fixtures/rails.png") - ) + image = Rack::Test::UploadedFile.new("#{Rails.root}/spec/fixtures/rails.png", + 'image/jpeg') } it 'attaches a file to an exercise' do expect do - api_post :create, user_token, parameters: { exercise_id: exercise_id, file: image } + api_post :create, user_token, params: { exercise_id: exercise_id, file: image } end.to change{ exercise.attachments.count }.by(1) expect(response).to have_http_status(:success) end @@ -54,7 +54,7 @@ module Api::V1 it 'creates a draft if needed' do exercise.publication.publish.save! expect do - api_post :create, user_token, parameters: { exercise_id: exercise_id, file: image } + api_post :create, user_token, params: { exercise_id: exercise_id, file: image } end.to change{ exercise.publication_group.reload.latest_version }.from(1).to(2) expect(response).to have_http_status(:success) end @@ -68,7 +68,7 @@ module Api::V1 ).outputs[:attachment] expect do - api_delete :destroy, user_token, parameters: { + api_delete :destroy, user_token, params: { exercise_id: exercise_id, filename: attachment.read_attribute(:asset) } end.to change(Attachment, :count).by(-1) diff --git a/spec/controllers/api/v1/community_solutions_controller_spec.rb b/spec/controllers/api/v1/community_solutions_controller_spec.rb index 7f243393..cb78dd08 100644 --- a/spec/controllers/api/v1/community_solutions_controller_spec.rb +++ b/spec/controllers/api/v1/community_solutions_controller_spec.rb @@ -11,7 +11,7 @@ module Api::V1 context "GET index" do it "assigns all solutions as @solutions" do solution = CommunitySolution.create! valid_attributes - get :index, {}, valid_session + get :index, session: valid_session expect(assigns(:solutions)).to eq([solution]) end end @@ -19,7 +19,7 @@ module Api::V1 context "GET show" do it "assigns the requested solution as @solution" do solution = CommunitySolution.create! valid_attributes - get :show, {id: solution.to_param}, valid_session + get :show, params: { id: solution.to_param }, session: valid_session expect(assigns(:solution)).to eq(solution) end end @@ -28,18 +28,18 @@ module Api::V1 context "with valid params" do it "creates a new CommunitySolution" do expect { - post :create, {solution: valid_attributes}, valid_session + post :create, params: { solution: valid_attributes }, session: valid_session }.to change(CommunitySolution, :count).by(1) end it "assigns a newly created solution as @solution" do - post :create, {solution: valid_attributes}, valid_session + post :create, params: { solution: valid_attributes }, session: valid_session expect(assigns(:solution)).to be_a(CommunitySolution) expect(assigns(:solution)).to be_persisted end it "redirects to the created solution" do - post :create, {solution: valid_attributes}, valid_session + post :create, params: { solution: valid_attributes }, session: valid_session expect(response).to redirect_to(CommunitySolution.last) end end @@ -48,14 +48,14 @@ module Api::V1 it "assigns a newly created but unsaved solution as @solution" do # Trigger the behavior that occurs when invalid params are submitted CommunitySolution.any_instance.stub(:save).and_return(false) - post :create, {solution: { "number" => "invalid value" }}, valid_session + post :create, params: { solution: { "number" => "invalid value" } }, session: valid_session expect(assigns(:solution)).to be_a_new(CommunitySolution) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted CommunitySolution.any_instance.stub(:save).and_return(false) - post :create, {solution: { "number" => "invalid value" }}, valid_session + post :create, params: { solution: { "number" => "invalid value" } }, session: valid_session expect(response).to render_template("new") end end @@ -70,18 +70,18 @@ module Api::V1 # receives the :update_attributes message with whatever params are # submitted in the request. expect(CommunitySolution.any_instance).to_receive(:update_attributes).with({ "number" => "1" }) - put :update, {id: solution.to_param, solution: { "number" => "1" }}, valid_session + put :update, params: { id: solution.to_param, solution: { "number" => "1" } }, session: valid_session end it "assigns the requested solution as @solution" do solution = CommunitySolution.create! valid_attributes - put :update, {id: solution.to_param, solution: valid_attributes}, valid_session + put :update, params: { id: solution.to_param, solution: valid_attributes }, session: valid_session expect(assigns(:solution)).to eq(solution) end it "redirects to the solution" do solution = CommunitySolution.create! valid_attributes - put :update, {id: solution.to_param, solution: valid_attributes}, valid_session + put :update, params: { id: solution.to_param, solution: valid_attributes }, session: valid_session expect(response).to redirect_to(solution) end end @@ -91,7 +91,7 @@ module Api::V1 solution = CommunitySolution.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted CommunitySolution.any_instance.stub(:save).and_return(false) - put :update, {id: solution.to_param, solution: { "number" => "invalid value" }}, valid_session + put :update, params: { id: solution.to_param, solution: { "number" => "invalid value" } }, session: valid_session expect(assigns(:solution)).to eq(solution) end @@ -99,7 +99,7 @@ module Api::V1 solution = CommunitySolution.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted CommunitySolution.any_instance.stub(:save).and_return(false) - put :update, {id: solution.to_param, solution: { "number" => "invalid value" }}, valid_session + put :update, params: { id: solution.to_param, solution: { "number" => "invalid value" } }, session: valid_session expect(response).to render_template("edit") end end @@ -109,13 +109,13 @@ module Api::V1 it "destroys the requested solution" do solution = CommunitySolution.create! valid_attributes expect { - delete :destroy, {id: solution.to_param}, valid_session + delete :destroy, params: { id: solution.to_param }, session: valid_session }.to change(CommunitySolution, :count).by(-1) end it "redirects to the solutions list" do solution = CommunitySolution.create! valid_attributes - delete :destroy, {id: solution.to_param}, valid_session + delete :destroy, params: { id: solution.to_param }, session: valid_session expect(response).to redirect_to(solutions_url) end end diff --git a/spec/controllers/api/v1/deputizations_controller_spec.rb b/spec/controllers/api/v1/deputizations_controller_spec.rb index a1d0e964..176e434d 100644 --- a/spec/controllers/api/v1/deputizations_controller_spec.rb +++ b/spec/controllers/api/v1/deputizations_controller_spec.rb @@ -11,7 +11,7 @@ module Api::V1 context "GET index" do it "assigns all deputizations as @deputizations" do deputization = Deputization.create! valid_attributes - get :index, {}, valid_session + get :index, session: valid_session expect(assigns(:deputizations)).to eq([deputization]) end end @@ -20,18 +20,18 @@ module Api::V1 context "with valid params" do it "creates a new Deputization" do expect { - post :create, {:deputization => valid_attributes}, valid_session + post :create, params: { :deputization => valid_attributes }, session: valid_session }.to change(Deputization, :count).by(1) end it "assigns a newly created deputization as @deputization" do - post :create, {:deputization => valid_attributes}, valid_session + post :create, params: { :deputization => valid_attributes }, session: valid_session expect(assigns(:deputization)).to be_a(Deputization) expect(assigns(:deputization)).to be_persisted end it "redirects to the created deputization" do - post :create, {:deputization => valid_attributes}, valid_session + post :create, params: { :deputization => valid_attributes }, session: valid_session expect(response).to redirect_to(Deputization.last) end end @@ -40,14 +40,14 @@ module Api::V1 it "assigns a newly created but unsaved deputization as @deputization" do # Trigger the behavior that occurs when invalid params are submitted Deputization.any_instance.stub(:save).and_return(false) - post :create, {:deputization => { "number" => "invalid value" }}, valid_session + post :create, params: { :deputization => { "number" => "invalid value" } }, session: valid_session expect(assigns(:deputization)).to be_a_new(Deputization) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted Deputization.any_instance.stub(:save).and_return(false) - post :create, {:deputization => { "number" => "invalid value" }}, valid_session + post :create, params: { :deputization => { "number" => "invalid value" } }, session: valid_session expect(response).to render_template("new") end end @@ -57,13 +57,13 @@ module Api::V1 it "destroys the requested deputization" do deputization = Deputization.create! valid_attributes expect { - delete :destroy, {id: deputization.to_param}, valid_session + delete :destroy, params: { id: deputization.to_param }, session: valid_session }.to change(Deputization, :count).by(-1) end it "redirects to the deputizations list" do deputization = Deputization.create! valid_attributes - delete :destroy, {id: deputization.to_param}, valid_session + delete :destroy, params: { id: deputization.to_param }, session: valid_session expect(response).to redirect_to(deputizations_url) end end diff --git a/spec/controllers/api/v1/exercises_controller_spec.rb b/spec/controllers/api/v1/exercises_controller_spec.rb index afe7f693..cb7c241b 100644 --- a/spec/controllers/api/v1/exercises_controller_spec.rb +++ b/spec/controllers/api/v1/exercises_controller_spec.rb @@ -7,6 +7,7 @@ module Api::V1 application = FactoryBot.create :doorkeeper_application @user = FactoryBot.create :user, :agreed_to_terms + @user_1 = FactoryBot.create :user, :agreed_to_terms admin = FactoryBot.create :user, :administrator, :agreed_to_terms @application_token = FactoryBot.create :doorkeeper_access_token, application: application, @@ -19,9 +20,12 @@ module Api::V1 resource_owner_id: admin.id @exercise = FactoryBot.build(:exercise) - @exercise.publication.authors << FactoryBot.build( + @exercise.publication.authors << FactoryBot.create( :author, user: @user, publication: @exercise.publication ) + @exercise.publication.copyright_holders << FactoryBot.create( + :copyright_holder, user: @user, publication: @exercise.publication + ) @exercise.nickname = 'MyExercise' @exercise.save! end @@ -34,16 +38,21 @@ module Api::V1 10.times { FactoryBot.create(:exercise, :published) } - tested_strings = ["%adipisci%", "%draft%"] - Exercise.joins {questions.outer.stems.outer} - .joins {questions.outer.answers.outer} - .where do - title.like_any(tested_strings) |\ - stimulus.like_any(tested_strings) |\ - questions.stimulus.like_any(tested_strings) |\ - stems.content.like_any(tested_strings) |\ - answers.content.like_any(tested_strings) - end.delete_all + ex = Exercise.arel_table + qu = Question.arel_table + st = Stem.arel_table + ans = Answer.arel_table + + tested_strings = [ "%adipisci%", "%draft%" ] + + ex_ids = Exercise.left_joins(questions: [:stems, :answers]).where( + ex[:title].matches_any(tested_strings) + .or(ex[:stimulus].matches_any(tested_strings)) + .or(qu[:stimulus].matches_any(tested_strings)) + .or(st[:content].matches_any(tested_strings)) + .or(ans[:content].matches_any(tested_strings))).pluck(:id) + + Exercise.where(id: ex_ids).delete_all @exercise_1 = FactoryBot.build(:exercise, :published) Api::V1::Exercises::Representer.new(@exercise_1).from_hash( @@ -59,6 +68,12 @@ module Api::V1 'formats' => [ 'multiple-choice', 'free-response' ] }] ) + @exercise_1.publication.authors << FactoryBot.create( + :author, user: @user_1, publication: @exercise_1.publication + ) + @exercise_1.publication.copyright_holders << FactoryBot.create( + :copyright_holder, user: @user_1, publication: @exercise_1.publication + ) @exercise_1.save! @exercise_2 = FactoryBot.build(:exercise, :published) @@ -75,6 +90,12 @@ module Api::V1 'formats' => [ 'multiple-choice', 'free-response' ] }] ) + @exercise_2.publication.authors << FactoryBot.create( + :author, user: @user_1, publication: @exercise_2.publication + ) + @exercise_2.publication.copyright_holders << FactoryBot.create( + :copyright_holder, user: @user_1, publication: @exercise_2.publication + ) @exercise_2.save! @exercise_draft = FactoryBot.build(:exercise) @@ -99,7 +120,7 @@ module Api::V1 context "no matches" do it "does not return drafts that the user is not allowed to see" do - send method, :index, q: 'content:draft', format: :json + send method, :index, params: {q: 'content:draft', format: :json} expect(response).to have_http_status(:success) expected_response = { @@ -114,9 +135,10 @@ module Api::V1 context "single match" do it "returns drafts that the user is allowed to see" do @exercise_draft.publication.authors << Author.new(user: @user) + @exercise_draft.publication.copyright_holders << CopyrightHolder.new(user: @user) @exercise_draft.reload @user.reload - send method, :index, q: 'content:draft', format: :json + send method, :index, params: {q: 'content:draft', format: :json} expect(response).to have_http_status(:success) expected_response = { @@ -128,7 +150,7 @@ module Api::V1 end it "returns an Exercise matching the content" do - send method, :index, q: 'content:"aDiPiScInG eLiT"', format: :json + send method, :index, params: {q: 'content:"aDiPiScInG eLiT"', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -140,7 +162,7 @@ module Api::V1 end it "returns an Exercise matching the tags" do - send method, :index, q: 'tag:tAg1', format: :json + send method, :index, params: {q: 'tag:tAg1', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -154,7 +176,7 @@ module Api::V1 context "multiple matches" do it "returns Exercises matching the content" do - send method, :index, q: 'content:AdIpIsCi', format: :json + send method, :index, params: {q: 'content:AdIpIsCi', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -167,7 +189,7 @@ module Api::V1 end it "returns Exercises matching the tags" do - send method, :index, q: 'tag:TaG2', format: :json + send method, :index, params: {q: 'tag:TaG2', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -180,8 +202,8 @@ module Api::V1 end it "sorts by multiple fields in different directions" do - send method, :index, q: 'content:aDiPiScI', order_by: "number DESC, version ASC", - format: :json + send method, :index, params: {q: 'content:aDiPiScI', order_by: "number DESC, version ASC", + format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -214,7 +236,7 @@ module Api::V1 end it "returns the Exercise requested by group_uuid and version" do - api_get :show, @user_token, parameters: { + api_get :show, @user_token, params: { id: "#{@exercise.group_uuid}@#{@exercise.version}" } expect(response).to have_http_status(:success) @@ -225,7 +247,7 @@ module Api::V1 end it "returns the Exercise requested by uuid" do - api_get :show, @user_token, parameters: { id: @exercise.uuid } + api_get :show, @user_token, params: { id: @exercise.uuid } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise.uuid)) expect(response.body_as_hash[:versions]).to( @@ -234,7 +256,7 @@ module Api::V1 end it "returns the Exercise requested by uid" do - api_get :show, @user_token, parameters: { id: @exercise.uid } + api_get :show, @user_token, params: { id: @exercise.uid } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise.uuid)) expect(response.body_as_hash[:versions]).to( @@ -243,7 +265,7 @@ module Api::V1 end it "returns the latest published Exercise if only the group_uuid is specified" do - api_get :show, @user_token, parameters: { id: @exercise.group_uuid } + api_get :show, @user_token, params: { id: @exercise.group_uuid } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise.uuid)) expect(response.body_as_hash[:versions]).to( @@ -252,7 +274,7 @@ module Api::V1 end it "returns the latest published Exercise if only the number is specified" do - api_get :show, @user_token, parameters: { id: @exercise.number } + api_get :show, @user_token, params: { id: @exercise.number } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise.uuid)) expect(response.body_as_hash[:versions]).to( @@ -261,7 +283,7 @@ module Api::V1 end it "returns the latest draft Exercise if \"group_uuid@draft\" is requested" do - api_get :show, @user_token, parameters: { id: "#{@exercise.group_uuid}@draft" } + api_get :show, @user_token, params: { id: "#{@exercise.group_uuid}@draft" } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise_2.uuid)) expect(response.body_as_hash[:versions]).to( @@ -270,7 +292,7 @@ module Api::V1 end it "returns the latest draft Exercise if \"number@draft\" is requested" do - api_get :show, @user_token, parameters: { id: "#{@exercise.number}@draft" } + api_get :show, @user_token, params: { id: "#{@exercise.number}@draft" } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise_2.uuid)) expect(response.body_as_hash[:versions]).to( @@ -280,7 +302,7 @@ module Api::V1 it "returns the latest version of a Exercise if \"@latest\" is requested" do @exercise_1.publication.update_attributes(version: 1000) - api_get :show, @user_token, parameters: { id: "#{@exercise.number}@latest" } + api_get :show, @user_token, params: { id: "#{@exercise.number}@latest" } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @exercise_1.uuid)) expect(response.body_as_hash[:versions]).to( @@ -293,7 +315,7 @@ module Api::V1 @exercise_2.destroy expect do - api_get :show, @user_token, parameters: { id: "#{@exercise.number}@draft" } + api_get :show, @user_token, params: { id: "#{@exercise.number}@draft" } end.to change{ Exercise.count }.by(1) expect(response).to have_http_status(:success) @@ -317,7 +339,7 @@ module Api::V1 after(:all) { DatabaseCleaner.clean } it "shows solutions for published exercises if the requestor is an app" do - api_get :show, @application_token, parameters: { id: @exercise.uid } + api_get :show, @application_token, params: { id: @exercise.uid } expect(response).to have_http_status(:success) expect(response.body_as_hash[:questions].first[:collaborator_solutions]).not_to be_empty @@ -328,7 +350,7 @@ module Api::V1 end it "shows solutions for published exercises if the requestor is allowed to edit it" do - api_get :show, @user_token, parameters: { id: @exercise.uid } + api_get :show, @user_token, params: { id: @exercise.uid } expect(response).to have_http_status(:success) expect(response.body_as_hash[:questions].first[:collaborator_solutions]).not_to be_empty @@ -340,8 +362,15 @@ module Api::V1 it "hides solutions for published exercises if the requestor is not allowed to edit it" do @exercise.publication.authors.destroy_all + @exercise.publication.copyright_holders.destroy_all + @exercise.publication.authors << FactoryBot.create( + :author, user: @user_1, publication: @exercise.publication + ) + @exercise.publication.copyright_holders << FactoryBot.create( + :copyright_holder, user: @user_1, publication: @exercise.publication + ) - api_get :show, @user_token, parameters: { id: @exercise.uid } + api_get :show, @user_token, params: { id: @exercise.uid } expect(response).to have_http_status(:success) expect(response.body_as_hash[:questions].first['collaborator_solutions']).to be_nil @@ -352,7 +381,7 @@ module Api::V1 end it "includes versions of the exercise" do - api_get :show, @user_token, parameters: { id: @exercise.uid } + api_get :show, @user_token, params: { id: @exercise.uid } expect(response).to have_http_status(:success) expect(response.body_as_hash[:versions]).to( eq([@exercise_2.version, @exercise_1.version, @exercise.version]) @@ -375,7 +404,7 @@ module Api::V1 it "creates the requested Exercise and assigns the user as author and CR holder" do expect do - api_post :create, @user_token, raw_post_data: Api::V1::Exercises::Representer.new( + api_post :create, @user_token, body: Api::V1::Exercises::Representer.new( @exercise ).to_hash(user_options: { user: @user }) end.to change { Exercise.count }.by(1) @@ -410,9 +439,12 @@ module Api::V1 exercise.publication.authors << FactoryBot.build( :author, user: @user, publication: @exercise.publication ) + exercise.publication.copyright_holders << FactoryBot.build( + :copyright_holder, user: @user, publication: @exercise.publication + ) expect do - api_post :create, @user_token, raw_post_data: Api::V1::Exercises::Representer.new( + api_post :create, @user_token, body: Api::V1::Exercises::Representer.new( exercise ).to_hash(user_options: { user: @user }) end.to change { Exercise.count }.by(1) @@ -427,7 +459,7 @@ module Api::V1 FactoryBot.create :publication_group, nickname: 'MyExercise' expect do - api_post :create, @user_token, raw_post_data: Api::V1::Exercises::Representer.new( + api_post :create, @user_token, body: Api::V1::Exercises::Representer.new( @exercise ).to_hash(user_options: { user: @user }) end.not_to change { Exercise.count } @@ -442,7 +474,7 @@ module Api::V1 before { @old_attributes = @exercise.reload.attributes } it "updates the requested Exercise" do - api_patch :update, @user_token, parameters: { id: @exercise.uid }, raw_post_data: { + api_patch :update, @user_token, params: { id: @exercise.uid }, body: { nickname: 'MyExercise', title: "Ipsum lorem" } expect(response).to have_http_status(:success) @@ -459,7 +491,7 @@ module Api::V1 @exercise.publication.publish.save! expect do - api_patch :update, @user_token, parameters: { id: @exercise.uid }, raw_post_data: { + api_patch :update, @user_token, params: { id: @exercise.uid }, body: { nickname: 'MyExercise', title: "Ipsum lorem" } end.to raise_error(SecurityTransgression) @@ -471,7 +503,7 @@ module Api::V1 it "fails if the nickname has already been taken" do FactoryBot.create :publication_group, nickname: 'MyExercise2' - api_patch :update, @user_token, parameters: { id: @exercise.uid }, raw_post_data: { + api_patch :update, @user_token, params: { id: @exercise.uid }, body: { nickname: 'MyExercise2', title: "Ipsum lorem" } @@ -489,7 +521,7 @@ module Api::V1 exercise_2.reload id = "#{@exercise.number}@draft" - api_patch :update, @user_token, parameters: { id: id }, raw_post_data: { + api_patch :update, @user_token, params: { id: id }, body: { nickname: 'MyExercise', title: "Ipsum lorem" } expect(response).to have_http_status(:success) @@ -512,7 +544,7 @@ module Api::V1 id = "#{@exercise.number}@draft" expect do - api_patch :update, @user_token, parameters: { id: id }, raw_post_data: { + api_patch :update, @user_token, params: { id: id }, body: { nickname: 'MyExercise', title: "Ipsum lorem" } end.to change{ Exercise.count }.by(1) @@ -538,7 +570,7 @@ module Api::V1 context "DELETE destroy" do it "deletes the requested draft Exercise" do expect do - api_delete :destroy, @user_token, parameters: { id: @exercise.uid } + api_delete :destroy, @user_token, params: { id: @exercise.uid } end.to change(Exercise, :count).by(-1) expect(response).to have_http_status(:success) expect(Exercise.where(id: @exercise.id)).not_to exist diff --git a/spec/controllers/api/v1/publications_controller_spec.rb b/spec/controllers/api/v1/publications_controller_spec.rb index 11a38744..cffd9950 100644 --- a/spec/controllers/api/v1/publications_controller_spec.rb +++ b/spec/controllers/api/v1/publications_controller_spec.rb @@ -7,16 +7,26 @@ module Api::V1 let(:vocab_term) { FactoryBot.create :vocab_term } let(:solution) { FactoryBot.create :community_solution } - let(:exercise_author) { + let!(:exercise_author) { FactoryBot.create :author, publication: exercise.publication } - let(:vocab_term_author) { + let!(:vocab_term_author) { FactoryBot.create :author, publication: vocab_term.publication } - let(:solution_author) { + let!(:solution_author) { FactoryBot.create :author, publication: solution.publication } + let!(:exercise_copyright_holder) { + FactoryBot.create :copyright_holder, publication: exercise.publication + } + let!(:vocab_term_copyright_holder) { + FactoryBot.create :copyright_holder, publication: vocab_term.publication + } + let!(:solution_copyright_holder) { + FactoryBot.create :copyright_holder, publication: solution.publication + } + let(:exercise_author_token) { FactoryBot.create :doorkeeper_access_token, resource_owner_id: exercise_author.user_id @@ -36,7 +46,7 @@ module Api::V1 expect(exercise.reload.is_published?).to eq false api_put :publish, exercise_author_token, - parameters: { exercise_id: exercise.uid.to_s } + params: { exercise_id: exercise.uid.to_s } expected_response = Api::V1::Exercises::Representer .new(exercise.reload) @@ -54,7 +64,7 @@ module Api::V1 end api_put :publish, exercise_author_token, - parameters: { exercise_id: exercise.uid.to_s } + params: { exercise_id: exercise.uid.to_s } expected_response = { errors: [{ code: 'exercise_has_a_question_with_only_incorrect_answers', @@ -72,7 +82,7 @@ module Api::V1 expect(vocab_term.reload.is_published?).to eq false api_put :publish, vocab_term_author_token, - parameters: { vocab_term_id: vocab_term.uid.to_s } + params: { vocab_term_id: vocab_term.uid.to_s } expected_response = \ Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new(vocab_term.reload) @@ -88,7 +98,7 @@ module Api::V1 vocab_term.vocab_distractors.delete_all api_put :publish, vocab_term_author_token, - parameters: { vocab_term_id: vocab_term.uid.to_s } + params: { vocab_term_id: vocab_term.uid.to_s } expected_response = { errors: [{ code: 'vocab_term_must_have_at_least_1_distractor', @@ -105,7 +115,7 @@ module Api::V1 it "publishes the requested community solution" do expect(solution.reload.is_published?).to eq false - api_put :publish, solution_author_token, parameters: { + api_put :publish, solution_author_token, params: { community_solution_id: solution.uid.to_s } @@ -124,7 +134,7 @@ module Api::V1 expect{ api_put :publish, exercise_author_token, - parameters: { exercise_id: exercise.uid.to_s, + params: { exercise_id: exercise.uid.to_s, vocab_term_id: vocab_term.uid.to_s, community_solution_id: solution.uid.to_s } }.to raise_error(ActionController::BadRequest) diff --git a/spec/controllers/api/v1/users_controller_spec.rb b/spec/controllers/api/v1/users_controller_spec.rb index e46fd998..7b04dbca 100644 --- a/spec/controllers/api/v1/users_controller_spec.rb +++ b/spec/controllers/api/v1/users_controller_spec.rb @@ -28,9 +28,10 @@ module Api::V1 before do 100.times { FactoryBot.create(:user) } - User.joins(:account).where {(account.first_name.like '%doe%') | - (account.last_name.like '%doe%') | - (account.username.like '%doe%')}.delete_all + acc = OpenStax::Accounts::Account.arel_table + User.joins(:account).where(acc[:first_name].matches('%doe%').or( + acc[:last_name].matches('%doe%')).or( + acc[:username].matches('%doe%'))).delete_all @john_doe = FactoryBot.create :user, first_name: "John", last_name: "Doe", @@ -43,7 +44,7 @@ module Api::V1 end it "returns no results if the maximum number of results is exceeded" do - api_get :index, admin_token, parameters: {q: ''} + api_get :index, admin_token, params: {q: ''} expect(response).to have_http_status(:success) expected_response = { @@ -55,7 +56,7 @@ module Api::V1 end it "returns single results" do - api_get :index, application_token, parameters: { q: 'first_name:jOhN last_name:dOe' } + api_get :index, application_token, params: { q: 'first_name:jOhN last_name:dOe' } expect(response).to have_http_status(:success) expected_response = { @@ -72,7 +73,8 @@ module Api::V1 self_reported_role: @john_doe.role, uuid: @john_doe.uuid, support_identifier: @john_doe.support_identifier, - is_test: true + is_test: true, + school_type: 'unknown_school_type' } ] } @@ -81,7 +83,7 @@ module Api::V1 end it "returns multiple results" do - api_get :index, user_token, parameters: {q: 'last_name:DoE'} + api_get :index, user_token, params: {q: 'last_name:DoE'} expect(response).to have_http_status(:success) expected_response = { @@ -98,7 +100,8 @@ module Api::V1 self_reported_role: @jane_doe.role, uuid: @jane_doe.uuid, support_identifier: @jane_doe.support_identifier, - is_test: true + is_test: true, + school_type: 'unknown_school_type' }, { id: @john_doe.account.openstax_uid, @@ -111,7 +114,9 @@ module Api::V1 self_reported_role: @john_doe.role, uuid: @john_doe.uuid, support_identifier: @john_doe.support_identifier, - is_test: true + is_test: true, + school_type: 'unknown_school_type' + } ] } @@ -120,7 +125,7 @@ module Api::V1 end it "sorts by multiple fields in different directions" do - api_get :index, user_token, parameters: {q: 'username:doe', + api_get :index, user_token, params: {q: 'username:doe', order_by: "first_name DESC, last_name"} expect(response).to have_http_status(:success) @@ -138,7 +143,9 @@ module Api::V1 self_reported_role: @john_doe.role, uuid: @john_doe.uuid, support_identifier: @john_doe.support_identifier, - is_test: true + is_test: true, + school_type: 'unknown_school_type' + }, { id: @jane_doe.account.openstax_uid, @@ -151,7 +158,9 @@ module Api::V1 self_reported_role: @jane_doe.role, uuid: @jane_doe.uuid, support_identifier: @jane_doe.support_identifier, - is_test: true + is_test: true, + school_type: 'unknown_school_type' + } ] } @@ -184,7 +193,7 @@ module Api::V1 end it "ignores id parameters" do - api_get :show, user_token, parameters: {id: admin.id, user_id: admin.id} + api_get :show, user_token, params: {id: admin.id, user_id: admin.id} expect(response).to have_http_status(:success) expected_response = { @@ -208,7 +217,7 @@ module Api::V1 context "PATCH update" do it "updates the current User's profile" do - api_patch :update, user_token, raw_post_data: { + api_patch :update, user_token, body: { first_name: "Jerry", last_name: "Mouse" } expect(response).to have_http_status(:success) @@ -218,9 +227,9 @@ module Api::V1 end it "ignores id parameters" do - api_patch :update, user_token, raw_post_data: { + api_patch :update, user_token, body: { first_name: "Jerry", last_name: "Mouse" - }, parameters: {id: admin.id, user_id: admin.id} + }, params: {id: admin.id, user_id: admin.id} expect(response).to have_http_status(:success) user.reload admin.reload @@ -242,7 +251,7 @@ module Api::V1 end it "ignores id parameters" do - api_delete :destroy, user_token, parameters: {id: admin.id, user_id: admin.id} + api_delete :destroy, user_token, params: {id: admin.id, user_id: admin.id} expect(response).to have_http_status(:success) user.reload admin.reload diff --git a/spec/controllers/api/v1/vocab_terms_controller_spec.rb b/spec/controllers/api/v1/vocab_terms_controller_spec.rb index b3582b9e..1336ef4e 100644 --- a/spec/controllers/api/v1/vocab_terms_controller_spec.rb +++ b/spec/controllers/api/v1/vocab_terms_controller_spec.rb @@ -7,6 +7,9 @@ module Api::V1 application = FactoryBot.create :doorkeeper_application @user = FactoryBot.create :user, :agreed_to_terms + @user_1 = FactoryBot.create :user, :agreed_to_terms + @user_2 = FactoryBot.create :user, :agreed_to_terms + @user_draft = FactoryBot.create :user, :agreed_to_terms admin = FactoryBot.create :user, :administrator, :agreed_to_terms @application_token = FactoryBot.create :doorkeeper_access_token, application: application, @@ -22,6 +25,9 @@ module Api::V1 @vocab_term.publication.authors << FactoryBot.build( :author, user: @user, publication: @vocab_term.publication ) + @vocab_term.publication.copyright_holders << FactoryBot.build( + :copyright_holder, user: @user, publication: @vocab_term.publication + ) @vocab_term.nickname = 'MyVocab' @vocab_term.save! end @@ -35,11 +41,12 @@ module Api::V1 10.times do vt = FactoryBot.create(:vocab_term, :published) vt.publication.authors << Author.new(publication: vt.publication, user: @user) + vt.publication.copyright_holders << CopyrightHolder.new(publication: vt.publication, user: @user) end tested_strings = ["%lorem ipsu%", "%adipiscing elit%", "draft"] - VocabTerm.where {(name.like_any tested_strings) | - (definition.like_any tested_strings)}.delete_all + VocabTerm.where(VocabTerm.arel_table[:name].matches_any(tested_strings).or( + VocabTerm.arel_table[:definition].matches_any(tested_strings))).delete_all @vocab_term_1 = FactoryBot.build(:vocab_term, :published) Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new( @@ -50,6 +57,8 @@ module Api::V1 'definition' => "Dolor sit amet", 'distractor_literals' => ["Consectetur adipiscing elit", "Sed do eiusmod tempor"] ) + @vocab_term_1.publication.authors << Author.new(publication: @vocab_term_1.publication, user: @user_1) + @vocab_term_1.publication.copyright_holders << CopyrightHolder.new(publication: @vocab_term_1.publication, user: @user_1) @vocab_term_1.save! @vocab_term_2 = FactoryBot.build(:vocab_term, :published) @@ -61,6 +70,8 @@ module Api::V1 'definition' => "Quia dolor sit amet", 'distractor_literals' => ["Consectetur adipisci velit", "Sed quia non numquam"] ) + @vocab_term_2.publication.authors << Author.new(publication: @vocab_term_2.publication, user: @user_2) + @vocab_term_2.publication.copyright_holders << CopyrightHolder.new(publication: @vocab_term_2.publication, user: @user_2) @vocab_term_2.save! @vocab_term_draft = FactoryBot.build(:vocab_term) @@ -72,6 +83,8 @@ module Api::V1 'definition' => "Not ready for prime time", 'distractor_literals' => ["Release to production NOW"] ) + @vocab_term_draft.publication.authors << Author.new(publication: @vocab_term_draft.publication, user: @user_draft) + @vocab_term_draft.publication.copyright_holders << CopyrightHolder.new(publication: @vocab_term_draft.publication, user: @user_draft) @vocab_term_draft.save! end after(:all) { DatabaseCleaner.clean } @@ -80,7 +93,7 @@ module Api::V1 context "no matches" do it "does not return drafts that the user is not allowed to see" do - send method, :index, q: 'content:draft', format: :json + send method, :index, params: { q: 'content:draft', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -95,9 +108,10 @@ module Api::V1 context "single match" do it "returns drafts that the user is allowed to see" do @vocab_term_draft.publication.authors << Author.new(user: @user) + @vocab_term_draft.publication.copyright_holders << CopyrightHolder.new(user: @user) @vocab_term_draft.reload @user.reload - send method, :index, q: 'content:draft', format: :json + send method, :index, params: { q: 'content:draft', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -109,7 +123,7 @@ module Api::V1 end it "returns a VocabTerm matching the content" do - send method, :index, q: 'content:"oLoReM iPsU"', format: :json + send method, :index, params: { q: 'content:"oLoReM iPsU"', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -121,7 +135,7 @@ module Api::V1 end it "returns a VocabTerm matching the tags" do - send method, :index, q: 'tag:tAg1', format: :json + send method, :index, params: { q: 'tag:tAg1', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -135,7 +149,7 @@ module Api::V1 context "multiple matches" do it "returns VocabTerms matching the content" do - send method, :index, q: 'content:"lOrEm IpSuM"', format: :json + send method, :index, params: { q: 'content:"lOrEm IpSuM"', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -148,7 +162,7 @@ module Api::V1 end it "returns VocabTerms matching the tags" do - send method, :index, q: 'tag:TaG2', format: :json + send method, :index, params: { q: 'tag:TaG2', format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -161,8 +175,8 @@ module Api::V1 end it "sorts by multiple fields in different directions" do - send method, :index, q: 'content:"lOrEm IpSuM"', order_by: "number DESC, version ASC", - format: :json + send method, :index, params: {q: 'content:"lOrEm IpSuM"', order_by: "number DESC, version ASC", + format: :json } expect(response).to have_http_status(:success) expected_response = { @@ -189,7 +203,7 @@ module Api::V1 after(:all) { DatabaseCleaner.clean } it "returns the VocabTerm requested by group_uuid and version" do - api_get :show, @user_token, parameters: { + api_get :show, @user_token, params: { id: "#{@vocab_term.group_uuid}@#{@vocab_term.version}" } expect(response).to have_http_status(:success) @@ -197,37 +211,37 @@ module Api::V1 end it "returns the VocabTerm requested by uuid" do - api_get :show, @user_token, parameters: { id: @vocab_term.uuid } + api_get :show, @user_token, params: { id: @vocab_term.uuid } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @vocab_term.uuid)) end it "returns the VocabTerm requested by uid" do - api_get :show, @user_token, parameters: { id: @vocab_term.uid } + api_get :show, @user_token, params: { id: @vocab_term.uid } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @vocab_term.uuid)) end it "returns the latest published VocabTerm if only the group_uuid is specified" do - api_get :show, @user_token, parameters: { id: @vocab_term.group_uuid } + api_get :show, @user_token, params: { id: @vocab_term.group_uuid } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @vocab_term.uuid)) end it "returns the latest published VocabTerm if only the number is specified" do - api_get :show, @user_token, parameters: { id: @vocab_term.number } + api_get :show, @user_token, params: { id: @vocab_term.number } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @vocab_term.uuid)) end it "returns the latest draft VocabTerm if \"group_uuid@draft\" is requested" do - api_get :show, @user_token, parameters: { id: "#{@vocab_term.group_uuid}@draft" } + api_get :show, @user_token, params: { id: "#{@vocab_term.group_uuid}@draft" } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @vocab_term_2.uuid)) end it "returns the latest draft VocabTerm if \"number@draft\" is requested" do - api_get :show, @user_token, parameters: { id: "#{@vocab_term.number}@draft" } + api_get :show, @user_token, params: { id: "#{@vocab_term.number}@draft" } expect(response).to have_http_status(:success) expect(response.body_as_hash).to match(a_hash_including(uuid: @vocab_term_2.uuid)) end @@ -236,7 +250,7 @@ module Api::V1 @vocab_term_2.destroy expect do - api_get :show, @user_token, parameters: { id: "#{@vocab_term.number}@draft" } + api_get :show, @user_token, params: { id: "#{@vocab_term.number}@draft" } end.to change{ VocabTerm.count }.by(1) expect(response).to have_http_status(:success) @@ -265,7 +279,7 @@ module Api::V1 it "creates the requested VocabTerm and assigns the user as author and CR holder" do expect do api_post :create, @user_token, - raw_post_data: Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new( + body: Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new( @vocab_term ).to_hash(user_options: { user: @user }) end.to change { VocabTerm.count }.by(1) @@ -285,7 +299,7 @@ module Api::V1 expect do api_post :create, @user_token, - raw_post_data: Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new( + body: Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new( @vocab_term ).to_hash(user_options: { user: @user }) end.not_to change { VocabTerm.count } @@ -299,8 +313,8 @@ module Api::V1 before { @old_attributes = @vocab_term.reload.attributes } it "updates the requested VocabTerm" do - api_patch :update, @user_token, parameters: { id: @vocab_term.uid }, - raw_post_data: { nickname: 'MyVocab', term: "Ipsum lorem" } + api_patch :update, @user_token, params: { id: @vocab_term.uid }, + body: { nickname: 'MyVocab', term: "Ipsum lorem" } expect(response).to have_http_status(:success) @vocab_term.reload new_attributes = @vocab_term.attributes @@ -315,7 +329,7 @@ module Api::V1 @vocab_term.publication.publish.save! expect do - api_patch :update, @user_token, parameters: { id: @vocab_term.uid }, raw_post_data: { + api_patch :update, @user_token, params: { id: @vocab_term.uid }, body: { nickname: 'MyVocab', term: "Ipsum lorem" } end.to raise_error(SecurityTransgression) @@ -329,7 +343,7 @@ module Api::V1 it "fails if the nickname has already been taken" do FactoryBot.create :publication_group, nickname: 'MyVocab2' - api_patch :update, @user_token, parameters: { id: @vocab_term.uid }, raw_post_data: { + api_patch :update, @user_token, params: { id: @vocab_term.uid }, body: { nickname: 'MyVocab2', title: "Ipsum lorem" } @@ -348,8 +362,8 @@ module Api::V1 vocab_term_2.save! vocab_term_2.reload - api_patch :update, @user_token, parameters: { id: "#{@vocab_term.number}@draft" }, - raw_post_data: { nickname: 'MyVocab', term: "Ipsum lorem" } + api_patch :update, @user_token, params: { id: "#{@vocab_term.number}@draft" }, + body: { nickname: 'MyVocab', term: "Ipsum lorem" } expect(response).to have_http_status(:success) @vocab_term.reload @@ -371,8 +385,8 @@ module Api::V1 @vocab_term.publication.publish.save! expect do - api_patch :update, @user_token, parameters: { id: "#{@vocab_term.number}@draft" }, - raw_post_data: { + api_patch :update, @user_token, params: { id: "#{@vocab_term.number}@draft" }, + body: { nickname: 'MyVocab', term: "Ipsum lorem" } end.to change{ VocabTerm.count }.by(1) @@ -400,7 +414,7 @@ module Api::V1 context "DELETE destroy" do it "deletes the requested draft VocabTerm" do expect do - api_delete :destroy, @user_token, parameters: { id: @vocab_term.uid } + api_delete :destroy, @user_token, params: { id: @vocab_term.uid } end.to change(VocabTerm, :count).by(-1) expect(response).to have_http_status(:success) expect(VocabTerm.where(id: @vocab_term.id)).not_to exist diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index 1f63244e..fd55c01d 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -34,7 +34,7 @@ module Oauth context "GET index" do it "assigns the user's applications as @applications" do - get :index, {}, valid_session + get :index, session: valid_session expect(response.code).to eq('200') expect(assigns :applications).to include(group_1_application_1) expect(assigns :applications).to include(group_1_application_2) @@ -43,7 +43,7 @@ module Oauth end it "assigns all applications as @applications for admins" do - get :index, {}, admin_session + get :index, session: admin_session expect(response.code).to eq('200') expect(assigns :applications).to include(group_1_application_1) expect(assigns :applications).to include(group_1_application_2) @@ -54,21 +54,21 @@ module Oauth context "GET show" do it "assigns the requested application as @application" do - get :show, {id: group_1_application_1.to_param}, valid_session + get :show, params: {id: group_1_application_1.to_param}, session: valid_session expect(assigns(:application)).to eq(group_1_application_1) end end context "GET new" do it "assigns a new application as @application" do - get :new, {}, admin_session + get :new, session: admin_session expect(assigns(:application)).to be_a_new(Doorkeeper::Application) end end context "GET edit" do it "assigns the requested application as @application" do - get :edit, {id: group_1_application_1.to_param}, valid_session + get :edit, params: {id: group_1_application_1.to_param}, session: valid_session expect(assigns(:application)).to eq(group_1_application_1) end end @@ -79,18 +79,18 @@ module Oauth it "creates a new Application" do expect { - post :create, {doorkeeper_application: valid_attributes}, admin_session + post :create, params: {doorkeeper_application: valid_attributes}, session: admin_session }.to change(Doorkeeper::Application, :count).by(1) end it "assigns a newly created application as @application" do - post :create, {doorkeeper_application: valid_attributes}, admin_session + post :create, params: {doorkeeper_application: valid_attributes}, session: admin_session expect(assigns(:application)).to be_a(Doorkeeper::Application) expect(assigns(:application)).to be_persisted end it "redirects to the created application" do - post :create, {doorkeeper_application: valid_attributes}, admin_session + post :create, params: {doorkeeper_application: valid_attributes}, session: admin_session expect(response).to redirect_to( oauth_application_url(Doorkeeper::Application.last) ) @@ -100,13 +100,13 @@ module Oauth context "with invalid params" do it "assigns a newly created but unsaved application as @application" do # Trigger the behavior that occurs when invalid params are submitted - post :create, {doorkeeper_application: { "redirect_uri" => "invalid" }}, admin_session + post :create, params: {doorkeeper_application: { "redirect_uri" => "invalid" }}, session: admin_session expect(assigns(:application)).to be_a_new(Doorkeeper::Application) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted - post :create, {doorkeeper_application: { "redirect_uri" => "invalid" }}, admin_session + post :create, params: {doorkeeper_application: { "redirect_uri" => "invalid" }}, session: admin_session expect(response).to render_template("new") end end @@ -114,22 +114,23 @@ module Oauth context "PATCH update" do context "with valid params" do + dummy_params = ActionController::Parameters.new({ "name" => "Dummy" }).permit! it "updates the requested application" do expect_any_instance_of(Doorkeeper::Application) - .to receive(:update_attributes).with({ "name" => "Dummy" }) - patch :update, {id: group_1_application_1.to_param, - doorkeeper_application: { "name" => "Dummy" }}, valid_session + .to receive(:update_attributes).with(dummy_params) + patch :update, params: {id: group_1_application_1.to_param, + doorkeeper_application: dummy_params}, session: valid_session end it "assigns the requested application as @application" do - patch :update, {id: group_1_application_1.to_param, - doorkeeper_application: { "name" => "Dummy" }}, valid_session + patch :update, params: {id: group_1_application_1.to_param, + doorkeeper_application: dummy_params}, session: valid_session expect(assigns(:application)).to eq(group_1_application_1) end it "redirects to the application" do - patch :update, {id: group_1_application_1.to_param, - doorkeeper_application: { "name" => "Dummy" }}, valid_session + patch :update, params: {id: group_1_application_1.to_param, + doorkeeper_application: dummy_params}, session: valid_session expect(response).to redirect_to( oauth_application_url(group_1_application_1) ) @@ -139,15 +140,15 @@ module Oauth context "with invalid params" do it "assigns the application as @application" do # Trigger the behavior that occurs when invalid params are submitted - patch :update, {id: group_1_application_1.to_param, - doorkeeper_application: { "redirect_uri" => "invalid" }}, valid_session + patch :update, params: {id: group_1_application_1.to_param, + doorkeeper_application: { "redirect_uri" => "invalid" }}, session: valid_session expect(assigns(:application)).to eq(group_1_application_1) end it "re-renders the 'edit' template" do # Trigger the behavior that occurs when invalid params are submitted - patch :update, {id: group_1_application_1.to_param, - doorkeeper_application: { "redirect_uri" => "invalid" }}, valid_session + patch :update, params: {id: group_1_application_1.to_param, + doorkeeper_application: { "redirect_uri" => "invalid" }}, session: valid_session expect(response).to render_template("edit") end end @@ -156,12 +157,12 @@ module Oauth context "DELETE destroy" do it "destroys the requested application" do expect { - delete :destroy, {id: group_1_application_1.to_param}, admin_session + delete :destroy, params: {id: group_1_application_1.to_param}, session: admin_session }.to change(Doorkeeper::Application, :count).by(-1) end it "redirects to the applications list" do - delete :destroy, {id: group_1_application_1.to_param}, admin_session + delete :destroy, params: {id: group_1_application_1.to_param}, session: admin_session expect(response).to redirect_to(oauth_applications_url) end end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 98d4ead8..79dad655 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -7,7 +7,7 @@ first_name { Faker::Name.first_name } last_name { Faker::Name.last_name } full_name { "#{first_name} #{last_name}" } - title { Faker::Name.title } + title { Faker::Job.title } end after(:build) do |user, evaluator| diff --git a/spec/lib/publishable_spec.rb b/spec/lib/publishable_spec.rb index 7ff3661c..cd962d30 100644 --- a/spec/lib/publishable_spec.rb +++ b/spec/lib/publishable_spec.rb @@ -6,9 +6,11 @@ subject(:publishable) { FactoryBot.create :exercise } let(:author) { FactoryBot.create :user } + let(:coyright_holder) { FactoryBot.create :user } let(:user) { FactoryBot.create :user } - before { publishable.authors << Author.new(user: author) } + before { publishable.authors << Author.new(user: author) + publishable.copyright_holders << CopyrightHolder.new(user: author) } it 'can determine versions visible for a user' do p1 = publishable diff --git a/spec/models/administrator_spec.rb b/spec/models/administrator_spec.rb index a80b1e50..9882ca3a 100644 --- a/spec/models/administrator_spec.rb +++ b/spec/models/administrator_spec.rb @@ -6,8 +6,6 @@ it { is_expected.to belong_to(:user) } - it { is_expected.to validate_presence_of(:user) } - it { is_expected.to validate_uniqueness_of(:user) } end diff --git a/spec/models/answer_spec.rb b/spec/models/answer_spec.rb index bcd19753..12a960b2 100644 --- a/spec/models/answer_spec.rb +++ b/spec/models/answer_spec.rb @@ -6,7 +6,6 @@ it { is_expected.to have_many(:combo_choice_answers).dependent(:destroy) } - it { is_expected.to validate_presence_of(:question) } it { is_expected.to validate_presence_of(:content) } end diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb index bcaa9442..10ee5924 100644 --- a/spec/models/attachment_spec.rb +++ b/spec/models/attachment_spec.rb @@ -9,8 +9,6 @@ it { is_expected.to belong_to(:parent) } - it { is_expected.to validate_presence_of(:parent) } - it 'requires a unique asset for each parent' do attachment_2 = FactoryBot.build :attachment, parent: attachment.parent, asset: nil expect(attachment_2).not_to be_valid diff --git a/spec/models/author_spec.rb b/spec/models/author_spec.rb index 76a7cd7d..b4b9cb75 100644 --- a/spec/models/author_spec.rb +++ b/spec/models/author_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:publication) } it { is_expected.to belong_to(:user) } - it { is_expected.to validate_presence_of(:publication) } - it { is_expected.to validate_presence_of(:user) } - it { is_expected.to delegate_method(:name).to(:user) } it 'requires a unique user for each publication' do diff --git a/spec/models/class_license_spec.rb b/spec/models/class_license_spec.rb index 800874c7..a94d4aa0 100644 --- a/spec/models/class_license_spec.rb +++ b/spec/models/class_license_spec.rb @@ -6,7 +6,6 @@ it { is_expected.to belong_to(:license) } - it { is_expected.to validate_presence_of(:license) } it { is_expected.to validate_presence_of(:class_name) } it { is_expected.to validate_uniqueness_of(:class_name).scoped_to(:license_id) } diff --git a/spec/models/combo_choice_answer_spec.rb b/spec/models/combo_choice_answer_spec.rb index b1f612d8..ae65371c 100644 --- a/spec/models/combo_choice_answer_spec.rb +++ b/spec/models/combo_choice_answer_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:combo_choice) } it { is_expected.to belong_to(:answer) } - it { is_expected.to validate_presence_of(:combo_choice) } - it { is_expected.to validate_presence_of(:answer) } - it { is_expected.to validate_uniqueness_of(:answer).scoped_to(:combo_choice_id) } it 'should require answer and combo_choice to have the same question' do diff --git a/spec/models/combo_choice_spec.rb b/spec/models/combo_choice_spec.rb index 6135bb22..5a9e227b 100644 --- a/spec/models/combo_choice_spec.rb +++ b/spec/models/combo_choice_spec.rb @@ -6,7 +6,6 @@ it { is_expected.to have_many(:combo_choice_answers).dependent(:destroy) } - it { is_expected.to validate_presence_of(:stem) } it { is_expected.to validate_presence_of(:correctness) } it { is_expected.to validate_numericality_of(:correctness) } diff --git a/spec/models/copyright_holder_spec.rb b/spec/models/copyright_holder_spec.rb index 00ac4373..ebb927f3 100644 --- a/spec/models/copyright_holder_spec.rb +++ b/spec/models/copyright_holder_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:publication) } it { is_expected.to belong_to(:user) } - it { is_expected.to validate_presence_of(:publication) } - it { is_expected.to validate_presence_of(:user) } - it { is_expected.to delegate_method(:name).to(:user) } it 'requires a unique user for each publication' do diff --git a/spec/models/deputization_spec.rb b/spec/models/deputization_spec.rb index 2dbc0317..0b97c4cb 100644 --- a/spec/models/deputization_spec.rb +++ b/spec/models/deputization_spec.rb @@ -5,9 +5,6 @@ it { is_expected.to belong_to(:deputy) } it { is_expected.to belong_to(:deputizer) } - it { is_expected.to validate_presence_of(:deputy) } - it { is_expected.to validate_presence_of(:deputizer) } - it 'requires a unique deputizer for each deputy' do deputization_1 = FactoryBot.create :deputization deputization_2 = FactoryBot.build :deputization, diff --git a/spec/models/derivation_spec.rb b/spec/models/derivation_spec.rb index 63079537..912719ce 100644 --- a/spec/models/derivation_spec.rb +++ b/spec/models/derivation_spec.rb @@ -5,9 +5,7 @@ subject(:derivation) { FactoryBot.create :derivation } it { is_expected.to belong_to(:derived_publication) } - it { is_expected.to belong_to(:source_publication) } - - it { is_expected.to validate_presence_of(:derived_publication) } + it { is_expected.to belong_to(:source_publication).optional } it 'requires a unique source_publication for each derived_publication' do derivation_2 = FactoryBot.build(:derivation, diff --git a/spec/models/exercise_spec.rb b/spec/models/exercise_spec.rb index 7f9a75e6..ce1cdc68 100644 --- a/spec/models/exercise_spec.rb +++ b/spec/models/exercise_spec.rb @@ -14,7 +14,7 @@ expect(exercise.errors).to be_empty exercise.questions = [] - exercise.send :has_questions + expect { exercise.send :has_questions }.to throw_symbol(:abort) expect(exercise.errors[:questions]).to include("can't be blank") end @@ -26,7 +26,7 @@ exercise.questions.first.stems.first.stem_answers.each do |stem_answer| stem_answer.update_attribute :correctness, 0.0 end - exercise.before_publication + expect { exercise.before_publication }.to throw_symbol(:abort) expect(exercise.errors[:base]).to include('has a question with only incorrect answers') end diff --git a/spec/models/exercise_tag_spec.rb b/spec/models/exercise_tag_spec.rb index f7c27b89..85db1952 100644 --- a/spec/models/exercise_tag_spec.rb +++ b/spec/models/exercise_tag_spec.rb @@ -6,8 +6,5 @@ it { is_expected.to belong_to(:exercise) } it { is_expected.to belong_to(:tag) } - it { is_expected.to validate_presence_of(:exercise) } - it { is_expected.to validate_presence_of(:tag) } - it { is_expected.to validate_uniqueness_of(:tag).scoped_to(:exercise_id) } end diff --git a/spec/models/hint_spec.rb b/spec/models/hint_spec.rb index d761581e..7c7d06b2 100644 --- a/spec/models/hint_spec.rb +++ b/spec/models/hint_spec.rb @@ -4,7 +4,6 @@ it { is_expected.to belong_to(:question) } - it { is_expected.to validate_presence_of(:question) } it { is_expected.to validate_presence_of(:content) } end diff --git a/spec/models/license_compatibility_spec.rb b/spec/models/license_compatibility_spec.rb index 6998cb0e..4e0f1db7 100644 --- a/spec/models/license_compatibility_spec.rb +++ b/spec/models/license_compatibility_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:original_license) } it { is_expected.to belong_to(:combined_license) } - it { is_expected.to validate_presence_of(:original_license) } - it { is_expected.to validate_presence_of(:combined_license) } - it { is_expected.to validate_uniqueness_of(:combined_license).scoped_to(:original_license_id) } end diff --git a/spec/models/list_editor_spec.rb b/spec/models/list_editor_spec.rb index 52ddde8f..cb0f505b 100644 --- a/spec/models/list_editor_spec.rb +++ b/spec/models/list_editor_spec.rb @@ -5,9 +5,6 @@ it { is_expected.to belong_to(:list) } it { is_expected.to belong_to(:editor) } - it { is_expected.to validate_presence_of(:list) } - it { is_expected.to validate_presence_of(:editor) } - it 'requires a unique editor for each list' do list_editor_1 = FactoryBot.create :list_editor list_editor_2 = FactoryBot.build :list_editor, diff --git a/spec/models/list_nesting_spec.rb b/spec/models/list_nesting_spec.rb index f39fb14f..d0abfbfe 100644 --- a/spec/models/list_nesting_spec.rb +++ b/spec/models/list_nesting_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:parent_list) } it { is_expected.to belong_to(:child_list) } - it { is_expected.to validate_presence_of(:parent_list) } - it { is_expected.to validate_presence_of(:child_list) } - it { is_expected.to validate_uniqueness_of(:child_list) } end diff --git a/spec/models/list_owner_spec.rb b/spec/models/list_owner_spec.rb index d3a12966..d89b1a23 100644 --- a/spec/models/list_owner_spec.rb +++ b/spec/models/list_owner_spec.rb @@ -5,9 +5,6 @@ it { is_expected.to belong_to(:list) } it { is_expected.to belong_to(:owner) } - it { is_expected.to validate_presence_of(:list) } - it { is_expected.to validate_presence_of(:owner) } - it 'requires a unique owner for each list' do list_owner_1 = FactoryBot.create :list_owner list_owner_2 = FactoryBot.build :list_owner, diff --git a/spec/models/list_publication_group_spec.rb b/spec/models/list_publication_group_spec.rb index ce6a2625..37bbe45c 100644 --- a/spec/models/list_publication_group_spec.rb +++ b/spec/models/list_publication_group_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:list) } it { is_expected.to belong_to(:publication_group) } - it { is_expected.to validate_presence_of(:list) } - it { is_expected.to validate_presence_of(:publication_group) } - it { is_expected.to validate_uniqueness_of(:publication_group).scoped_to(:list_id) } end diff --git a/spec/models/list_reader_spec.rb b/spec/models/list_reader_spec.rb index 4e6078c1..878b4a63 100644 --- a/spec/models/list_reader_spec.rb +++ b/spec/models/list_reader_spec.rb @@ -5,9 +5,6 @@ it { is_expected.to belong_to(:list) } it { is_expected.to belong_to(:reader) } - it { is_expected.to validate_presence_of(:list) } - it { is_expected.to validate_presence_of(:reader) } - it 'requires a unique reader for each list' do list_reader_1 = FactoryBot.create :list_reader list_reader_2 = FactoryBot.build :list_reader, diff --git a/spec/models/logic_spec.rb b/spec/models/logic_spec.rb index 25ec3dee..2cc015dd 100644 --- a/spec/models/logic_spec.rb +++ b/spec/models/logic_spec.rb @@ -6,7 +6,6 @@ it { is_expected.to have_many(:logic_variables) } - it { is_expected.to validate_presence_of(:parent) } it { is_expected.to validate_presence_of(:language) } it { is_expected.to validate_presence_of(:code) } diff --git a/spec/models/logic_variable_spec.rb b/spec/models/logic_variable_spec.rb index 5c2f005b..e8d6b49f 100644 --- a/spec/models/logic_variable_spec.rb +++ b/spec/models/logic_variable_spec.rb @@ -11,7 +11,6 @@ it { is_expected.to have_many(:logic_variable_values).dependent(:destroy) } - it { is_expected.to validate_presence_of(:logic) } it { is_expected.to validate_presence_of(:variable) } it { is_expected.to validate_uniqueness_of(:variable).scoped_to(:logic_id) } diff --git a/spec/models/logic_variable_value_spec.rb b/spec/models/logic_variable_value_spec.rb index 04d67ddd..8a2cd31c 100644 --- a/spec/models/logic_variable_value_spec.rb +++ b/spec/models/logic_variable_value_spec.rb @@ -6,7 +6,6 @@ it { is_expected.to belong_to(:logic_variable) } - it { is_expected.to validate_presence_of(:logic_variable) } it { is_expected.to validate_presence_of(:seed) } it { is_expected.to validate_presence_of(:value) } diff --git a/spec/models/publication_spec.rb b/spec/models/publication_spec.rb index 9d1c0cfd..3640af62 100644 --- a/spec/models/publication_spec.rb +++ b/spec/models/publication_spec.rb @@ -6,7 +6,7 @@ it { is_expected.to belong_to(:publication_group) } it { is_expected.to belong_to(:publishable) } - it { is_expected.to belong_to(:license) } + it { is_expected.to belong_to(:license).optional } it { is_expected.to have_many(:authors).dependent(:destroy) } it { is_expected.to have_many(:copyright_holders).dependent(:destroy) } @@ -14,9 +14,6 @@ it { is_expected.to have_many(:sources).dependent(:destroy) } it { is_expected.to have_many(:derivations).dependent(:destroy) } - it { is_expected.to validate_presence_of(:publication_group) } - it { is_expected.to validate_presence_of(:publishable) } - it { is_expected.to validate_uniqueness_of(:uuid).case_insensitive } it { is_expected.to validate_uniqueness_of(:version).scoped_to(:publication_group_id) } @@ -96,7 +93,7 @@ it 'validates the publishable before publication' do expect(publication.reload.publishable).to receive(:before_publication) do publication.publishable.errors.add(:base, 'is invalid') - false + throw :abort end expect(publication.publish.save).to eq false expect(publication.errors.first).to eq [:exercise, 'is invalid'] diff --git a/spec/models/question_dependency_spec.rb b/spec/models/question_dependency_spec.rb index c2112c3f..a77e98eb 100644 --- a/spec/models/question_dependency_spec.rb +++ b/spec/models/question_dependency_spec.rb @@ -7,9 +7,6 @@ it { is_expected.to belong_to(:parent_question) } it { is_expected.to belong_to(:dependent_question) } - it { is_expected.to validate_presence_of(:parent_question) } - it { is_expected.to validate_presence_of(:dependent_question) } - it { is_expected.to validate_uniqueness_of(:dependent_question).scoped_to(:parent_question_id) } it 'requires both questions to belong to the same exercise' do diff --git a/spec/models/question_spec.rb b/spec/models/question_spec.rb index 723253cf..55b8ed26 100644 --- a/spec/models/question_spec.rb +++ b/spec/models/question_spec.rb @@ -12,6 +12,6 @@ it { is_expected.to have_many(:parent_dependencies).dependent(:destroy) } it { is_expected.to have_many(:child_dependencies).dependent(:destroy) } - it { is_expected.to validate_presence_of(:exercise) } + it { is_expected.to belong_to(:exercise) } end diff --git a/spec/models/stem_answer_spec.rb b/spec/models/stem_answer_spec.rb index f48243f7..fbd8b650 100644 --- a/spec/models/stem_answer_spec.rb +++ b/spec/models/stem_answer_spec.rb @@ -7,8 +7,6 @@ it { is_expected.to belong_to(:stem) } it { is_expected.to belong_to(:answer) } - it { is_expected.to validate_presence_of(:stem) } - it { is_expected.to validate_presence_of(:answer) } it { is_expected.to validate_presence_of(:correctness) } it { is_expected.to validate_uniqueness_of(:answer).scoped_to(:stem_id) } diff --git a/spec/models/stem_spec.rb b/spec/models/stem_spec.rb index 896a0ee1..2e0883a7 100644 --- a/spec/models/stem_spec.rb +++ b/spec/models/stem_spec.rb @@ -8,7 +8,6 @@ it { is_expected.to have_many(:combo_choices).dependent(:destroy) } - it { is_expected.to validate_presence_of(:question) } it { is_expected.to validate_presence_of(:content) } end diff --git a/spec/models/styling_spec.rb b/spec/models/styling_spec.rb index 2868daa2..2c6ac1c1 100644 --- a/spec/models/styling_spec.rb +++ b/spec/models/styling_spec.rb @@ -4,7 +4,6 @@ it { is_expected.to belong_to(:stylable) } - it { is_expected.to validate_presence_of(:stylable) } it { is_expected.to validate_presence_of(:style) } it { is_expected.to validate_inclusion_of(:style).in_array(Style.all) } diff --git a/spec/models/trusted_application_spec.rb b/spec/models/trusted_application_spec.rb index 790144dd..0a4e84ae 100644 --- a/spec/models/trusted_application_spec.rb +++ b/spec/models/trusted_application_spec.rb @@ -6,8 +6,6 @@ it { is_expected.to belong_to(:application) } - it { is_expected.to validate_presence_of(:application) } - it { is_expected.to validate_uniqueness_of(:application) } end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index f7f90883..f16862f3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -23,8 +23,6 @@ it { is_expected.to have_many(:direct_applications).dependent(:destroy) } - it { is_expected.to validate_presence_of(:account) } - it { is_expected.to validate_uniqueness_of(:account) } [ :username, :first_name, :last_name, :full_name, diff --git a/spec/models/vocab_distractor_spec.rb b/spec/models/vocab_distractor_spec.rb index 06d49db0..849ad0d9 100644 --- a/spec/models/vocab_distractor_spec.rb +++ b/spec/models/vocab_distractor_spec.rb @@ -5,8 +5,7 @@ it { is_expected.to belong_to(:vocab_term) } - it { is_expected.to validate_presence_of(:vocab_term) } - it { is_expected.to validate_presence_of(:distractor_publication_group) } + it { is_expected.to belong_to(:distractor_publication_group) } it { is_expected.to validate_presence_of(:distractor_term) } it do diff --git a/spec/models/vocab_term_spec.rb b/spec/models/vocab_term_spec.rb index 7c4a544d..01be958b 100644 --- a/spec/models/vocab_term_spec.rb +++ b/spec/models/vocab_term_spec.rb @@ -24,7 +24,7 @@ expect(vocab_term.errors).to be_empty vocab_term.definition = '' - vocab_term.before_publication + expect { vocab_term.before_publication }.to throw_symbol(:abort) expect(vocab_term.errors[:base]).to include('must have a definition') end @@ -39,7 +39,7 @@ expect(vocab_term.errors).to be_empty vocab_term.distractor_literals = [] - vocab_term.before_publication + expect { vocab_term.before_publication }.to throw_symbol(:abort) expect(vocab_term.errors[:base]).to include('must have at least 1 distractor') end diff --git a/spec/models/vocab_term_tag_spec.rb b/spec/models/vocab_term_tag_spec.rb index c6ae378f..ca727845 100644 --- a/spec/models/vocab_term_tag_spec.rb +++ b/spec/models/vocab_term_tag_spec.rb @@ -6,8 +6,5 @@ it { is_expected.to belong_to(:vocab_term) } it { is_expected.to belong_to(:tag) } - it { is_expected.to validate_presence_of(:vocab_term) } - it { is_expected.to validate_presence_of(:tag) } - it { is_expected.to validate_uniqueness_of(:tag).scoped_to(:vocab_term_id) } end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e2623a92..37ae970a 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -20,9 +20,6 @@ end end -# Remove after dropping support of Rails 4.2 -require "#{File.dirname(__FILE__)}/support/ruby_2_6_rails_4_2_patch" - # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end diff --git a/spec/routines/search_exercises_spec.rb b/spec/routines/search_exercises_spec.rb index 94549879..f2d53de3 100644 --- a/spec/routines/search_exercises_spec.rb +++ b/spec/routines/search_exercises_spec.rb @@ -6,14 +6,21 @@ 10.times { FactoryBot.create(:exercise, :published) } + ex = Exercise.arel_table + qu = Question.arel_table + st = Stem.arel_table + ans = Answer.arel_table + tested_strings = [ "%adipisci%", "%draft%" ] - Exercise.joins {questions.outer.stems.outer} - .joins {questions.outer.answers.outer} - .where {(title.like_any tested_strings) |\ - (stimulus.like_any tested_strings) |\ - (questions.stimulus.like_any tested_strings) |\ - (stems.content.like_any tested_strings) |\ - (answers.content.like_any tested_strings)}.delete_all + + ex_ids = Exercise.left_joins(questions: [:stems, :answers]).where( + ex[:title].matches_any(tested_strings) + .or(ex[:stimulus].matches_any(tested_strings)) + .or(qu[:stimulus].matches_any(tested_strings)) + .or(st[:content].matches_any(tested_strings)) + .or(ans[:content].matches_any(tested_strings))).pluck(:id) + + Exercise.where(id: ex_ids).delete_all @exercise_1 = Exercise.new Api::V1::Exercises::Representer.new(@exercise_1).from_hash( @@ -91,6 +98,7 @@ it "returns drafts that the user is allowed to see" do user = FactoryBot.create :user @exercise_draft.publication.authors << Author.new(user: user) + @exercise_draft.publication.copyright_holders << CopyrightHolder.new(user: user) @exercise_draft.reload result = described_class.call({q: 'content:draft'}, user: user.reload) expect(result.errors).to be_empty diff --git a/spec/routines/search_vocab_terms_spec.rb b/spec/routines/search_vocab_terms_spec.rb index 07242a94..f1ab9d55 100644 --- a/spec/routines/search_vocab_terms_spec.rb +++ b/spec/routines/search_vocab_terms_spec.rb @@ -7,8 +7,10 @@ 10.times { FactoryBot.create(:vocab_term, :published) } tested_strings = ["%lorem ipsu%", "%uia dolor sit ame%", "%adipiscing elit%", "draft"] - VocabTerm.where {(name.like_any tested_strings) | - (definition.like_any tested_strings)}.delete_all + + vt = VocabTerm.arel_table + VocabTerm.where(vt[:name].matches_any(tested_strings).or( + vt[:definition].matches_any(tested_strings))).delete_all @vocab_term_1 = FactoryBot.build(:vocab_term, :published) Api::V1::Vocabs::TermWithDistractorsAndExerciseIdsRepresenter.new(@vocab_term_1).from_hash( @@ -67,6 +69,7 @@ it "returns drafts that the user is allowed to see" do user = FactoryBot.create :user @vocab_term_draft.publication.authors << Author.new(user: user) + @vocab_term_draft.publication.copyright_holders << CopyrightHolder.new(user: user) @vocab_term_draft.reload result = described_class.call({q: 'content:draft'}, user: user) expect(result.errors).to be_empty diff --git a/spec/support/ruby_2_6_rails_4_2_patch.rb b/spec/support/ruby_2_6_rails_4_2_patch.rb deleted file mode 100644 index 1e1d9f98..00000000 --- a/spec/support/ruby_2_6_rails_4_2_patch.rb +++ /dev/null @@ -1,14 +0,0 @@ -if RUBY_VERSION >= '2.6.0' - if Rails::VERSION::MAJOR < 5 - class ActionController::TestResponse < ActionDispatch::TestResponse - def recycle! - # hack to avoid MonitorMixin double-initialize error: - @mon_mutex_owner_object_id = nil - @mon_mutex = nil - initialize - end - end - else - puts "Monkeypatch for ActionController::TestResponse no longer needed" - end -end