clay 1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/bin/clay +29 -0
  2. data/src/clay.rb +196 -0
  3. data/src/rack/clay.rb +114 -0
  4. metadata +170 -0
@@ -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
@@ -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
@@ -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
+