Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shopify internship assessment - sparshalc #313

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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