diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..c99d2e73 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/Gemfile b/Gemfile index ce35e710..4458447c 100644 --- a/Gemfile +++ b/Gemfile @@ -16,8 +16,6 @@ gem 'image_processing', '~> 1.2' gem 'jbuilder' # Build JSON APIs with ease [https://github.com/rails/jbuilder] gem 'jsbundling-rails' # Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails] gem 'mini_magick', '~> 4.12' -# Motor Admin allows you to deploy a no-code admin panel for your application in less than a minute -gem 'motor-admin', '~> 0.4.7' gem 'pg', '~> 1.1' # Use postgresql as the database for Active Record gem 'premailer-rails', '~> 1.12' # This gem is a drop in solution for styling HTML emails with CSS gem 'puma', '~> 5.0' # Use the Puma web server [https://github.com/puma/puma] @@ -66,7 +64,11 @@ group :test do # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] gem 'capybara' gem 'faker', '~> 3.1' + gem 'rspec-rails', '~> 6.1' gem 'selenium-webdriver' gem 'simplecov', require: false # Code coverage analysis tool for ruby gem 'webdrivers' end + +gem 'rails_admin', '~> 3.1' +gem 'sassc-rails' diff --git a/Gemfile.lock b/Gemfile.lock index cd1df74a..d01e491d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,6 +56,10 @@ GEM globalid (>= 0.3.6) activemodel (7.0.3) activesupport (= 7.0.3) + activemodel-serializers-xml (1.0.2) + activemodel (> 5.x) + activesupport (> 5.x) + builder (~> 3.1) activerecord (7.0.3) activemodel (= 7.0.3) activesupport (= 7.0.3) @@ -75,11 +79,7 @@ GEM public_suffix (>= 2.0.2, < 5.0) airbrussh (1.4.1) sshkit (>= 1.6.1, != 1.7.0) - ar_lazy_preload (1.1.2) - rails (>= 5.2) ast (2.4.2) - audited (5.3.2) - activerecord (>= 5.0, < 7.1) aws-eventstream (1.2.0) aws-partitions (1.721.0) aws-sdk-core (3.170.0) @@ -149,19 +149,15 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) + diff-lcs (1.5.1) digest (3.1.0) docile (1.4.0) dockerfile-rails (1.2.5) rails erubi (1.10.0) - et-orbi (1.2.7) - tzinfo faker (3.1.0) i18n (>= 1.8.11, < 2) ffi (1.15.5) - fugit (1.8.1) - et-orbi (~> 1, >= 1.2.7) - raabro (~> 1.4) globalid (1.0.0) activesupport (>= 5.0) htmlentities (4.3.4) @@ -180,6 +176,18 @@ GEM jsbundling-rails (1.0.2) railties (>= 6.0.0) json (2.6.3) + kaminari (1.2.2) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) + actionview + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) launchy (2.5.0) addressable (~> 2.7) letter_opener (1.8.1) @@ -195,13 +203,8 @@ GEM mini_magick (4.12.0) mini_mime (1.1.2) minitest (5.15.0) - motor-admin (0.4.7) - ar_lazy_preload (~> 1.0) - audited (~> 5.0) - cancancan (~> 3.0) - fugit (~> 1.0) - rails (>= 5.2) msgpack (1.5.2) + nested_form (0.3.2) net-imap (0.2.3) digest net-protocol @@ -242,7 +245,6 @@ GEM public_suffix (4.0.7) puma (5.6.4) nio4r (~> 2.0) - raabro (1.4.0) racc (1.6.0) rack (2.2.3.1) rack-test (1.1.0) @@ -266,6 +268,12 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.4.2) loofah (~> 2.3) + rails_admin (3.1.2) + activemodel-serializers-xml (>= 1.0) + kaminari (>= 0.14, < 2.0) + nested_form (~> 0.3) + rails (>= 6.0, < 8) + turbo-rails (~> 1.0) railties (7.0.3) actionpack (= 7.0.3) activesupport (= 7.0.3) @@ -283,6 +291,23 @@ GEM actionpack (>= 5.0) railties (>= 5.0) rexml (3.2.5) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (6.1.2) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.1) rubocop (1.51.0) json (~> 2.3) parallel (~> 1.10) @@ -307,6 +332,14 @@ GEM ffi (~> 1.12) ruby_http_client (3.5.5) rubyzip (2.3.2) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt selenium-webdriver (4.2.0) childprocess (>= 0.5, < 5.0) rexml (~> 3.2, >= 3.2.5) @@ -346,6 +379,7 @@ GEM railties (>= 6.0.0) strscan (3.0.3) thor (1.2.1) + tilt (2.3.0) timeout (0.3.0) turbo-rails (1.1.1) actionpack (>= 6.0.0) @@ -401,15 +435,17 @@ DEPENDENCIES jsbundling-rails letter_opener mini_magick (~> 4.12) - motor-admin (~> 0.4.7) pg (~> 1.1) premailer-rails (~> 1.12) puma (~> 5.0) rails (~> 7.0.3) + rails_admin (~> 3.1) redis (~> 4.0) + rspec-rails (~> 6.1) rubocop (~> 1.51.0) rubocop-performance (~> 1.18) rubocop-rails (~> 2.19.1) + sassc-rails selenium-webdriver sendgrid-actionmailer (~> 3.2) simple_form (~> 5.1) diff --git a/app/assets/images/learningmaterialsthumbnail.jpeg b/app/assets/images/learningmaterialsthumbnail.jpeg new file mode 100644 index 00000000..a672a2f1 Binary files /dev/null and b/app/assets/images/learningmaterialsthumbnail.jpeg differ diff --git a/app/assets/stylesheets/mailgun_mails.css b/app/assets/stylesheets/mailgun_mails.css index 11f9a3d5..c828fda3 100644 --- a/app/assets/stylesheets/mailgun_mails.css +++ b/app/assets/stylesheets/mailgun_mails.css @@ -9,6 +9,15 @@ font-size: 14px; } +/* Add CSS variable for repetitive colors */ + +:root { + --red: #D0021B; + --lightblue: #348eda; + --orange: #ff9f00; + --lightgreen: #68B90F; + } + img { max-width: 100%; } @@ -136,15 +145,15 @@ p li, ul li, ol li { LINKS & BUTTONS ------------------------------------- */ a { - color: #348eda; + color: var(--lightblue); text-decoration: underline; } .btn-primary { text-decoration: none; color: #FFF; - background-color: #348eda; - border: solid #348eda; + background-color: var(--lightblue); + border: solid var(--lightblue); border-width: 10px 20px; line-height: 2em; /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ @@ -160,9 +169,9 @@ a { .btn-warning { text-decoration: none; color: #FFF; - background-color: #ff9f00; - border: solid #ff9f00; - border-color: #ff9f00; + background-color: var(--orange); + border: solid var(--orange); + border-color: var(--orange); border-width: 10px 20px; line-height: 2em; /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ @@ -178,9 +187,9 @@ a { .btn-success { text-decoration: none; color: #FFF; - background-color: #68B90F; - border: solid #68B90F; - border-color: #68B90F; + background-color: var(--lightgreen); + border: solid var(--lightgreen); + border-color: var(--lightgreen); border-width: 10px 20px; line-height: 2em; /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ @@ -196,9 +205,9 @@ a { .btn-danger { text-decoration: none; color: #FFF; - background-color: #D0021B; - border: solid #D0021B; - border-color: #D0021B; + background-color: var(--red); + border: solid var(--red); + border-color: var(--red); border-width: 10px 20px; line-height: 2em; /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ @@ -257,13 +266,13 @@ a { font-size: 16px; } .alert.alert-warning { - background-color: #FF9F00; + background-color: var(--orange); } .alert.alert-bad { - background-color: #D0021B; + background-color: var(--red); } .alert.alert-good { - background-color: #68B90F; + background-color: var(--lightgreen); } /* ------------------------------------- diff --git a/app/controllers/learning_materials_controller.rb b/app/controllers/learning_materials_controller.rb new file mode 100644 index 00000000..7b1912a0 --- /dev/null +++ b/app/controllers/learning_materials_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class LearningMaterialsController < ApplicationController + skip_before_action :authenticate_user!, only: %i[index] + + def index + @learning_materials = LearningMaterial.includes(:thumbnail_blob).all + @learning_materials_with_thumbnails = @learning_materials.select do |learning_material| + learning_material.thumbnail.attached? + end + @random_learning_materials = @learning_materials_with_thumbnails.sample(2) + @learning_materials_with_or_without_thumbnails = @learning_materials - @random_learning_materials + end +end diff --git a/app/helpers/learning_materials_helper.rb b/app/helpers/learning_materials_helper.rb new file mode 100644 index 00000000..7bef30b0 --- /dev/null +++ b/app/helpers/learning_materials_helper.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module LearningMaterialsHelper +end diff --git a/app/models/learning_material.rb b/app/models/learning_material.rb new file mode 100644 index 00000000..25e8fe7f --- /dev/null +++ b/app/models/learning_material.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class LearningMaterial < ApplicationRecord + # Associations + belongs_to :user + has_one_attached :thumbnail + + # Enum + enum tag: { beginners: 0, intermediates: 1, experts: 2 } + + # Validations + validates :tag, :learning_material_title, :learning_material_description, :learning_material_link, presence: true + validates :learning_material_link, + format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: :invalid_url } +end diff --git a/app/models/motor/ability.rb b/app/models/motor/ability.rb deleted file mode 100644 index 6596b366..00000000 --- a/app/models/motor/ability.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -## -# CanCan permissions for motor_admin -module Motor - class Ability - include CanCan::Ability - - def initialize(user, _request) - case user.role - when 'organization_admin' - can :manage, :all - end - end - end -end diff --git a/app/models/user.rb b/app/models/user.rb index 614c5056..5ed48e99 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,6 +10,7 @@ class User < ApplicationRecord # Associations has_many :users_chapters, dependent: :nullify has_many :chapters, through: :users_chapters + has_many :learning_materials, dependent: :nullify # Callbacks before_create :set_defaults # Set model defaults before create diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index 1b99ece6..14cf70de 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -21,7 +21,7 @@
  • <%= link_to 'Chapters', chapters_path %>
  • -
  • <%= link_to 'Learning materials', landing_learn_path %>
  • +
  • <%= link_to 'Learning materials', learning_materials_path %>
  • <% if FeatureFlag.find_by(name: 'projects').try(:enabled) %>
  • <%= link_to 'Projects', '#' %>
  • diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d074c1bf..7968e3f9 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -44,7 +44,7 @@
  • Projects
  • <% end %> -
  • <%= link_to 'Learning Materials', landing_learn_path %>
  • +
  • <%= link_to 'Learning Materials', learning_materials_path %>
  • <% if user_signed_in? %>
  • <%= button_to "Sign out", destroy_user_session_path, method: :delete %>
  • @@ -85,7 +85,7 @@ <% end %> - <%= link_to 'Learning Materials', landing_learn_path, + <%= link_to 'Learning Materials', learning_materials_path, class: 'text-sm font-small text-gray-500 hover:text-red-600' %> <% if user_signed_in? %> diff --git a/app/views/learning_materials/index.html.erb b/app/views/learning_materials/index.html.erb new file mode 100644 index 00000000..252ff041 --- /dev/null +++ b/app/views/learning_materials/index.html.erb @@ -0,0 +1,94 @@ +<% content_for(:title,"Learning Materials") %> +<% content_for(:description,"A list of ruby and ruby on rails learning resources compiled by the ARC community") %> + +
    +
    +

    Featured Materials

    +
    + +
    + <% if @learning_materials.empty? %> +

    There are no created learning materials yet

    + <% else %> + <% # Display the selected random records with thumbnails %> + <% @random_learning_materials.each do |learning_material| %> +
    + +
    +
    + +
    +
    + <%= learning_material.created_at.strftime("%b %d") %> +
    +
    +
    +

    <%= learning_material.learning_material_title %>

    +

    <%= learning_material.tag %>

    +
    +
    +

    <%= learning_material.learning_material_description %>

    +
    +
    + + <%= link_to "READ MORE HERE", learning_material.learning_material_link, class: "learning_materialslink ml-1", target: "_blank", style: "color: #D0021B;" %> + +
    +
    + <% end %> + <% end %> +
    + +
    +

    Other Resources

    +
    + + <% # Display the remaining records regardless of whether it has thumbnail or not %> +
    + <% @learning_materials_with_or_without_thumbnails.each do |learning_material| %> +
    + +
    + <% if learning_material.thumbnail.attached? %> +
    + +
    + <% else %> +
    + +
    + <% end %> +
    + <%= learning_material.created_at.strftime("%b %d") %> +
    +
    +
    +

    <%= learning_material.learning_material_title %>

    +

    <%= learning_material.tag %>

    +
    +
    +

    <%= learning_material.learning_material_description %>

    +
    +
    + + <%= link_to "READ MORE HERE", learning_material.learning_material_link, class: "learningmaterialslink ml-1", target: "_blank", style: "color: #D0021B;" %> + +
    +
    + <% end %> +
    + +
    +

    If you may not want the view above, here is a list of the same details shown above.

    +
    + +
      + <% @learning_materials.each do |learning_material| %> +
    1. + + <%= link_to learning_material.learning_material_title, learning_material.learning_material_link, class: "learningmaterialslink ml-1", target: "_blank", style: "color: #D0021B;" %> - <%= learning_material.learning_material_description %> + +
    2. + <% end %> +
    +
    \ No newline at end of file diff --git a/config/initializers/rails_admin.rb b/config/initializers/rails_admin.rb new file mode 100644 index 00000000..9283c387 --- /dev/null +++ b/config/initializers/rails_admin.rb @@ -0,0 +1,50 @@ +RailsAdmin.config do |config| + config.asset_source = :sprockets + + ### Popular gems integration + + ## == Devise == + # config.authenticate_with do + # warden.authenticate! scope: :user + # end + # config.current_user_method(&:current_user) + + ## == CancanCan == + # config.authorize_with :cancancan + + ## == Pundit == + # config.authorize_with :pundit + + ## == PaperTrail == + # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0 + + ### More at https://github.com/railsadminteam/rails_admin/wiki/Base-configuration + + ## == Gravatar integration == + ## To disable Gravatar integration in Navigation Bar set to false + # config.show_gravatar = true + + config.actions do + dashboard # mandatory + index # mandatory + new + export + bulk_delete + show + edit + delete + show_in_app + + ## With an audit adapter, you can add: + # history_index + # history_show + end + + # Configuring rails admin to use cancancan for authorization + config.authorize_with :cancancan + config.authorize_with do + unless current_user && current_user.organization_admin? + redirect_to main_app.root_path, alert: 'You are not authorized to access this page.' + end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index bb6f3755..c7fba7de 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -29,4 +29,8 @@ en: destroy: success: 'Project was successfully destroyed.' + errors: + messages: + invalid_url: "must be a valid URL" + diff --git a/config/motor.yml b/config/motor.yml deleted file mode 100644 index 53e043d0..00000000 --- a/config/motor.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -engine_version: 0.4.7 -file_version: 2023-03-09 16:16:17.698355000 Z -resources: -- name: chapter - preferences: - columns: - - column_type: richtext - name: description - updated_at: 2023-03-05 09:31:13.962854000 +00:00 -- name: feature_flag - preferences: - columns: - - column_type: richtext - name: description - updated_at: 2023-03-09 16:16:17.698355000 +00:00 -configs: -- key: header.links - value: - - name: Reports - link_type: reports - - name: Forms - link_type: forms - - conditions: [] - type: header - name: Exit admin portal - path: "/" - link_type: header - updated_at: 2023-03-05 09:37:51.799389000 +00:00 -queries: [] -dashboards: [] -forms: [] -alerts: [] -api_configs: -- id: 1 - name: origin - url: "/" - preferences: {} - description: - updated_at: 2023-03-05 08:41:14.200426000 +00:00 diff --git a/config/routes.rb b/config/routes.rb index 0a04f5e9..846be344 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,12 +1,11 @@ # frozen_string_literal: true Rails.application.routes.draw do - authenticate :user do - mount Motor::Admin => '/admin' - end + mount RailsAdmin::Engine => '/admin', as: 'rails_admin' resources :projects, only: %i[index show] resources :chapters, only: %i[index show] resources :countries, only: %i[index show] + resources :learning_materials, only: %i[index show] devise_for :users, controllers: { registrations: 'users/registrations' # Override devise registration controller } @@ -16,5 +15,4 @@ root 'landing#index' get 'about_us', to: 'landing#about', as: :landing_about - get 'learn', to: 'landing#learn', as: :landing_learn end diff --git a/db/migrate/20230305084113_install_motor_admin.rb b/db/migrate/20230305084113_install_motor_admin.rb deleted file mode 100644 index 97821239..00000000 --- a/db/migrate/20230305084113_install_motor_admin.rb +++ /dev/null @@ -1,268 +0,0 @@ -class InstallMotorAdmin < ActiveRecord::Migration[7.0] - def self.up - create_table :motor_queries do |t| - t.column :name, :string, null: false - t.column :description, :text - t.column :sql_body, :text, null: false - t.column :preferences, :text, null: false - t.column :author_id, :bigint - t.column :author_type, :string - t.column :deleted_at, :datetime - - t.timestamps - - t.index :updated_at - t.index 'name', - name: 'motor_queries_name_unique_index', - unique: true, - where: 'deleted_at IS NULL' - end - - create_table :motor_dashboards do |t| - t.column :title, :string, null: false - t.column :description, :text - t.column :preferences, :text, null: false - t.column :author_id, :bigint - t.column :author_type, :string - t.column :deleted_at, :datetime - - t.timestamps - - t.index :updated_at - t.index 'title', - name: 'motor_dashboards_title_unique_index', - unique: true, - where: 'deleted_at IS NULL' - end - - create_table :motor_forms do |t| - t.column :name, :string, null: false - t.column :description, :text - t.column :api_path, :text, null: false - t.column :http_method, :string, null: false - t.column :preferences, :text, null: false - t.column :author_id, :bigint - t.column :author_type, :string - t.column :deleted_at, :datetime - t.column :api_config_name, :string, null: false - - t.timestamps - - t.index :updated_at - t.index 'name', - name: 'motor_forms_name_unique_index', - unique: true, - where: 'deleted_at IS NULL' - end - - create_table :motor_resources do |t| - t.column :name, :string, null: false, index: { unique: true } - t.column :preferences, :text, null: false - - t.timestamps - - t.index :updated_at - end - - create_table :motor_configs do |t| - t.column :key, :string, null: false, index: { unique: true } - t.column :value, :text, null: false - - t.timestamps - - t.index :updated_at - end - - create_table :motor_alerts do |t| - t.references :query, null: false, foreign_key: { to_table: :motor_queries }, index: true - t.column :name, :string, null: false - t.column :description, :text - t.column :to_emails, :text, null: false - t.column :is_enabled, :boolean, null: false, default: true - t.column :preferences, :text, null: false - t.column :author_id, :bigint - t.column :author_type, :string - t.column :deleted_at, :datetime - - t.timestamps - - t.index :updated_at - t.index 'name', - name: 'motor_alerts_name_unique_index', - unique: true, - where: 'deleted_at IS NULL' - end - - create_table :motor_alert_locks do |t| - t.references :alert, null: false, foreign_key: { to_table: :motor_alerts } - t.column :lock_timestamp, :string, null: false - - t.timestamps - - t.index %i[alert_id lock_timestamp], unique: true - end - - create_table :motor_tags do |t| - t.column :name, :string, null: false - - t.timestamps - - t.index 'name', - name: 'motor_tags_name_unique_index', - unique: true - end - - create_table :motor_taggable_tags do |t| - t.references :tag, null: false, foreign_key: { to_table: :motor_tags }, index: true - t.column :taggable_id, :bigint, null: false - t.column :taggable_type, :string, null: false - - t.index %i[taggable_id taggable_type tag_id], - name: 'motor_polymorphic_association_tag_index', - unique: true - end - - create_table :motor_audits do |t| - t.column :auditable_id, :string - t.column :auditable_type, :string - t.column :associated_id, :string - t.column :associated_type, :string - t.column :user_id, :bigint - t.column :user_type, :string - t.column :username, :string - t.column :action, :string - t.column :audited_changes, :text - t.column :version, :bigint, default: 0 - t.column :comment, :text - t.column :remote_address, :string - t.column :request_uuid, :string - t.column :created_at, :datetime - end - - create_table :motor_api_configs do |t| - t.column :name, :string, null: false - t.column :url, :string, null: false - t.column :preferences, :text, null: false - t.column :credentials, :text, null: false - t.column :description, :text - t.column :deleted_at, :datetime - - t.timestamps - - t.index 'name', - name: 'motor_api_configs_name_unique_index', - unique: true, - where: 'deleted_at IS NULL' - end - - create_table :motor_notes do |t| - t.column :body, :text - t.column :author_id, :bigint - t.column :author_type, :string - t.column :record_id, :string, null: false - t.column :record_type, :string, null: false - t.column :deleted_at, :datetime - - t.timestamps - - t.index %i[author_id author_type], - name: 'motor_notes_author_id_author_type_index' - end - - create_table :motor_note_tags do |t| - t.column :name, :string, null: false - - t.timestamps - - t.index 'name', - name: 'motor_note_tags_name_unique_index', - unique: true - end - - create_table :motor_note_tag_tags do |t| - t.references :tag, null: false, foreign_key: { to_table: :motor_note_tags }, index: true - t.references :note, null: false, foreign_key: { to_table: :motor_notes }, index: false - - t.index %i[note_id tag_id], - name: 'motor_note_tags_note_id_tag_id_index', - unique: true - end - - create_table :motor_reminders do |t| - t.column :author_id, :bigint, null: false - t.column :author_type, :string, null: false - t.column :recipient_id, :bigint, null: false - t.column :recipient_type, :string, null: false - t.column :record_id, :string - t.column :record_type, :string - t.column :scheduled_at, :datetime, null: false, index: true - - t.timestamps - - t.index %i[author_id author_type], - name: 'motor_reminders_author_id_author_type_index' - - t.index %i[recipient_id recipient_type], - name: 'motor_reminders_recipient_id_recipient_type_index' - - t.index %i[record_id record_type], - name: 'motor_reminders_record_id_record_type_index' - end - - create_table :motor_notifications do |t| - t.column :title, :string, null: false - t.column :description, :text - t.column :recipient_id, :bigint, null: false - t.column :recipient_type, :string, null: false - t.column :record_id, :string - t.column :record_type, :string - t.column :status, :string, null: false - - t.timestamps - - t.index %i[recipient_id recipient_type], - name: 'motor_notifications_recipient_id_recipient_type_index' - - t.index %i[record_id record_type], - name: 'motor_notifications_record_id_record_type_index' - end - - add_index :motor_audits, %i[auditable_type auditable_id version], name: 'motor_auditable_index' - add_index :motor_audits, %i[associated_type associated_id], name: 'motor_auditable_associated_index' - add_index :motor_audits, %i[user_id user_type], name: 'motor_auditable_user_index' - add_index :motor_audits, :request_uuid - add_index :motor_audits, :created_at - - model = Class.new(ApplicationRecord) - - model.table_name = 'motor_configs' - - model.create!(key: 'header.links', value: [{ - name: '⭐ Star on GitHub', - path: 'https://github.com/motor-admin/motor-admin-rails' - }].to_json) - - model.table_name = 'motor_api_configs' - - model.create!(name: 'origin', url: '/', preferences: {}, credentials: {}) - end - - def self.down - drop_table :motor_audits - drop_table :motor_alert_locks - drop_table :motor_alerts - drop_table :motor_forms - drop_table :motor_taggable_tags - drop_table :motor_tags - drop_table :motor_resources - drop_table :motor_configs - drop_table :motor_queries - drop_table :motor_dashboards - drop_table :motor_api_configs - drop_table :motor_note_tag_tags - drop_table :motor_note_tags - drop_table :motor_notes - drop_table :motor_notifications - drop_table :motor_reminders - end -end diff --git a/db/migrate/20240416055500_create_learning_materials.rb b/db/migrate/20240416055500_create_learning_materials.rb new file mode 100644 index 00000000..2d2c8054 --- /dev/null +++ b/db/migrate/20240416055500_create_learning_materials.rb @@ -0,0 +1,14 @@ +class CreateLearningMaterials < ActiveRecord::Migration[7.0] + def change + create_table :learning_materials do |t| + t.references :user, null: false, foreign_key: true + t.string :thumbnail + t.integer :tag + t.string :learning_material_title + t.text :learning_material_description + t.string :learning_material_link + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 0f6705eb..d014ce41 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_05_31_154135) do +ActiveRecord::Schema[7.0].define(version: 2024_04_16_055500) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -67,6 +67,18 @@ t.datetime "updated_at", null: false end + create_table "learning_materials", force: :cascade do |t| + t.bigint "user_id", null: false + t.string "thumbnail" + t.integer "tag" + t.string "learning_material_title" + t.text "learning_material_description" + t.string "learning_material_link" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_learning_materials_on_user_id" + end + create_table "motor_alert_locks", force: :cascade do |t| t.bigint "alert_id", null: false t.string "lock_timestamp", null: false @@ -313,6 +325,7 @@ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" + add_foreign_key "learning_materials", "users" add_foreign_key "motor_alert_locks", "motor_alerts", column: "alert_id" add_foreign_key "motor_alerts", "motor_queries", column: "query_id" add_foreign_key "motor_note_tag_tags", "motor_note_tags", column: "tag_id" diff --git a/spec/features/learning_materials_spec.rb b/spec/features/learning_materials_spec.rb new file mode 100644 index 00000000..5f6fc713 --- /dev/null +++ b/spec/features/learning_materials_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.feature 'LearningMaterials', type: :feature do + before :each do + visit learning_materials_path + end + + it 'Displays the two main page headers' do + visit learning_materials_path + expect(page).to have_content('Featured Materials') + expect(page).to have_content('Other Resources') + end + + it 'Display default text if there are no learning materials found' do + @learningmaterials = [] + expect(page).to have_content('There are no created learning materials yet') + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 00000000..c616c677 --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require_relative '../config/environment' +# Prevent database truncation if the environment is production +abort('The Rails environment is running in production mode!') if Rails.env.production? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# 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 +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Rails.root.glob('spec/support/**/*.rb').sort.each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + abort e.to_s.strip +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = Rails.root.join('spec/fixtures') + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, type: :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://rspec.info/features/6-0/rspec-rails + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..409c64b6 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows you to limit a spec run to individual examples or groups + # # you care about by tagging them with `:focus` metadata. When nothing + # # is tagged with `:focus`, all examples get run. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + # config.disable_monkey_patching! + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed +end diff --git a/test/controllers/learning_materials_controller_test.rb b/test/controllers/learning_materials_controller_test.rb new file mode 100644 index 00000000..037865c8 --- /dev/null +++ b/test/controllers/learning_materials_controller_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class LearningMaterialsControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/fixtures/learning_materials.yml b/test/fixtures/learning_materials.yml new file mode 100644 index 00000000..15fcfff2 --- /dev/null +++ b/test/fixtures/learning_materials.yml @@ -0,0 +1,17 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user: one + thumbnail: MyString + tag: 1 + learning_material_title: MyString + learning_material_description: MyText + learning_material_link: MyString + +two: + user: two + thumbnail: MyString + tag: 1 + learning_material_title: MyString + learning_material_description: MyText + learning_material_link: MyString diff --git a/test/models/learning_material_test.rb b/test/models/learning_material_test.rb new file mode 100644 index 00000000..84352f52 --- /dev/null +++ b/test/models/learning_material_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class LearningMaterialTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end