trahald 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +16 -0
- data/Gemfile.lock +58 -0
- data/README.md +29 -8
- data/lib/public/lib/markdown/markdown.js +1616 -0
- data/lib/public/lib/masonry/jquery.masonry.min.js +10 -0
- data/lib/public/lib/reveal/markdown.js +151 -0
- data/lib/public/lib/reveal/showdown.js +62 -0
- data/lib/public/lib/reveal/theme/beige.css +142 -0
- data/lib/public/lib/reveal/theme/default.css +150 -0
- data/lib/public/lib/reveal/theme/moon.css +142 -0
- data/lib/public/lib/reveal/theme/night.css +130 -0
- data/lib/public/lib/reveal/theme/serif.css +130 -0
- data/lib/public/lib/reveal/theme/simple.css +132 -0
- data/lib/public/lib/reveal/theme/sky.css +136 -0
- data/lib/public/lib/reveal/theme/solarized.css +142 -0
- data/lib/trahald.rb +90 -11
- data/lib/trahald/.redis-client.rb.swn +0 -0
- data/lib/trahald/article.rb +34 -0
- data/lib/trahald/backend-base.rb +13 -0
- data/lib/trahald/git.rb +46 -1
- data/lib/trahald/markdown-body.rb +44 -0
- data/lib/trahald/redis-client.rb +33 -9
- data/lib/trahald/version.rb +1 -1
- data/lib/views/edit.slim +47 -10
- data/lib/views/fd.scss +22 -0
- data/lib/views/header.slim +13 -0
- data/lib/views/layout.slim +7 -4
- data/lib/views/list.slim +3 -1
- data/lib/views/page.slim +5 -8
- data/lib/views/slide.slim +37 -0
- data/lib/views/style.scss +3 -1
- data/lib/views/summary.slim +36 -0
- data/lib/views/tab.slim +10 -0
- data/lib/views/tab_edit.slim +10 -0
- data/spec/git_spec.rb +3 -23
- data/spec/redis-client_spec.rb +3 -25
- data/spec/spec_helper.rb +2 -1
- data/spec/support/shared_examples_for_backends.rb +38 -0
- data/trahald.gemspec +1 -0
- metadata +44 -4
@@ -0,0 +1,136 @@
|
|
1
|
+
@import url(http://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic);
|
2
|
+
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
|
3
|
+
/**
|
4
|
+
* Sky theme for reveal.js.
|
5
|
+
*
|
6
|
+
* Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
|
7
|
+
*/
|
8
|
+
/*********************************************
|
9
|
+
* GLOBAL STYLES
|
10
|
+
*********************************************/
|
11
|
+
body {
|
12
|
+
background: #add9e4;
|
13
|
+
background: -moz-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
|
14
|
+
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #f7fbfc), color-stop(100%, #add9e4));
|
15
|
+
background: -webkit-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
|
16
|
+
background: -o-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
|
17
|
+
background: -ms-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
|
18
|
+
background: radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
|
19
|
+
background-color: #f7fbfc; }
|
20
|
+
|
21
|
+
.reveal {
|
22
|
+
font-family: "Open Sans", sans-serif;
|
23
|
+
font-size: 36px;
|
24
|
+
font-weight: 200;
|
25
|
+
letter-spacing: -0.02em;
|
26
|
+
color: #333333; }
|
27
|
+
|
28
|
+
::selection {
|
29
|
+
color: white;
|
30
|
+
background: #134674;
|
31
|
+
text-shadow: none; }
|
32
|
+
|
33
|
+
/*********************************************
|
34
|
+
* HEADERS
|
35
|
+
*********************************************/
|
36
|
+
.reveal h1,
|
37
|
+
.reveal h2,
|
38
|
+
.reveal h3,
|
39
|
+
.reveal h4,
|
40
|
+
.reveal h5,
|
41
|
+
.reveal h6 {
|
42
|
+
margin: 0 0 20px 0;
|
43
|
+
color: #333333;
|
44
|
+
font-family: "Quicksand", sans-serif;
|
45
|
+
line-height: 0.9em;
|
46
|
+
letter-spacing: -0.08em;
|
47
|
+
text-transform: uppercase;
|
48
|
+
text-shadow: none; }
|
49
|
+
|
50
|
+
.reveal h1 {
|
51
|
+
text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); }
|
52
|
+
|
53
|
+
/*********************************************
|
54
|
+
* LINKS
|
55
|
+
*********************************************/
|
56
|
+
.reveal a:not(.image) {
|
57
|
+
color: #3b759e;
|
58
|
+
text-decoration: none;
|
59
|
+
-webkit-transition: color 0.15s ease;
|
60
|
+
-moz-transition: color 0.15s ease;
|
61
|
+
-ms-transition: color 0.15s ease;
|
62
|
+
-o-transition: color 0.15s ease;
|
63
|
+
transition: color 0.15s ease; }
|
64
|
+
|
65
|
+
.reveal a:not(.image):hover {
|
66
|
+
color: #74a7cb;
|
67
|
+
text-shadow: none;
|
68
|
+
border: none; }
|
69
|
+
|
70
|
+
.reveal .roll span:after {
|
71
|
+
color: #fff;
|
72
|
+
background: #264c66; }
|
73
|
+
|
74
|
+
/*********************************************
|
75
|
+
* IMAGES
|
76
|
+
*********************************************/
|
77
|
+
.reveal section img {
|
78
|
+
margin: 15px 0px;
|
79
|
+
background: rgba(255, 255, 255, 0.12);
|
80
|
+
border: 4px solid #333333;
|
81
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
|
82
|
+
-webkit-transition: all 0.2s linear;
|
83
|
+
-moz-transition: all 0.2s linear;
|
84
|
+
-ms-transition: all 0.2s linear;
|
85
|
+
-o-transition: all 0.2s linear;
|
86
|
+
transition: all 0.2s linear; }
|
87
|
+
|
88
|
+
.reveal a:hover img {
|
89
|
+
background: rgba(255, 255, 255, 0.2);
|
90
|
+
border-color: #3b759e;
|
91
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
|
92
|
+
|
93
|
+
/*********************************************
|
94
|
+
* NAVIGATION CONTROLS
|
95
|
+
*********************************************/
|
96
|
+
.reveal .controls div.navigate-left,
|
97
|
+
.reveal .controls div.navigate-left.enabled {
|
98
|
+
border-right-color: #3b759e; }
|
99
|
+
|
100
|
+
.reveal .controls div.navigate-right,
|
101
|
+
.reveal .controls div.navigate-right.enabled {
|
102
|
+
border-left-color: #3b759e; }
|
103
|
+
|
104
|
+
.reveal .controls div.navigate-up,
|
105
|
+
.reveal .controls div.navigate-up.enabled {
|
106
|
+
border-bottom-color: #3b759e; }
|
107
|
+
|
108
|
+
.reveal .controls div.navigate-down,
|
109
|
+
.reveal .controls div.navigate-down.enabled {
|
110
|
+
border-top-color: #3b759e; }
|
111
|
+
|
112
|
+
.reveal .controls div.navigate-left.enabled:hover {
|
113
|
+
border-right-color: #74a7cb; }
|
114
|
+
|
115
|
+
.reveal .controls div.navigate-right.enabled:hover {
|
116
|
+
border-left-color: #74a7cb; }
|
117
|
+
|
118
|
+
.reveal .controls div.navigate-up.enabled:hover {
|
119
|
+
border-bottom-color: #74a7cb; }
|
120
|
+
|
121
|
+
.reveal .controls div.navigate-down.enabled:hover {
|
122
|
+
border-top-color: #74a7cb; }
|
123
|
+
|
124
|
+
/*********************************************
|
125
|
+
* PROGRESS BAR
|
126
|
+
*********************************************/
|
127
|
+
.reveal .progress {
|
128
|
+
background: rgba(0, 0, 0, 0.2); }
|
129
|
+
|
130
|
+
.reveal .progress span {
|
131
|
+
background: #3b759e;
|
132
|
+
-webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
133
|
+
-moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
134
|
+
-ms-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
135
|
+
-o-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
136
|
+
transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
|
@@ -0,0 +1,142 @@
|
|
1
|
+
@import url(http://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
|
2
|
+
/**
|
3
|
+
* Solarized Light theme for reveal.js.
|
4
|
+
* Author: Achim Staebler
|
5
|
+
*/
|
6
|
+
@font-face {
|
7
|
+
font-family: 'League Gothic';
|
8
|
+
src: url("../../lib/font/league_gothic-webfont.eot");
|
9
|
+
src: url("../../lib/font/league_gothic-webfont.eot?#iefix") format("embedded-opentype"), url("../../lib/font/league_gothic-webfont.woff") format("woff"), url("../../lib/font/league_gothic-webfont.ttf") format("truetype"), url("../../lib/font/league_gothic-webfont.svg#LeagueGothicRegular") format("svg");
|
10
|
+
font-weight: normal;
|
11
|
+
font-style: normal; }
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Solarized colors by Ethan Schoonover
|
15
|
+
*/
|
16
|
+
html * {
|
17
|
+
color-profile: sRGB;
|
18
|
+
rendering-intent: auto; }
|
19
|
+
|
20
|
+
/*********************************************
|
21
|
+
* GLOBAL STYLES
|
22
|
+
*********************************************/
|
23
|
+
body {
|
24
|
+
background: #fdf6e3;
|
25
|
+
background-color: #fdf6e3; }
|
26
|
+
|
27
|
+
.reveal {
|
28
|
+
font-family: "Lato", sans-serif;
|
29
|
+
font-size: 36px;
|
30
|
+
font-weight: 200;
|
31
|
+
letter-spacing: -0.02em;
|
32
|
+
color: #657b83; }
|
33
|
+
|
34
|
+
::selection {
|
35
|
+
color: white;
|
36
|
+
background: #d33682;
|
37
|
+
text-shadow: none; }
|
38
|
+
|
39
|
+
/*********************************************
|
40
|
+
* HEADERS
|
41
|
+
*********************************************/
|
42
|
+
.reveal h1,
|
43
|
+
.reveal h2,
|
44
|
+
.reveal h3,
|
45
|
+
.reveal h4,
|
46
|
+
.reveal h5,
|
47
|
+
.reveal h6 {
|
48
|
+
margin: 0 0 20px 0;
|
49
|
+
color: #586e75;
|
50
|
+
font-family: "League Gothic", Impact, sans-serif;
|
51
|
+
line-height: 0.9em;
|
52
|
+
letter-spacing: 0.02em;
|
53
|
+
text-transform: uppercase;
|
54
|
+
text-shadow: none; }
|
55
|
+
|
56
|
+
.reveal h1 {
|
57
|
+
text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); }
|
58
|
+
|
59
|
+
/*********************************************
|
60
|
+
* LINKS
|
61
|
+
*********************************************/
|
62
|
+
.reveal a:not(.image) {
|
63
|
+
color: #268bd2;
|
64
|
+
text-decoration: none;
|
65
|
+
-webkit-transition: color 0.15s ease;
|
66
|
+
-moz-transition: color 0.15s ease;
|
67
|
+
-ms-transition: color 0.15s ease;
|
68
|
+
-o-transition: color 0.15s ease;
|
69
|
+
transition: color 0.15s ease; }
|
70
|
+
|
71
|
+
.reveal a:not(.image):hover {
|
72
|
+
color: #78b9e6;
|
73
|
+
text-shadow: none;
|
74
|
+
border: none; }
|
75
|
+
|
76
|
+
.reveal .roll span:after {
|
77
|
+
color: #fff;
|
78
|
+
background: #1a6091; }
|
79
|
+
|
80
|
+
/*********************************************
|
81
|
+
* IMAGES
|
82
|
+
*********************************************/
|
83
|
+
.reveal section img {
|
84
|
+
margin: 15px 0px;
|
85
|
+
background: rgba(255, 255, 255, 0.12);
|
86
|
+
border: 4px solid #657b83;
|
87
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
|
88
|
+
-webkit-transition: all 0.2s linear;
|
89
|
+
-moz-transition: all 0.2s linear;
|
90
|
+
-ms-transition: all 0.2s linear;
|
91
|
+
-o-transition: all 0.2s linear;
|
92
|
+
transition: all 0.2s linear; }
|
93
|
+
|
94
|
+
.reveal a:hover img {
|
95
|
+
background: rgba(255, 255, 255, 0.2);
|
96
|
+
border-color: #268bd2;
|
97
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
|
98
|
+
|
99
|
+
/*********************************************
|
100
|
+
* NAVIGATION CONTROLS
|
101
|
+
*********************************************/
|
102
|
+
.reveal .controls div.navigate-left,
|
103
|
+
.reveal .controls div.navigate-left.enabled {
|
104
|
+
border-right-color: #268bd2; }
|
105
|
+
|
106
|
+
.reveal .controls div.navigate-right,
|
107
|
+
.reveal .controls div.navigate-right.enabled {
|
108
|
+
border-left-color: #268bd2; }
|
109
|
+
|
110
|
+
.reveal .controls div.navigate-up,
|
111
|
+
.reveal .controls div.navigate-up.enabled {
|
112
|
+
border-bottom-color: #268bd2; }
|
113
|
+
|
114
|
+
.reveal .controls div.navigate-down,
|
115
|
+
.reveal .controls div.navigate-down.enabled {
|
116
|
+
border-top-color: #268bd2; }
|
117
|
+
|
118
|
+
.reveal .controls div.navigate-left.enabled:hover {
|
119
|
+
border-right-color: #78b9e6; }
|
120
|
+
|
121
|
+
.reveal .controls div.navigate-right.enabled:hover {
|
122
|
+
border-left-color: #78b9e6; }
|
123
|
+
|
124
|
+
.reveal .controls div.navigate-up.enabled:hover {
|
125
|
+
border-bottom-color: #78b9e6; }
|
126
|
+
|
127
|
+
.reveal .controls div.navigate-down.enabled:hover {
|
128
|
+
border-top-color: #78b9e6; }
|
129
|
+
|
130
|
+
/*********************************************
|
131
|
+
* PROGRESS BAR
|
132
|
+
*********************************************/
|
133
|
+
.reveal .progress {
|
134
|
+
background: rgba(0, 0, 0, 0.2); }
|
135
|
+
|
136
|
+
.reveal .progress span {
|
137
|
+
background: #268bd2;
|
138
|
+
-webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
139
|
+
-moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
140
|
+
-ms-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
141
|
+
-o-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
|
142
|
+
transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
|
data/lib/trahald.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
require_relative "trahald/article"
|
2
3
|
require_relative "trahald/backend-base"
|
3
|
-
require_relative "trahald/
|
4
|
-
require_relative "trahald/redis-client"
|
4
|
+
require_relative "trahald/markdown-body"
|
5
5
|
require_relative "trahald/version"
|
6
6
|
|
7
7
|
module Trahald
|
@@ -14,26 +14,86 @@ module Trahald
|
|
14
14
|
|
15
15
|
class App < Sinatra::Base
|
16
16
|
|
17
|
+
configure :test do
|
18
|
+
require_relative "trahald/git"
|
19
|
+
require_relative "trahald/redis-client"
|
20
|
+
end
|
21
|
+
|
17
22
|
configure :production, :development, :git do
|
23
|
+
require_relative "trahald/git"
|
18
24
|
dir = Dir::pwd + "/data"
|
19
25
|
Git::init_repo_if_needed dir
|
20
26
|
DB = Git.new dir
|
21
27
|
end
|
22
28
|
|
23
29
|
configure :redis do
|
24
|
-
|
25
|
-
DB = RedisClient.new
|
30
|
+
require_relative "trahald/redis-client"
|
31
|
+
DB = RedisClient.new ENV["TRAHALD_REDIS_URL"]
|
32
|
+
end
|
33
|
+
|
34
|
+
configure do
|
35
|
+
UPLOAD = "upload"
|
36
|
+
UPLOAD_DIR = "#{Dir::pwd}/lib/public/#{UPLOAD}"
|
37
|
+
UPLOAD_LIMIT_SIZE = 2000000 # 2MB
|
38
|
+
Dir::mkdir UPLOAD_DIR unless FileTest.exist? UPLOAD_DIR
|
39
|
+
end
|
40
|
+
|
41
|
+
helpers do
|
42
|
+
def request_headers
|
43
|
+
env.inject({}){|acc, (k,v) | acc[$1.downcase] = v if k =~ /^http_(.*)/i; acc}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
before do
|
48
|
+
expires 500, :public, :must_revalidate
|
26
49
|
end
|
27
50
|
|
28
51
|
get '/' do
|
29
|
-
redirect 'home'
|
52
|
+
#redirect 'home'
|
53
|
+
redirect 'summary'
|
30
54
|
end
|
31
55
|
|
32
56
|
get '/list' do
|
57
|
+
last_modified DB.last_modified
|
58
|
+
@name = "list"
|
59
|
+
@title = "ページ一覧"
|
33
60
|
@keys = DB.list
|
34
61
|
slim :list
|
35
62
|
end
|
36
63
|
|
64
|
+
get '/summary' do
|
65
|
+
last_modified DB.last_modified
|
66
|
+
@name = "summary" # not used
|
67
|
+
@title = "summary" # not used
|
68
|
+
@data = DB.data.sort_by{|d| d.date}.reverse
|
69
|
+
slim :summary
|
70
|
+
end
|
71
|
+
|
72
|
+
get '/uploads' do
|
73
|
+
@name = "uploads"
|
74
|
+
@title = "添付画像一覧"
|
75
|
+
@keys = Dir::glob("#{UPLOAD_DIR}/**/*.{gif,jpg,jpeg,png}").map{|f| "#{UPLOAD}/#{File.basename(f)}"}
|
76
|
+
slim :list
|
77
|
+
end
|
78
|
+
|
79
|
+
get '/css/fd.css' do
|
80
|
+
scss :fd
|
81
|
+
end
|
82
|
+
|
83
|
+
get %r{^/(.+?)/slide$} do
|
84
|
+
puts "slide"
|
85
|
+
puts params[:captures]
|
86
|
+
@name = params[:captures][0]
|
87
|
+
@body = DB.body(@name)
|
88
|
+
puts @body
|
89
|
+
if @body
|
90
|
+
slim :slide, :layout => :raw_layout
|
91
|
+
else
|
92
|
+
@body = ""
|
93
|
+
slim :edit
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
37
97
|
get %r{^/(.+?)/edit$} do
|
38
98
|
puts "edit"
|
39
99
|
puts params[:captures]
|
@@ -60,12 +120,11 @@ module Trahald
|
|
60
120
|
get %r{^/(.+?)$} do
|
61
121
|
puts params[:captures]
|
62
122
|
@name = params[:captures][0]
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@body = Kramdown::Document.new(@body).to_html
|
123
|
+
article = DB.article(@name)
|
124
|
+
if article
|
125
|
+
@body = Kramdown::Document.new(article.body).to_html
|
126
|
+
@date = article.date
|
127
|
+
@tab = slim :tab
|
69
128
|
slim :page
|
70
129
|
else
|
71
130
|
@body = ""
|
@@ -91,6 +150,26 @@ module Trahald
|
|
91
150
|
redirect "/#{URI.escape(@name)}"
|
92
151
|
end
|
93
152
|
|
153
|
+
post "/upload" do
|
154
|
+
header = request_headers
|
155
|
+
data = request.body.read
|
156
|
+
name = header["x_file_name"]
|
157
|
+
size = header["x_file_size"].to_i
|
158
|
+
halt(400, "Invalid request. (Maybe filetype is forbidden.)") unless name
|
159
|
+
halt(400, "Only gif, jpg, png file is enable.") unless ['png', 'jpg', 'jpeg', 'gif'].include? name.split('.').last.downcase
|
160
|
+
filepath = UPLOAD_DIR + "/" + name.downcase
|
161
|
+
halt(423, "File has already existed.") if File.exist? filepath
|
162
|
+
halt(413, "File size is too big.") if size > UPLOAD_LIMIT_SIZE
|
163
|
+
begin
|
164
|
+
File.open(filepath, "w") do |f|
|
165
|
+
f.write(data)
|
166
|
+
end
|
167
|
+
rescue Exception
|
168
|
+
halt(403, "File can not be written. ")
|
169
|
+
end
|
170
|
+
halt 200
|
171
|
+
end
|
172
|
+
|
94
173
|
run! if $0 == __FILE__
|
95
174
|
end
|
96
175
|
end
|
Binary file
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Trahald
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Article
|
6
|
+
attr_reader :name, :body, :date
|
7
|
+
def initialize(name, body, date)
|
8
|
+
@name = name
|
9
|
+
@body = body
|
10
|
+
@date = date
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
{
|
15
|
+
:name => @name,
|
16
|
+
:body => @body,
|
17
|
+
:date => @date
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json
|
22
|
+
JSON.generate to_h
|
23
|
+
end
|
24
|
+
|
25
|
+
def Article.from_json(json)
|
26
|
+
begin
|
27
|
+
h = JSON.parse json
|
28
|
+
Article.new(h["name"], h["body"], h["date"])
|
29
|
+
rescue exception
|
30
|
+
"Json parse error."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|