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