完成
This commit is contained in:
parent
091bb382c8
commit
081344beae
9 changed files with 103 additions and 93 deletions
|
@ -1,9 +1,9 @@
|
|||
## Usage
|
||||
|
||||
`git clone https://github.com/riq0h/memoapp.git`
|
||||
|
||||
`cd memoapp`
|
||||
|
||||
`bundle install`
|
||||
|
||||
`ruby app.rb`
|
||||
```
|
||||
git clone https://github.com/riq0h/memoapp.git
|
||||
cd memoapp
|
||||
bundle install
|
||||
psql -U postgres -f memos.sql
|
||||
ruby app.rb
|
||||
```
|
||||
|
|
|
@ -2,29 +2,46 @@
|
|||
|
||||
require 'sinatra'
|
||||
require 'sinatra/reloader'
|
||||
require 'json'
|
||||
require 'securerandom'
|
||||
require 'pg'
|
||||
require 'cgi'
|
||||
|
||||
FILE_PATH = 'memos.json'
|
||||
configure do
|
||||
set :conn, PG.connect(dbname: 'memo_app')
|
||||
end
|
||||
|
||||
helpers do
|
||||
def h(text)
|
||||
CGI.escapeHTML(text.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def load_memos
|
||||
if !File.zero?(FILE_PATH)
|
||||
JSON.parse(File.read(FILE_PATH))
|
||||
else
|
||||
{}
|
||||
def db
|
||||
settings.conn
|
||||
end
|
||||
end
|
||||
|
||||
def save_memos(memos)
|
||||
File.open(FILE_PATH, 'w') do |file|
|
||||
file.write(JSON.generate(memos))
|
||||
def find_memo(id)
|
||||
db.exec_params('SELECT * FROM memos WHERE id = $1', [id]).first
|
||||
end
|
||||
|
||||
def create_memo(title, content)
|
||||
db.exec_params(
|
||||
'INSERT INTO memos (title, content) VALUES ($1, $2) RETURNING id',
|
||||
[title, content]
|
||||
).first['id']
|
||||
end
|
||||
|
||||
def update_memo(id, title, content)
|
||||
db.exec_params(
|
||||
'UPDATE memos SET title = $1, content = $2 WHERE id = $3',
|
||||
[title, content, id]
|
||||
)
|
||||
end
|
||||
|
||||
def delete_memo(id)
|
||||
db.exec_params('DELETE FROM memos WHERE id = $1', [id])
|
||||
end
|
||||
|
||||
def all_memos
|
||||
db.exec('SELECT * FROM memos ORDER BY created_at DESC').to_a
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -33,7 +50,7 @@ get '/' do
|
|||
end
|
||||
|
||||
get '/memos' do
|
||||
@memos = load_memos
|
||||
@memos = all_memos
|
||||
erb :index
|
||||
end
|
||||
|
||||
|
@ -42,37 +59,29 @@ get '/memos/new' do
|
|||
end
|
||||
|
||||
post '/memos' do
|
||||
memos = load_memos
|
||||
id = SecureRandom.uuid
|
||||
memos[id] = { 'title' => params[:title], 'content' => params[:content] }
|
||||
save_memos(memos)
|
||||
redirect '/memos'
|
||||
id = create_memo(params[:title], params[:content])
|
||||
redirect "/memos/#{id}"
|
||||
end
|
||||
|
||||
get '/memos/:id' do
|
||||
@memo = load_memos[params[:id]]
|
||||
@memo = find_memo(params[:id])
|
||||
halt 404, erb(:not_found) unless @memo
|
||||
erb :show
|
||||
end
|
||||
|
||||
get '/memos/:id/edit' do
|
||||
@memo = load_memos[params[:id]]
|
||||
@memo = find_memo(params[:id])
|
||||
halt 404, erb(:not_found) unless @memo
|
||||
erb :edit
|
||||
end
|
||||
|
||||
patch '/memos/:id' do
|
||||
memos = load_memos
|
||||
halt 404, erb(:not_found) unless memos[params[:id]]
|
||||
memos[params[:id]] = { 'title' => params[:title], 'content' => params[:content] }
|
||||
save_memos(memos)
|
||||
update_memo(params[:id], params[:title], params[:content])
|
||||
redirect "/memos/#{params[:id]}"
|
||||
end
|
||||
|
||||
delete '/memos/:id' do
|
||||
memos = load_memos
|
||||
halt 404, erb(:not_found) unless memos.delete(params[:id])
|
||||
save_memos(memos)
|
||||
delete_memo(params[:id])
|
||||
redirect '/memos'
|
||||
end
|
||||
|
||||
|
|
9
memoapp/memos.sql
Normal file
9
memoapp/memos.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE DATABASE memo_app;
|
||||
DROP TABLE IF EXISTS memos;
|
||||
\c memo_app
|
||||
CREATE TABLE memos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
|
@ -1,3 +1,10 @@
|
|||
:root {
|
||||
--save-color: #007bff;
|
||||
--edit-color: #28a745;
|
||||
--delete-color: #dc3545;
|
||||
--back-color: #6c757d;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f0f0f0;
|
||||
|
@ -35,7 +42,7 @@ nav {
|
|||
nav a {
|
||||
margin: 0 10px;
|
||||
text-decoration: none;
|
||||
color: #007bff;
|
||||
color: var(--save-color);
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
|
@ -59,44 +66,42 @@ form textarea {
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
form button {
|
||||
.button {
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
form button.save {
|
||||
background-color: #007bff;
|
||||
.button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
form button.save:hover {
|
||||
background-color: #0056b3;
|
||||
.button.save {
|
||||
background-color: var(--save-color);
|
||||
}
|
||||
|
||||
form button.delete {
|
||||
background-color: #dc3545;
|
||||
.button.delete {
|
||||
background-color: var(--delete-color);
|
||||
padding: 12px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
form button.delete:hover {
|
||||
background-color: #c82333;
|
||||
.button.edit {
|
||||
background-color: var(--edit-color);
|
||||
}
|
||||
|
||||
form button.edit {
|
||||
background-color: #28a745;
|
||||
}
|
||||
|
||||
form button.edit:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
form button.back {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
|
||||
form button.back:hover {
|
||||
background-color: #5a6268;
|
||||
.button.back {
|
||||
background-color: var(--back-color);
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
margin: 10px auto 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.memo-list {
|
||||
|
@ -110,7 +115,7 @@ form button.back:hover {
|
|||
|
||||
.memo-list a {
|
||||
text-decoration: none;
|
||||
color: #007bff;
|
||||
color: var(--save-color);
|
||||
}
|
||||
|
||||
.memo-list a:hover {
|
||||
|
@ -123,27 +128,18 @@ form button.back:hover {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.memo-actions form {
|
||||
display: inline;
|
||||
.memo-actions form,
|
||||
.memo-actions a {
|
||||
flex: 1;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.memo-actions button {
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
.memo-actions form:first-child,
|
||||
.memo-actions a:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.memo-actions .edit {
|
||||
background-color: #28a745;
|
||||
.memo-actions form:last-child,
|
||||
.memo-actions a:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.memo-actions .edit:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.memo-actions .back {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<form action="/memos/<%= params[:id] %>" method="post">
|
||||
<form action="/memos/<%= @memo['id'] %>" method="post">
|
||||
<input type="hidden" name="_method" value="patch">
|
||||
<label for="title">タイトル</label>
|
||||
<input type="text" name="title" id="title" value="<%= h(@memo['title']) %>">
|
||||
<label for="content">内容</label>
|
||||
<textarea name="content" id="content"><%= h(@memo['content']) %></textarea>
|
||||
<button type="submit" class="edit">変更</button>
|
||||
<button type="submit" class="button save">保存</button>
|
||||
</form>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ul class="memo-list">
|
||||
<% @memos.each do |id, memo| %>
|
||||
<li><a href="/memos/<%= id %>"><%= h(memo["title"]) %></a></li>
|
||||
<% @memos.each do |memo| %>
|
||||
<li><a href="/memos/<%= memo['id'] %>"><%= h(memo['title']) %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
<input type="text" name="title" id="title">
|
||||
<label for="content">内容</label>
|
||||
<textarea name="content" id="content"></textarea>
|
||||
<button type="submit" class="save">保存</button>
|
||||
<button type="submit" class="button save">保存</button>
|
||||
</form>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<h1>404 Not Found</h1>
|
||||
<h2>404 Not Found</h2>
|
||||
<p>お探しのページは見つかりませんでした。</p>
|
||||
<a href="/">トップページに戻る</a>
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
<h2><%= h(@memo['title']) %></h2>
|
||||
<p><%= h(@memo['content']) %></p>
|
||||
<div class="memo-actions">
|
||||
<form action="/memos/<%= params[:id] %>/edit" method="get">
|
||||
<button type="submit" class="edit">変更</button>
|
||||
</form>
|
||||
<form action="/memos/<%= params[:id] %>" method="post">
|
||||
<a href="/memos/<%= @memo['id'] %>/edit" class="button edit">編集</a>
|
||||
<form action="/memos/<%= @memo['id'] %>" method="post">
|
||||
<input type="hidden" name="_method" value="delete">
|
||||
<button type="submit" class="delete">削除</button>
|
||||
<button type="submit" class="button delete">削除</button>
|
||||
</form>
|
||||
</div>
|
||||
<form action="/memos" method="get">
|
||||
<button type="submit" class="back">戻る</button>
|
||||
</form>
|
||||
<a href="/memos" class="button back">戻る</a>
|
||||
|
|
Loading…
Reference in a new issue