wcコマンド改良&&メモアプリ試作

This commit is contained in:
Rikuoh Tsujitani 2024-07-28 20:53:15 +09:00
parent bff969520f
commit 473407048c
Signed by: riq0h
GPG key ID: 010F09DEA298C717
12 changed files with 337 additions and 10 deletions

4
memoapp/Gemfile Normal file
View file

@ -0,0 +1,4 @@
source "https://rubygems.org"
gem "sinatra"
gem "sinatra-contrib"

38
memoapp/Gemfile.lock Normal file
View file

@ -0,0 +1,38 @@
GEM
remote: https://rubygems.org/
specs:
base64 (0.2.0)
multi_json (1.15.0)
mustermann (3.0.0)
ruby2_keywords (~> 0.0.1)
rack (3.1.7)
rack-protection (4.0.0)
base64 (>= 0.1.0)
rack (>= 3.0.0, < 4)
rack-session (2.0.0)
rack (>= 3.0.0)
ruby2_keywords (0.0.5)
sinatra (4.0.0)
mustermann (~> 3.0)
rack (>= 3.0.0, < 4)
rack-protection (= 4.0.0)
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0)
sinatra-contrib (4.0.0)
multi_json (>= 0.0.2)
mustermann (~> 3.0)
rack-protection (= 4.0.0)
sinatra (= 4.0.0)
tilt (~> 2.0)
tilt (2.4.0)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
sinatra
sinatra-contrib
BUNDLED WITH
2.5.11

2
memoapp/README.md Normal file
View file

@ -0,0 +1,2 @@
# memoapp
Sinatraでシンプルなメモアプリを作る

62
memoapp/app.rb Normal file
View file

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'sinatra'
require 'sinatra/reloader' if development?
require 'json'
FILE_PATH = 'public/memos.json'
def load_memos
File.exist?(FILE_PATH) ? JSON.parse(File.read(FILE_PATH)) : {}
end
def save_memos(memos)
File.open(FILE_PATH, 'w') do |file|
file.write(JSON.pretty_generate(memos))
end
end
get '/' do
redirect '/memos'
end
get '/memos' do
@memos = load_memos
erb :index
end
get '/memos/new' do
erb :new
end
post '/memos' do
memos = load_memos
id = (memos.keys.map(&:to_i).max || 0) + 1
memos[id.to_s] = { 'title' => params[:title], 'content' => params[:content] }
save_memos(memos)
redirect '/memos'
end
get '/memos/:id' do
@memo = load_memos[params[:id]]
erb :show
end
get '/memos/:id/edit' do
@memo = load_memos[params[:id]]
erb :edit
end
patch '/memos/:id' do
memos = load_memos
memos[params[:id]] = { 'title' => params[:title], 'content' => params[:content] }
save_memos(memos)
redirect "/memos/#{params[:id]}"
end
delete '/memos/:id' do
memos = load_memos
memos.delete(params[:id])
save_memos(memos)
redirect '/memos'
end

10
memoapp/public/memos.json Normal file
View file

@ -0,0 +1,10 @@
{
"1": {
"title": "あ",
"content": "い"
},
"2": {
"title": "い",
"content": "え"
}
}

149
memoapp/public/styles.css Normal file
View file

@ -0,0 +1,149 @@
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
width: 300px;
}
header {
text-align: center;
margin-bottom: 20px;
}
header h1 {
font-size: 24px;
margin: 0;
}
nav {
margin-bottom: 20px;
text-align: center;
}
nav a {
margin: 0 10px;
text-decoration: none;
color: #007bff;
}
nav a:hover {
text-decoration: underline;
}
form {
display: flex;
flex-direction: column;
}
form label {
margin-bottom: 5px;
}
form input[type="text"],
form textarea {
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
form button {
padding: 10px;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
form button.save {
background-color: #007bff;
}
form button.save:hover {
background-color: #0056b3;
}
form button.delete {
background-color: #dc3545;
}
form button.delete:hover {
background-color: #c82333;
}
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;
}
.memo-list {
list-style: none;
padding: 0;
}
.memo-list li {
margin-bottom: 10px;
}
.memo-list a {
text-decoration: none;
color: #007bff;
}
.memo-list a:hover {
text-decoration: underline;
}
.memo-actions {
display: flex;
justify-content: space-between;
margin-bottom: 10px; /* 追加: 「戻る」ボタンの上に余白を追加 */
}
.memo-actions form {
display: inline;
}
.memo-actions button {
padding: 10px;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.memo-actions .edit {
background-color: #28a745; /* 緑色に統一 */
}
.memo-actions .edit:hover {
background-color: #218838;
}
.memo-actions .back {
background-color: #6c757d;
}

8
memoapp/views/edit.erb Normal file
View file

@ -0,0 +1,8 @@
<form action="/memos/<%= params[:id] %>" method="post">
<input type="hidden" name="_method" value="patch">
<label for="title">タイトル</label>
<input type="text" name="title" id="title" value="<%= @memo["title"] %>">
<label for="content">内容</label>
<textarea name="content" id="content"><%= @memo["content"] %></textarea>
<button type="submit" class="edit">変更</button>
</form>

5
memoapp/views/index.erb Normal file
View file

@ -0,0 +1,5 @@
<ul class="memo-list">
<% @memos.each do |id, memo| %>
<li><a href="/memos/<%= id %>"><%= memo["title"] %></a></li>
<% end %>
</ul>

22
memoapp/views/layout.erb Normal file
View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>メモアプリ</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<div class="container">
<header>
<h1>メモアプリ</h1>
</header>
<nav>
<a href="/memos">メモ一覧</a>
<a href="/memos/new">新規メモ作成</a>
</nav>
<main>
<%= yield %>
</main>
</div>
</body>
</html>

7
memoapp/views/new.erb Normal file
View file

@ -0,0 +1,7 @@
<form action="/memos" method="post">
<label for="title">タイトル</label>
<input type="text" name="title" id="title">
<label for="content">内容</label>
<textarea name="content" id="content"></textarea>
<button type="submit" class="save">保存</button>
</form>

14
memoapp/views/show.erb Normal file
View file

@ -0,0 +1,14 @@
<h2><%= @memo["title"] %></h2>
<p><%= @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">
<input type="hidden" name="_method" value="delete">
<button type="submit" class="delete">削除</button>
</form>
</div>
<form action="/memos" method="get">
<button type="submit" class="back">戻る</button>
</form>

26
wc.rb
View file

@ -8,7 +8,7 @@ def main
total_stats = calculate_total_stats(file_stats)
max_widths = calculate_max_widths(file_stats, total_stats)
print_file_stats(file_stats, max_widths, options)
print_file_stats([['合計', total_stats]], max_widths, options) if sources.size > 1
print_file_stats([{ filename: '合計', **total_stats }], max_widths, options) if sources.size > 1
end
def parse_options
@ -26,31 +26,37 @@ end
def collect_file_stats(sources)
sources.map do |source|
input = source.empty? ? ARGF.read : File.read(source)
stats = { lines: input.lines.count, words: input.split.size, bytes: input.bytesize }
[source, stats]
{ filename: source, lines: input.lines.count, words: input.split.size, bytes: input.bytesize }
end
end
def calculate_total_stats(file_stats)
file_stats.reduce({ lines: 0, words: 0, bytes: 0 }) do |total, (_, stats)|
total.merge(stats) { |_, a, b| a + b }
total_stats = { lines: 0, words: 0, bytes: 0 }
file_stats.each do |stats|
total_stats[:lines] += stats[:lines]
total_stats[:words] += stats[:words]
total_stats[:bytes] += stats[:bytes]
end
total_stats
end
def calculate_max_widths(file_stats, total_stats)
(file_stats.map(&:last) + [total_stats]).each_with_object({ lines: 0, words: 0, bytes: 0 }) do |stats, max_widths|
max_widths.merge!(stats) { |_, max, stat| [max, stat.to_s.length].max }
all_stats = file_stats + [total_stats]
%i[lines words bytes].each_with_object({}) do |key, max_widths|
max_widths[key] = all_stats.map { |stats| stats[key].to_s.length }.max
end
end
def format_result(stats, max_widths, options)
%i[lines words bytes].map { |key| stats[key].to_s.rjust(max_widths[key]) if options[key] }.compact.join(' ')
%i[lines words bytes].filter_map do |key|
stats[key].to_s.rjust(max_widths[key]) if options[key]
end.join(' ')
end
def print_file_stats(file_stats, max_widths, options)
file_stats.each do |source, stats|
file_stats.each do |stats|
result = format_result(stats, max_widths, options)
puts "#{result} #{source}"
puts "#{result} #{stats[:filename]}"
end
end