clay 1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/clay +29 -0
- data/src/clay.rb +196 -0
- data/src/rack/clay.rb +114 -0
- metadata +170 -0
data/bin/clay
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. src]) # add ../src dir to the path of required modules
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'clay'
|
|
6
|
+
|
|
7
|
+
usage = <<HELP
|
|
8
|
+
USAGE:
|
|
9
|
+
$ clay <command>
|
|
10
|
+
|
|
11
|
+
COMMANDS:
|
|
12
|
+
init - creates a directory structure for the project
|
|
13
|
+
form - forms a set of static pages from the project
|
|
14
|
+
run - starts a local server
|
|
15
|
+
HELP
|
|
16
|
+
|
|
17
|
+
command = ARGV[0]
|
|
18
|
+
case command
|
|
19
|
+
when "init" then
|
|
20
|
+
if ARGV[1].nil?
|
|
21
|
+
puts "USAGE:\n $ clay init <site_name> "
|
|
22
|
+
else
|
|
23
|
+
Clay.init ARGV[1]
|
|
24
|
+
end
|
|
25
|
+
when "form" then Clay.form
|
|
26
|
+
when "run" then Clay.run
|
|
27
|
+
when "version" then puts "clay ver.: " + Goo::VERSION
|
|
28
|
+
else puts usage
|
|
29
|
+
end
|
data/src/clay.rb
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
require 'mustache'
|
|
4
|
+
require 'rdiscount'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'yaml'
|
|
7
|
+
|
|
8
|
+
module Clay
|
|
9
|
+
VERSION = "1.1"
|
|
10
|
+
|
|
11
|
+
def self.init project_name
|
|
12
|
+
print "Creating the folder structure... "
|
|
13
|
+
project.init project_name
|
|
14
|
+
puts "complete"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.form
|
|
18
|
+
print "Forming... "
|
|
19
|
+
project.check_consistency
|
|
20
|
+
project.build
|
|
21
|
+
puts "complete."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.run
|
|
25
|
+
puts "Starting server on http://localhost:9292/"
|
|
26
|
+
project.prepare_rack_config
|
|
27
|
+
`rackup config.ru`
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def self.project
|
|
33
|
+
Project.new project_root
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.project_root
|
|
37
|
+
`pwd`.strip
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Project
|
|
43
|
+
def initialize project_root
|
|
44
|
+
@project_root = project_root
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def init name
|
|
48
|
+
create_directory name
|
|
49
|
+
Dir.chdir name do
|
|
50
|
+
@project_root = File.join(@project_root, name)
|
|
51
|
+
check_consistency
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def check_consistency
|
|
56
|
+
init_clay_project? and layouts_exist? and pages_exist? and public_exist?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def build
|
|
60
|
+
unless File.directory?(path("build"))
|
|
61
|
+
create_directory path("build")
|
|
62
|
+
end
|
|
63
|
+
publish_public
|
|
64
|
+
publish_pages
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def prepare_rack_config
|
|
68
|
+
unless File.exists? path("config.ru")
|
|
69
|
+
file = File.open(path("config.ru"), "w")
|
|
70
|
+
file.write "require 'rack/clay'\nrun Rack::Clay.new"
|
|
71
|
+
file.close
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def init_clay_project?
|
|
78
|
+
`touch #{path(".clay")}` unless File.exists? path(".clay")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def layouts_exist?
|
|
82
|
+
create_directory path("layouts")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def pages_exist?
|
|
86
|
+
create_directory path("pages")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def public_exist?
|
|
90
|
+
create_directory path("public")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def create_directory dirname
|
|
94
|
+
return if dirname.nil? || dirname.empty?
|
|
95
|
+
unless File.directory?(dirname)
|
|
96
|
+
puts "#{dirname} must be a directory"
|
|
97
|
+
FileUtils.rm_rf dirname
|
|
98
|
+
end
|
|
99
|
+
unless File.exists?(dirname)
|
|
100
|
+
puts "Creating directory: #{dirname}"
|
|
101
|
+
FileUtils.mkdir dirname
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def publish_pages
|
|
106
|
+
Dir.glob("pages/*.*").each { |page_path|
|
|
107
|
+
page = Page.new(page_path)
|
|
108
|
+
File.open(page.target, "w") {|f| f.write page.content }
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def publish_public
|
|
113
|
+
FileUtils.cp_r(Dir.glob("public/*"), "build")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def path filename
|
|
117
|
+
File.expand_path(File.join("#{@project_root}/", filename))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class Page
|
|
123
|
+
attr_reader :content
|
|
124
|
+
def initialize filename
|
|
125
|
+
case filename.split(".").last
|
|
126
|
+
when "md", "markdown" then @page_type = "markdown"
|
|
127
|
+
when "html" then @page_type = "html"
|
|
128
|
+
else raise "File type of #{filename} unknown.\nMaybe it belongs to the public directory?"
|
|
129
|
+
end
|
|
130
|
+
@filename = filename_within_pages filename
|
|
131
|
+
file_content = File.read(filename)
|
|
132
|
+
layout, raw_content, data = interpret file_content
|
|
133
|
+
@content = render raw_content, layout, @page_type, data
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def target
|
|
137
|
+
file_base = @filename.split(".")[0..-2].join('.')
|
|
138
|
+
"build/#{file_base}.html"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
def filename_within_pages filename
|
|
143
|
+
filename.split("/", 2)[1]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def render content, layout, page_type, data
|
|
147
|
+
data['content'] = parsed_content content, data
|
|
148
|
+
begin
|
|
149
|
+
layout_content = File.read(layout)
|
|
150
|
+
rescue NameError
|
|
151
|
+
raise "Layout #{layout} is missing.\nPlease create one in layouts directory"
|
|
152
|
+
end
|
|
153
|
+
rendered_content = Mustache.render(layout_content, data)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def parsed_content content, data
|
|
157
|
+
case @page_type
|
|
158
|
+
when "markdown" then return Mustache.render(RDiscount.new(content).to_html.strip, data)
|
|
159
|
+
when "html" then return Mustache.render(content, data)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def interpret content
|
|
164
|
+
layout_name = "default"
|
|
165
|
+
data = {}
|
|
166
|
+
if content.match(/^(\s*---(.+)---\s*)/m)
|
|
167
|
+
data = YAML.load($2.strip)
|
|
168
|
+
content = content.gsub($1, "")
|
|
169
|
+
layout_name = data.delete "layout" if data["layout"]
|
|
170
|
+
end
|
|
171
|
+
layout = "layouts/#{layout_name}.html"
|
|
172
|
+
texts = Dir.glob("texts/*")
|
|
173
|
+
texts.each{|filename| data.merge! Text.new(filename).to_h }
|
|
174
|
+
return layout, content, data
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
class Text
|
|
179
|
+
attr_reader :content
|
|
180
|
+
def initialize filename
|
|
181
|
+
extension = File.extname(filename)
|
|
182
|
+
@name = File.basename(filename, extension)
|
|
183
|
+
raise "Text must be a markdown file" unless extension == ".md" or extension == ".markdown"
|
|
184
|
+
@content = parse File.read(filename)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def to_h
|
|
188
|
+
{"text-#{@name}" => content}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
def parse content
|
|
194
|
+
RDiscount.new(content).to_html.strip
|
|
195
|
+
end
|
|
196
|
+
end
|
data/src/rack/clay.rb
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require "rack"
|
|
2
|
+
require "rack/request"
|
|
3
|
+
require "rack/response"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "yaml"
|
|
6
|
+
|
|
7
|
+
module Rack
|
|
8
|
+
class Clay
|
|
9
|
+
def initialize(opts = {})
|
|
10
|
+
@configfile = ::File.join(::Dir.pwd,"config.yaml")
|
|
11
|
+
@config = {}
|
|
12
|
+
if ::File.exist?(@configfile)
|
|
13
|
+
puts "Reading configs... "
|
|
14
|
+
@config = ::YAML.load(::File.read(@configfile))
|
|
15
|
+
@config = (@config.class == FalseClass ? {} : @config)
|
|
16
|
+
if @config[:destination].nil?
|
|
17
|
+
@path = opts[:destination].nil? ? "build" : opts[:destination]
|
|
18
|
+
else
|
|
19
|
+
opts.merge!(@config)
|
|
20
|
+
@path = @config[:destination].nil? ? "build" : @config[:destination]
|
|
21
|
+
end
|
|
22
|
+
puts @config.inspect
|
|
23
|
+
puts "Ready."
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@path = "build"
|
|
27
|
+
@path = @config["build_path"] if @config["build_path"]
|
|
28
|
+
@mimes = Rack::Mime::MIME_TYPES.map{|k,v| /#{k.gsub('.','\.')}$/i }
|
|
29
|
+
end
|
|
30
|
+
def call(env)
|
|
31
|
+
rebuild
|
|
32
|
+
|
|
33
|
+
request = Request.new(env)
|
|
34
|
+
path_info = request.path_info
|
|
35
|
+
@files = ::Dir[@path + "/**/*"].inspect
|
|
36
|
+
if @files.include?(path_info)
|
|
37
|
+
if path_info =~ /(\/?)$/
|
|
38
|
+
if @mimes.collect {|regex| path_info =~ regex }.compact.empty?
|
|
39
|
+
path_info += $1.nil? ? "/index.html" : "index.html"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
mime = mime(path_info)
|
|
43
|
+
|
|
44
|
+
file = file_info(@path + path_info)
|
|
45
|
+
body = file[:body]
|
|
46
|
+
time = file[:time]
|
|
47
|
+
|
|
48
|
+
if time == request.env['HTTP_IF_MODIFIED_SINCE']
|
|
49
|
+
[304, {'Last-Modified' => time}, []]
|
|
50
|
+
else
|
|
51
|
+
[200, {"Content-Type" => mime, "Content-length" => body.length.to_s, 'Last-Modified' => time}, [body]]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
else
|
|
55
|
+
status, body, path_info = ::File.exist?(@path+"/404.html") ? [404,file_info(@path+"/404.html")[:body],"404.html"] : [404,"Not found","404.html"]
|
|
56
|
+
mime = mime(path_info)
|
|
57
|
+
if !@compiling
|
|
58
|
+
[status, {"Content-Type" => mime, "Content-length" => body.length.to_s}, [body]]
|
|
59
|
+
else
|
|
60
|
+
[200, {"Content-Type" => "text/plain"}, ["This site is currently generating pages. Please reload this page after 10 secs."]]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def file_info(path)
|
|
66
|
+
expand_path = ::File.expand_path(path)
|
|
67
|
+
::File.open(expand_path, 'r') do |f|
|
|
68
|
+
{:body => f.read, :time => f.mtime.httpdate, :expand_path => expand_path}
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def mime(path_info)
|
|
73
|
+
if path_info !~ /html$/i
|
|
74
|
+
ext = $1 if path_info =~ /(\.\S+)$/
|
|
75
|
+
Mime.mime_type((ext.nil? ? ".html" : ext))
|
|
76
|
+
else
|
|
77
|
+
Mime.mime_type(".html")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def rebuild
|
|
82
|
+
if @config["autoreload"]
|
|
83
|
+
require 'json'
|
|
84
|
+
clayfile = ::File.join(::Dir.pwd, ".clay")
|
|
85
|
+
old_file_hashes = ::File.read(clayfile)
|
|
86
|
+
new_file_hashes = get_file_hashes
|
|
87
|
+
changes = compare_file_hashes old_file_hashes, new_file_hashes.to_json
|
|
88
|
+
unless changes
|
|
89
|
+
begin
|
|
90
|
+
require 'clay'
|
|
91
|
+
::Clay.form
|
|
92
|
+
rescue LoadError
|
|
93
|
+
raise "Could not load clay"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
::File.open(clayfile, "w") {|f| f.write(new_file_hashes.to_json)}
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def get_file_hashes
|
|
101
|
+
files = ::Dir.glob("**/*") - [@path] - ::Dir.glob(@path + "/**/*")
|
|
102
|
+
file_hashes_array = files.map {|filename| [filename, file_hash(filename)]}
|
|
103
|
+
file_hashes = Hash[*file_hashes_array.flatten]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def file_hash filename
|
|
107
|
+
::File.new(filename).mtime.to_s
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def compare_file_hashes old, new
|
|
111
|
+
old == new
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clay
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 13
|
|
5
|
+
prerelease: false
|
|
6
|
+
segments:
|
|
7
|
+
- 1
|
|
8
|
+
- 1
|
|
9
|
+
version: "1.1"
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Pavlo Kerestey
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2010-12-27 00:00:00 +01:00
|
|
18
|
+
default_executable:
|
|
19
|
+
dependencies:
|
|
20
|
+
- !ruby/object:Gem::Dependency
|
|
21
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
|
22
|
+
none: false
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
hash: 29
|
|
27
|
+
segments:
|
|
28
|
+
- 0
|
|
29
|
+
- 11
|
|
30
|
+
version: "0.11"
|
|
31
|
+
name: mustache
|
|
32
|
+
requirement: *id001
|
|
33
|
+
prerelease: false
|
|
34
|
+
type: :runtime
|
|
35
|
+
- !ruby/object:Gem::Dependency
|
|
36
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
|
37
|
+
none: false
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
hash: 3
|
|
42
|
+
segments:
|
|
43
|
+
- 0
|
|
44
|
+
version: "0"
|
|
45
|
+
name: rdiscount
|
|
46
|
+
requirement: *id002
|
|
47
|
+
prerelease: false
|
|
48
|
+
type: :runtime
|
|
49
|
+
- !ruby/object:Gem::Dependency
|
|
50
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
|
51
|
+
none: false
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
hash: 3
|
|
56
|
+
segments:
|
|
57
|
+
- 0
|
|
58
|
+
version: "0"
|
|
59
|
+
name: rack
|
|
60
|
+
requirement: *id003
|
|
61
|
+
prerelease: false
|
|
62
|
+
type: :runtime
|
|
63
|
+
- !ruby/object:Gem::Dependency
|
|
64
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
hash: 3
|
|
70
|
+
segments:
|
|
71
|
+
- 0
|
|
72
|
+
version: "0"
|
|
73
|
+
name: passenger
|
|
74
|
+
requirement: *id004
|
|
75
|
+
prerelease: false
|
|
76
|
+
type: :runtime
|
|
77
|
+
- !ruby/object:Gem::Dependency
|
|
78
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
|
79
|
+
none: false
|
|
80
|
+
requirements:
|
|
81
|
+
- - ~>
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
hash: 15
|
|
84
|
+
segments:
|
|
85
|
+
- 1
|
|
86
|
+
- 0
|
|
87
|
+
version: "1.0"
|
|
88
|
+
name: bundler
|
|
89
|
+
requirement: *id005
|
|
90
|
+
prerelease: false
|
|
91
|
+
type: :development
|
|
92
|
+
- !ruby/object:Gem::Dependency
|
|
93
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
|
94
|
+
none: false
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
hash: 3
|
|
99
|
+
segments:
|
|
100
|
+
- 0
|
|
101
|
+
version: "0"
|
|
102
|
+
name: rspec
|
|
103
|
+
requirement: *id006
|
|
104
|
+
prerelease: false
|
|
105
|
+
type: :development
|
|
106
|
+
- !ruby/object:Gem::Dependency
|
|
107
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
|
108
|
+
none: false
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
hash: 3
|
|
113
|
+
segments:
|
|
114
|
+
- 0
|
|
115
|
+
version: "0"
|
|
116
|
+
name: rake
|
|
117
|
+
requirement: *id007
|
|
118
|
+
prerelease: false
|
|
119
|
+
type: :development
|
|
120
|
+
description: A sticky clay to automatically form a static website using Mustache templates and markdown files.
|
|
121
|
+
email:
|
|
122
|
+
- pavlo@kerestey.net
|
|
123
|
+
executables:
|
|
124
|
+
- clay
|
|
125
|
+
extensions: []
|
|
126
|
+
|
|
127
|
+
extra_rdoc_files: []
|
|
128
|
+
|
|
129
|
+
files:
|
|
130
|
+
- src/clay.rb
|
|
131
|
+
- src/rack/clay.rb
|
|
132
|
+
- bin/clay
|
|
133
|
+
has_rdoc: true
|
|
134
|
+
homepage: http://kerestey.net/clay
|
|
135
|
+
licenses: []
|
|
136
|
+
|
|
137
|
+
post_install_message:
|
|
138
|
+
rdoc_options: []
|
|
139
|
+
|
|
140
|
+
require_paths:
|
|
141
|
+
- - src
|
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
|
+
none: false
|
|
144
|
+
requirements:
|
|
145
|
+
- - ">="
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
hash: 3
|
|
148
|
+
segments:
|
|
149
|
+
- 0
|
|
150
|
+
version: "0"
|
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
|
+
none: false
|
|
153
|
+
requirements:
|
|
154
|
+
- - ">="
|
|
155
|
+
- !ruby/object:Gem::Version
|
|
156
|
+
hash: 23
|
|
157
|
+
segments:
|
|
158
|
+
- 1
|
|
159
|
+
- 3
|
|
160
|
+
- 6
|
|
161
|
+
version: 1.3.6
|
|
162
|
+
requirements: []
|
|
163
|
+
|
|
164
|
+
rubyforge_project: clay
|
|
165
|
+
rubygems_version: 1.3.7
|
|
166
|
+
signing_key:
|
|
167
|
+
specification_version: 3
|
|
168
|
+
summary: A sticky clay to automatically form a static website.
|
|
169
|
+
test_files: []
|
|
170
|
+
|