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.
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
+