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
data/lib/trahald/backend-base.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Trahald
|
4
4
|
class BackendBase
|
5
|
+
|
5
6
|
def initialize
|
6
7
|
end
|
7
8
|
|
@@ -9,6 +10,10 @@ module Trahald
|
|
9
10
|
raise "Called abstract method: add!"
|
10
11
|
end
|
11
12
|
|
13
|
+
def article(name)
|
14
|
+
raise "Called abstract method: article"
|
15
|
+
end
|
16
|
+
|
12
17
|
def body(name)
|
13
18
|
raise "Called abstract method: body"
|
14
19
|
end
|
@@ -17,10 +22,18 @@ module Trahald
|
|
17
22
|
raise "Called abstract method: commit!"
|
18
23
|
end
|
19
24
|
|
25
|
+
def last_modified
|
26
|
+
raise "Called abstract method: last_modified"
|
27
|
+
end
|
28
|
+
|
20
29
|
def list
|
21
30
|
raise "Called abstract method: list"
|
22
31
|
end
|
23
32
|
|
33
|
+
def data
|
34
|
+
raise "Called abstract method: data"
|
35
|
+
end
|
36
|
+
|
24
37
|
def self.init_repo_if_needed(dir)
|
25
38
|
raise "Called abstract method: self.init_repo_if_needed"
|
26
39
|
end
|
data/lib/trahald/git.rb
CHANGED
@@ -9,13 +9,25 @@ module Trahald
|
|
9
9
|
@ext = ext
|
10
10
|
end
|
11
11
|
|
12
|
+
def article(name)
|
13
|
+
commit = repo.commits('master', false).find{|c|
|
14
|
+
c.diffs.first.b_path.force_encoding("ASCII-8BIT") == "#{name}.#{@ext}".force_encoding("ASCII-8BIT")
|
15
|
+
}
|
16
|
+
return nil unless commit
|
17
|
+
Article.new(
|
18
|
+
name,
|
19
|
+
commit.diffs.first.b_blob.data.force_encoding("UTF-8"),
|
20
|
+
commit.date
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
12
24
|
def add!(name, body)
|
13
25
|
path = "#{@repo_dir}/#{name}.#{@ext}"
|
14
26
|
FileUtils.mkdir_p File.dirname(path)
|
15
27
|
begin
|
16
28
|
File.open(path, 'w'){|f| f.write(body)}
|
17
29
|
Dir.chdir(@repo_dir){
|
18
|
-
repo.add "#{name}.#{@ext}"
|
30
|
+
repo.add "#{name}.#{@ext}".force_encoding("ASCII-8BIT")
|
19
31
|
}
|
20
32
|
true
|
21
33
|
rescue => exception
|
@@ -24,7 +36,13 @@ module Trahald
|
|
24
36
|
end
|
25
37
|
end
|
26
38
|
|
39
|
+
#experimental
|
27
40
|
def body(name)
|
41
|
+
a = article(name)
|
42
|
+
if a; a.body else nil end
|
43
|
+
end
|
44
|
+
|
45
|
+
def body_old(name)
|
28
46
|
first = first_commit
|
29
47
|
return nil unless first
|
30
48
|
|
@@ -39,6 +57,17 @@ module Trahald
|
|
39
57
|
repo.commit_index(message)
|
40
58
|
end
|
41
59
|
|
60
|
+
# experimental
|
61
|
+
def data
|
62
|
+
first = first_commit
|
63
|
+
return [] unless first
|
64
|
+
summary 50
|
65
|
+
end
|
66
|
+
|
67
|
+
def last_modified
|
68
|
+
first_commit.date
|
69
|
+
end
|
70
|
+
|
42
71
|
def list
|
43
72
|
first = first_commit
|
44
73
|
return [] unless first
|
@@ -59,18 +88,34 @@ module Trahald
|
|
59
88
|
end
|
60
89
|
|
61
90
|
def first_commit
|
91
|
+
return Time.now unless repo.commits.any?
|
62
92
|
repo.commits.first
|
63
93
|
end
|
64
94
|
|
65
95
|
def files(pos, tree, list)
|
66
96
|
tree.blobs.each{|blob|
|
97
|
+
puts blob.name
|
67
98
|
list.push pos + File.basename(blob.name.force_encoding("UTF-8"), ".#{@ext}")
|
68
99
|
}
|
69
100
|
tree.trees.each{|t|
|
101
|
+
puts t.name
|
70
102
|
files "#{pos}#{t.name.force_encoding("UTF-8")}/", t, list
|
71
103
|
}
|
72
104
|
end
|
73
105
|
|
106
|
+
# args:
|
107
|
+
# max: number of commits gotten. if max is false, all commits are gotten.
|
108
|
+
def summary(max=false)
|
109
|
+
repo.commits('master', max).map{|commit|
|
110
|
+
path = commit.diffs.first.b_path.force_encoding("UTF-8")
|
111
|
+
MarkdownBody.new(
|
112
|
+
path.slice(0, path.size - (@ext.size+1)),
|
113
|
+
commit.diffs.first.b_blob.data.force_encoding("UTF-8"),
|
114
|
+
commit.date
|
115
|
+
).summary
|
116
|
+
}.uniq{|i| i.name}
|
117
|
+
end
|
118
|
+
|
74
119
|
def repo
|
75
120
|
@repo ||= Grit::Repo.new @repo_dir
|
76
121
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Trahald
|
4
|
+
require 'kramdown'
|
5
|
+
require 'sanitize'
|
6
|
+
|
7
|
+
class MarkdownBody
|
8
|
+
MAX_TEXT_SIZE = 160
|
9
|
+
Summary = Struct.new("Summary", :name, :imgs, :body, :date)
|
10
|
+
|
11
|
+
def initialize(name, body, date)
|
12
|
+
@name = name
|
13
|
+
@body = body
|
14
|
+
@date = date
|
15
|
+
end
|
16
|
+
|
17
|
+
def pre
|
18
|
+
raw = Sanitize.clean Kramdown::Document.new(@body).to_html
|
19
|
+
if raw.size > MAX_TEXT_SIZE
|
20
|
+
raw[0, MAX_TEXT_SIZE] + "..."
|
21
|
+
else
|
22
|
+
raw
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def img_src
|
27
|
+
pattern = Regexp.new '!\[.+\]\((.+)\)'
|
28
|
+
raw = @body.split('\n')
|
29
|
+
raw.map{|r|
|
30
|
+
$1 if pattern =~ r
|
31
|
+
}.select{|i| i}
|
32
|
+
end
|
33
|
+
|
34
|
+
def summary
|
35
|
+
Summary.new(
|
36
|
+
@name,
|
37
|
+
img_src,
|
38
|
+
pre,
|
39
|
+
@date
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
data/lib/trahald/redis-client.rb
CHANGED
@@ -5,14 +5,20 @@ module Trahald
|
|
5
5
|
require 'uri'
|
6
6
|
|
7
7
|
class RedisClient < BackendBase
|
8
|
+
# track all page names.
|
9
|
+
KEY_SET = ".keys" # TODO: this must not be collided with any page names.
|
10
|
+
|
11
|
+
# for using cache.
|
12
|
+
MODIFIED_DATE = ".modified" # TODO: same
|
13
|
+
|
8
14
|
def initialize(url)
|
9
|
-
|
10
|
-
@
|
11
|
-
|
12
|
-
:port => uri.port
|
13
|
-
)
|
15
|
+
@redis = Redis.new(:url => url)
|
16
|
+
@params = Hash.new
|
17
|
+
end
|
14
18
|
|
15
|
-
|
19
|
+
def article(name)
|
20
|
+
json = @redis.zrange(name, -1, -1).first # nil unless zrange(..).any?
|
21
|
+
if json; Article.from_json(json) else nil end
|
16
22
|
end
|
17
23
|
|
18
24
|
# This method does not set data to Redis DB. To confirm, use commit! after add!.
|
@@ -21,14 +27,20 @@ module Trahald
|
|
21
27
|
end
|
22
28
|
|
23
29
|
def body(name)
|
24
|
-
|
30
|
+
a = article name
|
31
|
+
if a; a.body else nil end
|
25
32
|
end
|
26
33
|
|
27
34
|
# message is not used.
|
28
35
|
def commit!(message)
|
36
|
+
date = Time.now
|
29
37
|
@params.each{|name, body|
|
30
|
-
|
38
|
+
json = Article.new(name, body, date).to_json
|
39
|
+
zcard = @redis.zcard name
|
40
|
+
@redis.zadd name, zcard+1, json
|
41
|
+
@redis.sadd KEY_SET, name
|
31
42
|
}
|
43
|
+
@redis.set MODIFIED_DATE, date.to_s
|
32
44
|
end
|
33
45
|
|
34
46
|
# CAUTION! This method flush data on current db.
|
@@ -36,8 +48,20 @@ module Trahald
|
|
36
48
|
@redis.flushdb
|
37
49
|
end
|
38
50
|
|
51
|
+
def data
|
52
|
+
@redis.smembers(KEY_SET).map do |name|
|
53
|
+
a = article name
|
54
|
+
MarkdownBody.new(name, a.body, a.date).summary
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def last_modified
|
59
|
+
date = @redis.get(MODIFIED_DATE)
|
60
|
+
if date; Time.parse date else Time.now end
|
61
|
+
end
|
62
|
+
|
39
63
|
def list
|
40
|
-
@redis.
|
64
|
+
@redis.smembers(KEY_SET).sort
|
41
65
|
end
|
42
66
|
|
43
67
|
def self.init_repo_if_needed(dir)
|
data/lib/trahald/version.rb
CHANGED
data/lib/views/edit.slim
CHANGED
@@ -1,10 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
script src="/lib/markdown/markdown.js"
|
2
|
+
script src="http://proger.i-forge.net/filedrop-min.js"
|
3
|
+
|
4
|
+
javascript:
|
5
|
+
$(function(){
|
6
|
+
var updatePreview = function(){
|
7
|
+
$("#preview").html(markdown.toHTML($("#text-input").val()));
|
8
|
+
};
|
9
|
+
$(document).ready(updatePreview());
|
10
|
+
$("#text-input").on("keyup", function(event){
|
11
|
+
updatePreview();
|
12
|
+
});
|
13
|
+
|
14
|
+
var options = {iframe: {url: '/upload'}};
|
15
|
+
var zone = new FileDrop('zone', options);
|
16
|
+
zone.on.send.push(function(files){
|
17
|
+
var unix_t = parseInt(new Date / 1000);
|
18
|
+
for(var i=0; i<files.length;++i){
|
19
|
+
name = unix_t + "." + files[i].name.split('.').pop().toLowerCase();
|
20
|
+
files[i].name = name
|
21
|
+
files[i].on.done.push(function(xhr,e){
|
22
|
+
var input = $("#text-input").val() + "\n![" + name + "](/#{UPLOAD}/" + name + ")\n";
|
23
|
+
$("#text-input").val(input);
|
24
|
+
updatePreview();
|
25
|
+
});
|
26
|
+
files[i].on.error.push(function(e, xhr){
|
27
|
+
alert(xhr.response);
|
28
|
+
});
|
29
|
+
files[i].SendTo('/upload');
|
30
|
+
}
|
31
|
+
});
|
32
|
+
});
|
33
|
+
|
34
|
+
== slim :header
|
35
|
+
|
36
|
+
div.row-fluid
|
37
|
+
div.span6
|
38
|
+
form action="/edit" method="post" style="margin-top:20px;"
|
39
|
+
div.row-fluid
|
40
|
+
input type="text" name="name" value="#{@name}" class="span12 input" style="font-size: 24px;"
|
41
|
+
fieldset#zone.row-fluid
|
42
|
+
textarea name="body" id="text-input" style="border:0; box-shadow: none; height: 500px;" class="span12" #{@body}
|
43
|
+
div.row-fluid
|
44
|
+
input type="submit" value="作成/更新" class="btn btn-primary"
|
45
|
+
div.span6#preview.well style="background: #fdfdfd; margin-top: 20px;"
|
46
|
+
|
47
|
+
== slim :tab_edit
|
data/lib/views/fd.scss
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
.fd-zone {
|
2
|
+
position: relative;
|
3
|
+
overflow: hidden;
|
4
|
+
//width: 15em;
|
5
|
+
text-align:center;
|
6
|
+
}
|
7
|
+
|
8
|
+
.fd-file {
|
9
|
+
opacity: 0;
|
10
|
+
font-size:118px;
|
11
|
+
position: absolute;
|
12
|
+
right: 0;
|
13
|
+
top: 0;
|
14
|
+
z-index: 1;
|
15
|
+
padding: 0;
|
16
|
+
margin: 0;
|
17
|
+
cursor: pointer;
|
18
|
+
filter: alpha(opacity=0);
|
19
|
+
font-family: sans-serif;
|
20
|
+
}
|
21
|
+
|
22
|
+
.fd-zone.over {border-color: maroon;}
|
data/lib/views/layout.slim
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
html
|
2
2
|
head
|
3
3
|
meta charset='UTF-8'
|
4
|
+
meta name="viewport" content="width=device-width, initial-scale=1.0"
|
4
5
|
title Trahald
|
5
|
-
link href=
|
6
|
+
link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"
|
7
|
+
link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css" rel="stylesheet"
|
8
|
+
link href="/css/fd.css" rel="stylesheet"
|
9
|
+
script src="//code.jquery.com/jquery-1.9.1.min.js"
|
10
|
+
script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"
|
6
11
|
body
|
7
12
|
div.container
|
8
|
-
|
9
|
-
a href='/' Trahald
|
10
|
-
== yield
|
13
|
+
== yield
|
data/lib/views/list.slim
CHANGED
data/lib/views/page.slim
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
html
|
2
|
+
head
|
3
|
+
meta charset='UTF-8'
|
4
|
+
meta content="yes" name="apple-mobile-web-app-capable"
|
5
|
+
meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"
|
6
|
+
meta name="viewport" content="width=device-width, initial-scale=1.0"
|
7
|
+
title Trahald
|
8
|
+
link rel="stylesheet" href="//cdn.jsdelivr.net/reveal.js/2.2/reveal.min.css"
|
9
|
+
link rel="stylesheet" href="/lib/reveal/theme/serif.css" id="theme"
|
10
|
+
style
|
11
|
+
body{}
|
12
|
+
body
|
13
|
+
.reveal
|
14
|
+
.slides
|
15
|
+
section data-markdown="" data-separator="---"
|
16
|
+
script type="text/template"
|
17
|
+
== @body
|
18
|
+
script src="//cdn.jsdelivr.net/headjs/0.99/head.min.js"
|
19
|
+
script src="//cdn.jsdelivr.net/reveal.js/2.2/reveal.min.js"
|
20
|
+
javascript:
|
21
|
+
Reveal.initialize({
|
22
|
+
controls: true,
|
23
|
+
progress: true,
|
24
|
+
history: true,
|
25
|
+
center: true,
|
26
|
+
|
27
|
+
theme: Reveal.getQueryHash().theme,
|
28
|
+
transition: Reveal.getQueryHash().transition || 'default',
|
29
|
+
|
30
|
+
// Optional libraries used to extend on reveal.js
|
31
|
+
dependencies: [
|
32
|
+
{ src: '/lib/reveal/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
33
|
+
{ src: '/lib/reveal/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }
|
34
|
+
]
|
35
|
+
});
|
36
|
+
|
37
|
+
|
data/lib/views/style.scss
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
== slim :header
|
2
|
+
script src="/lib/masonry/jquery.masonry.min.js"
|
3
|
+
|
4
|
+
css:
|
5
|
+
.item{
|
6
|
+
width: 300px;
|
7
|
+
margin: 10px;
|
8
|
+
float: center;
|
9
|
+
}
|
10
|
+
|
11
|
+
javascript:
|
12
|
+
$(function(){
|
13
|
+
$('#ultarget').masonry({
|
14
|
+
itemSelector: '.item',
|
15
|
+
columnWidth: 350
|
16
|
+
});
|
17
|
+
});
|
18
|
+
|
19
|
+
/h1 =@title
|
20
|
+
- if @data.any?
|
21
|
+
ul#ultarget.thumbnails
|
22
|
+
- for summary in @data do
|
23
|
+
li.thumbnail.item.span4
|
24
|
+
a href="#{summary.name}"
|
25
|
+
h3 =summary.name
|
26
|
+
- if summary.imgs.any?
|
27
|
+
a href="#{summary.name}"
|
28
|
+
img src="#{summary.imgs.first}" alt=""/
|
29
|
+
p=summary.body
|
30
|
+
p
|
31
|
+
small
|
32
|
+
em="更新日時: #{summary.date.to_s}"
|
33
|
+
- else
|
34
|
+
ul
|
35
|
+
li まだページが作成されていません。
|
36
|
+
|