diff --git a/.gitignore b/.gitignore index 5fb66c9f0..a35e445cc 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ # Ignore master key for decrypting credentials and more. /config/master.key +Gemfile +Gemfile.lock diff --git a/Gemfile b/Gemfile index 7abd44d7a..5e25721a4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -ruby "3.2.3" +ruby "3.3.0" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "rails", "~> 7.1.2" @@ -66,3 +66,5 @@ group :test do gem "selenium-webdriver" gem "webdrivers" end + +gem 'bootstrap', '~> 5.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index ac898c77c..b9e794ce8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,11 +77,17 @@ GEM tzinfo (~> 2.0) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) + autoprefixer-rails (10.4.16.0) + execjs (~> 2) base64 (0.2.0) bigdecimal (3.1.6) bindex (0.8.1) bootsnap (1.17.1) msgpack (~> 1.2) + bootstrap (5.0.2) + autoprefixer-rails (>= 9.1.0) + popper_js (>= 2.9.2, < 3) + sassc-rails (>= 2.0.0) builder (3.2.4) capybara (3.39.2) addressable @@ -102,6 +108,8 @@ GEM drb (2.2.0) ruby2_keywords erubi (1.12.0) + execjs (2.9.1) + ffi (1.16.3) globalid (1.2.1) activesupport (>= 6.1) i18n (1.14.1) @@ -153,6 +161,7 @@ GEM racc (~> 1.4) nokogiri (1.16.0-x86_64-linux) racc (~> 1.4) + popper_js (2.11.8) psych (5.1.2) stringio public_suffix (5.0.4) @@ -205,6 +214,14 @@ GEM rexml (3.2.6) ruby2_keywords (0.0.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.10.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) @@ -226,6 +243,7 @@ GEM railties (>= 6.0.0) stringio (3.1.0) thor (1.3.0) + tilt (2.3.0) timeout (0.4.1) turbo-rails (1.5.0) actionpack (>= 6.0.0) @@ -260,6 +278,7 @@ PLATFORMS DEPENDENCIES bootsnap + bootstrap (~> 5.0.1) capybara debug importmap-rails @@ -276,7 +295,7 @@ DEPENDENCIES webdrivers RUBY VERSION - ruby 3.2.3p157 + ruby 3.3.0p0 BUNDLED WITH 2.5.5 diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss similarity index 88% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index 288b9ab71..c07201e93 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -13,3 +13,12 @@ *= require_tree . *= require_self */ + + + //Import Bootstrap styles + @import "bootstrap"; + + + body { + background-color: #333; + } \ No newline at end of file diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb new file mode 100644 index 000000000..75bc64925 --- /dev/null +++ b/app/controllers/articles_controller.rb @@ -0,0 +1,70 @@ +# The ArticlesController is responsible for managing the lifecycle of articles in the application. +# It handles the standard CRUD operations: create, read, update, and delete. + +class ArticlesController < ApplicationController + # The 'index' action is responsible for listing all articles in the application. + def index + @articles = Article.search(params[:term]) + end + + # The 'show' action retrieves a single article by its ID and displays it. + def show + @article = Article.find(params[:id]) + end + + # The `new` action initializes a new, unsaved article for the form. + def new + @article = Article.new + end + + # The `create` action attempts to save a new article to the database from the form data. + def create + @article = Article.new(article_params) + + # Set the article's date to the current time if it is not provided. This is to ensure that the article's date is always accurate. +if @article.date.nil? + @article.date = Time.current + end + + # If the article is successfully saved, redirect to the article's page. Otherwise, render the `new` template with the provided errors. + if @article.save + redirect_to articles_path, notice: "Article was successfully created." + else + render :new, status: :unprocessable_entity + end + end + + # The 'edit' action retrieves an existing article by its ID for the form. + def edit + @article = Article.find(params[:id]) + end + + # The `update` action attempts to save changes to an existing article to the database from the form data. + def update + @article = Article.find(params[:id]) + + # Set the article's date to the current time if it is not provided. This is to ensure that the article's date is always accurate. + if @article.update(article_params) + @article.update(date: Time.current) + # If the article is successfully saved, redirect to the article's page. Otherwise, render the `edit` template with the provided errors. + redirect_to @article, notice: "Article was successfully updated." + else + render :edit, status: :unprocessable_entity + end + end + + # The `destroy` action deletes an existing article by its ID from the database. + def destroy + @article = Article.find(params[:id]) + @article.destroy + + redirect_to root_path, status: :see_other + end + + private + + # Strong Parameters: only allow the whitelisted parameters to be used to create/update an article. + def article_params + params.require(:article).permit(:title, :content, :author, :date) + end +end diff --git a/app/helpers/articles_helper.rb b/app/helpers/articles_helper.rb new file mode 100644 index 000000000..296827759 --- /dev/null +++ b/app/helpers/articles_helper.rb @@ -0,0 +1,2 @@ +module ArticlesHelper +end diff --git a/app/javascript/application.js b/app/javascript/application.js index 0d7b49404..ecb58706c 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,3 +1,7 @@ +//= require bootstrap + +// Added bootstrap to style + // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails -import "@hotwired/turbo-rails" -import "controllers" +import "@hotwired/turbo-rails"; +import "controllers"; diff --git a/app/models/article.rb b/app/models/article.rb new file mode 100644 index 000000000..9cfd53f59 --- /dev/null +++ b/app/models/article.rb @@ -0,0 +1,15 @@ +class Article < ApplicationRecord + # Validate rules checking to see the presence of the title and content. +validates :title, :content, presence: true + +# The `search` class method allows for searching articles by title, content, or author. +# If a search term is provided, it returns all articles where the title, content, or author matches the term. +# If no term is provided, it returns all articles. +def self.search(term) + if term + where('title LIKE ? OR content LIKE ? OR author LIKE ?' , "%#{term}%", "%#{term}%", "%#{term}%") + else + all + end +end +end diff --git a/app/views/articles/_form.html.erb b/app/views/articles/_form.html.erb new file mode 100644 index 000000000..590cbc113 --- /dev/null +++ b/app/views/articles/_form.html.erb @@ -0,0 +1,45 @@ +<%# This is a form partial for articles. It's used in the new and edit views. %> + +
<%= article.content %>
+ <%# Link to the article's show page with a 'Read' button %> + <%= link_to 'Read', article, class: 'btn btn-primary' %> + <%# Link to the article's edit page with an 'Edit' button %> + <%= link_to 'Edit', edit_article_path(article), class: 'btn btn-primary' %> + <%# Link to delete the article with a 'Delete' button. + This will trigger a DELETE HTTP request to /articles/:id. + A confirmation dialog will be displayed before the article is deleted. %> + <%= link_to 'Delete', article_path(article), data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'btn btn-danger' %> +Publishing Date: <%= @article.date %>
+ <%# Display the content of the article %> +<%= @article.content %>
+ <%= link_to 'Edit', edit_article_path(@article), class: 'btn btn-primary' %> + <%# Display a link to delete the article. This will trigger a DELETE HTTP request to /articles/:id. + A confirmation dialog will be displayed before the article is deleted. %> + <%= link_to 'Delete', article_path(@article), data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'btn btn-danger' %> + <%# Display a link to the articles index page %> + <%= link_to 'Back', articles_path, class: 'btn btn-secondary' %> +