Skip to content

Commit

Permalink
Implement CRUD actions and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sparshalc committed Jan 30, 2024
1 parent cbbe47a commit f372c3b
Show file tree
Hide file tree
Showing 23 changed files with 249 additions and 9 deletions.
2 changes: 1 addition & 1 deletion app/assets/config/manifest.js
@@ -1,4 +1,4 @@
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js
//= link_tree ../builds
2 changes: 2 additions & 0 deletions app/assets/stylesheets/application.bootstrap.scss
@@ -0,0 +1,2 @@
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
69 changes: 69 additions & 0 deletions app/controllers/articles_controller.rb
@@ -0,0 +1,69 @@
class ArticlesController < ApplicationController
# Ensures that the set_article method is called before the specified actions
before_action :set_article, only: %i[show edit update destroy]

# Displays a list of articles, filtered by title if a query parameter is present
def index
if params[:query].present?
@articles = Article.where("title LIKE ?", params[:query])
else
@articles = Article.all
end
end

# Displays the details of a specific article
def show
end

# Initializes a new article instance for creation
def new
@article = Article.new
end

# Renders the form for editing an existing article
def edit
end

# Creates a new article based on parameters, and renders turbo_stream if successful
def create
@article = Article.new(article_params)
if @article.save
respond_to do |format|
format.turbo_stream
end
else
render :new, status: :unprocessable_entity
end
end

# Updates an existing article with the provided parameters, redirects on success
def update
if @article.update(article_params)
respond_to do |format|
format.html { redirect_to article_url(@article), notice: "Article was successfully updated." }
end
else
format.html { render :edit, status: :unprocessable_entity }
end
end

# Destroys the specified article instance, rendering turbo_stream
def destroy
@article.destroy!

respond_to do |format|
format.turbo_stream
end
end

private
# Sets the @article instance variable based on the provided ID parameter
def set_article
@article = Article.find(params[:id])
end

# Defines the permitted parameters for article creation and updates
def article_params
params.require(:article).permit(:title, :content, :author, :date)
end
end
9 changes: 9 additions & 0 deletions app/helpers/articles_helper.rb
@@ -0,0 +1,9 @@
module ArticlesHelper
def display_errors_for_field(object, field)
if object.errors.any?
object.errors.full_messages_for(field).each do |message|
concat content_tag(:div, message, class: 'text-danger')
end
end
end
end
1 change: 1 addition & 0 deletions app/javascript/application.js
@@ -1,3 +1,4 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"
import * as bootstrap from "bootstrap"
10 changes: 10 additions & 0 deletions app/models/article.rb
@@ -0,0 +1,10 @@
class Article < ApplicationRecord
# Validates presence of title and content fields
validates :title, presence: true
validates :content, presence: true

# Defines a class method to search for articles based on a query
def self.search(query)
where("title LIKE :query OR content LIKE :query", query: "%#{query}%")
end
end
28 changes: 28 additions & 0 deletions app/views/articles/_article.html.erb
@@ -0,0 +1,28 @@
<%= turbo_frame_tag article do %>
<div class="card p-4 mt-2">
<p>
<strong>Title:</strong>
<%= link_to article.title, article, data: { turbo_frame: '_top'} %>
</p>

<p>
<strong>Content:</strong>
<%= article.content %>
</p>

<p>
<strong>Author:</strong>
<%= article.author %>
</p>

<p>
<strong>Date:</strong>
<%= article.date %>
</p>

<div class="d-flex gap-2">
<%= link_to 'Edit', edit_article_path(article), class: 'btn btn-success w-100' %>
<%= button_to 'Delete', article_path(article), method: :delete, class: 'btn btn-danger'%>
</div>
</div>
<% end %>
30 changes: 30 additions & 0 deletions app/views/articles/_form.html.erb
@@ -0,0 +1,30 @@
<div class="card p-4 mt-1">
<%= form_with(model: article) do |form| %>

<div>
<%= form.label :title, style: "display: block" %>
<%= form.text_field :title, class: 'form-control' %>
<% display_errors_for_field(@article, :title) %>
</div>

<div>
<%= form.label :content, style: "display: block" %>
<%= form.text_field :content, class: 'form-control' %>
<% display_errors_for_field(@article, :content) %>
</div>

<div>
<%= form.label :author, style: "display: block" %>
<%= form.text_field :author, class: 'form-control' %>
</div>

<div>
<%= form.hidden_field :date, class: 'form-control', value: Time.now %>
</div>

<div class="d-flex gap-2">
<%= form.submit class: 'btn btn-success mt-2 w-100' %>
<%= link_to 'Cancel', root_path, class: 'btn btn-danger mt-2'%>
</div>
<% end %>
</div>
5 changes: 5 additions & 0 deletions app/views/articles/create.turbo_stream.erb
@@ -0,0 +1,5 @@
<%= turbo_stream.append "articles" do%>
<%= render 'articles/article', article: @article%>
<% end %>
<%= turbo_stream.update Article.new, '' %>
1 change: 1 addition & 0 deletions app/views/articles/destroy.turbo_stream.erb
@@ -0,0 +1 @@
<%= turbo_stream.remove @article %>
3 changes: 3 additions & 0 deletions app/views/articles/edit.html.erb
@@ -0,0 +1,3 @@
<%= turbo_frame_tag @article do %>
<%= render "form", article: @article %>
<% end %>
24 changes: 24 additions & 0 deletions app/views/articles/index.html.erb
@@ -0,0 +1,24 @@
<h1 class="text-dark fw-bold">Encyclopedia</h1>
<p class="text-muted">CRUD with Rails</p>
<hr>

<%= form_with(url: articles_path, method: :get, data: { turbo_frame: "article", turbo_action: 'advance' }) do |form| %>
<div class="d-flex gap-1">
<%= form.text_field :query, class: 'form-control' %>
<%= form.submit class: 'btn btn-success' %>
<% if params[:query].present? %>
<%= link_to 'Clear', articles_path, class: 'btn btn-danger' %>
<% end %>
</div>
<% end %>

<hr>

<%= link_to 'Add Article', new_article_path, data: { turbo_frame: dom_id(Article.new) }, class: 'btn btn-dark' %>
<%= turbo_frame_tag Article.new %>

<div id="articles" class="d-flex gap-3">
<% @articles.each do |article| %>
<%= render article %>
<% end %>
</div>
3 changes: 3 additions & 0 deletions app/views/articles/new.html.erb
@@ -0,0 +1,3 @@
<%= turbo_frame_tag @article do%>
<%= render "form", article: @article %>
<% end %>
1 change: 1 addition & 0 deletions app/views/articles/show.html.erb
@@ -0,0 +1 @@
<%= render @article %>
1 change: 1 addition & 0 deletions app/views/articles/update.turbo_stream.erb
@@ -0,0 +1 @@
<%= turbo_stream.update @article %>
4 changes: 3 additions & 1 deletion app/views/layouts/application.html.erb
Expand Up @@ -11,6 +11,8 @@
</head>

<body>
<%= yield %>
<div class="container mt-5">
<%= yield %>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions config/importmap.rb
Expand Up @@ -5,3 +5,4 @@
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "bootstrap", to: "bootstrap.min.js"
4 changes: 4 additions & 0 deletions config/initializers/assets.rb
Expand Up @@ -5,8 +5,12 @@

# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
Rails.application.config.assets.paths << Rails.root.join("node_modules/bootstrap/dist/js")
Rails.application.config.assets.paths << Rails.root.join("node_modules/bootstrap-icons/font")
Rails.application.config.assets.paths << Rails.root.join("node_modules/bootstrap/dist/js")

# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
Rails.application.config.assets.precompile << "bootstrap.min.js"
10 changes: 3 additions & 7 deletions config/routes.rb
@@ -1,10 +1,6 @@
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
root "articles#index"
get '/search', to: "articles#search"
resources :articles
get "up" => "rails/health#show", as: :rails_health_check

# Defines the root path route ("/")
# root "posts#index"
end
11 changes: 11 additions & 0 deletions db/migrate/20240129153043_create_articles.rb
@@ -0,0 +1,11 @@
class CreateArticles < ActiveRecord::Migration[7.1]
def change
create_table :articles do |t|
t.string :title
t.string :content
t.string :author

t.timestamps
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240129173106_add_date_to_article.rb
@@ -0,0 +1,5 @@
class AddDateToArticle < ActiveRecord::Migration[7.1]
def change
add_column :articles, :date, :datetime
end
end
23 changes: 23 additions & 0 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions test/fixtures/articles.yml
@@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

# one:
# title: MyString
# content: MyString
# author: MyString

# two:
# title: MyString
# content: MyString
# author: MyString

0 comments on commit f372c3b

Please sign in to comment.