mattt-staticmatic 0.10.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/History.txt +6 -0
- data/Manifest.txt +24 -0
- data/README.markdown +50 -0
- data/Rakefile +31 -0
- data/bin/staticmatic +23 -0
- data/lib/staticmatic.rb +20 -0
- data/lib/staticmatic/base.rb +87 -0
- data/lib/staticmatic/configuration.rb +16 -0
- data/lib/staticmatic/error.rb +17 -0
- data/lib/staticmatic/helpers.rb +197 -0
- data/lib/staticmatic/mixins/build.rb +46 -0
- data/lib/staticmatic/mixins/helpers.rb +15 -0
- data/lib/staticmatic/mixins/render.rb +125 -0
- data/lib/staticmatic/mixins/rescue.rb +12 -0
- data/lib/staticmatic/mixins/server.rb +6 -0
- data/lib/staticmatic/mixins/setup.rb +20 -0
- data/lib/staticmatic/server.rb +94 -0
- data/lib/staticmatic/template_error.rb +40 -0
- data/lib/staticmatic/templates/default/application.haml +7 -0
- data/lib/staticmatic/templates/default/application.sass +4 -0
- data/lib/staticmatic/templates/default/index.haml +1 -0
- data/lib/staticmatic/templates/rescues/default.haml +7 -0
- data/lib/staticmatic/templates/rescues/template.haml +18 -0
- data/test/test_helper.rb +20 -0
- metadata +114 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
bin/staticmatic
|
6
|
+
lib/staticmatic.rb
|
7
|
+
lib/staticmatic/base.rb
|
8
|
+
lib/staticmatic/configuration.rb
|
9
|
+
lib/staticmatic/error.rb
|
10
|
+
lib/staticmatic/helpers.rb
|
11
|
+
lib/staticmatic/mixins/build.rb
|
12
|
+
lib/staticmatic/mixins/helpers.rb
|
13
|
+
lib/staticmatic/mixins/render.rb
|
14
|
+
lib/staticmatic/mixins/server.rb
|
15
|
+
lib/staticmatic/mixins/setup.rb
|
16
|
+
lib/staticmatic/server.rb
|
17
|
+
lib/staticmatic/templates/default/application.haml
|
18
|
+
lib/staticmatic/templates/default/application.sass
|
19
|
+
lib/staticmatic/templates/default/index.haml
|
20
|
+
lib/staticmatic/templates/default/index.haml
|
21
|
+
lib/staticmatic/templates/rescues/default.haml
|
22
|
+
lib/staticmatic/templates/rescues/template.haml
|
23
|
+
lib/staticmatic/template_error.rb
|
24
|
+
lib/staticmatic/mixins/rescue.rb
|
data/README.markdown
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# StaticMatic
|
2
|
+
|
3
|
+
*For information on Haml & Sass please see [haml.hamptoncatlin.com](http://haml.hamptoncatlin.com)*.
|
4
|
+
|
5
|
+
## What's it all about?
|
6
|
+
|
7
|
+
CMS is overrated. A lot of the time, clients want us to do what we do
|
8
|
+
best - well designed pages with structured, accessible and maintainable markup & styling.
|
9
|
+
|
10
|
+
CMSs are often perfect for this, but sometimes they can be restrictive and more cumbersome
|
11
|
+
than just working with good ol' source code. At the same time we want our code to be
|
12
|
+
structured, DRY and flexible.
|
13
|
+
|
14
|
+
Enter **StaticMatic**.
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
StaticMatic will set up a basic site structure for you with this command:
|
19
|
+
|
20
|
+
staticmatic setup <directory>
|
21
|
+
|
22
|
+
After this command you'll have the following files:
|
23
|
+
|
24
|
+
<directory>/
|
25
|
+
site/
|
26
|
+
images/
|
27
|
+
stylesheets/
|
28
|
+
javascripts/
|
29
|
+
src/
|
30
|
+
helpers/
|
31
|
+
layouts/
|
32
|
+
application.haml
|
33
|
+
pages/
|
34
|
+
index.haml
|
35
|
+
stylesheets/
|
36
|
+
application.sass
|
37
|
+
|
38
|
+
StaticMatic sets you up with a sample layout, stylesheet and page file. Once you've
|
39
|
+
edited the pages and stylesheets, you can generate the static site:
|
40
|
+
|
41
|
+
staticmatic build <directory>
|
42
|
+
|
43
|
+
All of the pages are parsed and wrapped up in application.haml and put into the site directory.
|
44
|
+
|
45
|
+
## Templates
|
46
|
+
|
47
|
+
StaticMatic adds a few helpers to the core Haml helpers:
|
48
|
+
|
49
|
+
= link 'Title', 'url'
|
50
|
+
= img 'my_image.jpg'
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen hoe].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/staticmatic'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('staticmatic', StaticMatic::VERSION) do |p|
|
7
|
+
p.developer('Stephen Bartholomew', 'steve@curve21.com')
|
8
|
+
p.rubyforge_name = p.name
|
9
|
+
p.extra_deps = [
|
10
|
+
['haml','>= 2.0'],
|
11
|
+
['mongrel','>= 1.0']
|
12
|
+
]
|
13
|
+
p.extra_dev_deps = [
|
14
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
15
|
+
]
|
16
|
+
|
17
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
+
|
26
|
+
|
27
|
+
desc "Run all unit tests"
|
28
|
+
Rake::TestTask.new(:test) do |t|
|
29
|
+
t.test_files = Dir.glob("test/*_test.rb")
|
30
|
+
t.verbose = true
|
31
|
+
end
|
data/bin/staticmatic
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/staticmatic'
|
4
|
+
|
5
|
+
command = ARGV[0]
|
6
|
+
directory = ARGV[1]
|
7
|
+
|
8
|
+
if !command || !directory
|
9
|
+
puts "Usage: #{$0} <build|setup|preview> <directory>"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
configuration = StaticMatic::Configuration.new
|
14
|
+
|
15
|
+
config_file = "#{directory}/src/configuration.rb"
|
16
|
+
|
17
|
+
if File.exists?(config_file)
|
18
|
+
config = File.read(config_file)
|
19
|
+
eval(config)
|
20
|
+
end
|
21
|
+
|
22
|
+
staticmatic = StaticMatic::Base.new(directory, configuration)
|
23
|
+
staticmatic.run(command)
|
data/lib/staticmatic.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'haml'
|
3
|
+
require 'sass'
|
4
|
+
require 'mongrel'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module StaticMatic
|
8
|
+
VERSION = '0.10.1'
|
9
|
+
end
|
10
|
+
|
11
|
+
["render", "build", "setup", "server", "helpers", "rescue"].each do |mixin|
|
12
|
+
require File.join(File.dirname(__FILE__), "staticmatic", "mixins", mixin)
|
13
|
+
end
|
14
|
+
|
15
|
+
["base", "configuration", "error", "server", "helpers", "template_error"].each do |lib|
|
16
|
+
require File.join(File.dirname(__FILE__), "staticmatic", lib)
|
17
|
+
end
|
18
|
+
|
19
|
+
Haml::Helpers.class_eval("include StaticMatic::Helpers")
|
20
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module StaticMatic
|
2
|
+
# Directories generated for a new site setup
|
3
|
+
BASE_DIRS = %w{
|
4
|
+
site/
|
5
|
+
site/stylesheets
|
6
|
+
site/images
|
7
|
+
site/javascripts
|
8
|
+
src/
|
9
|
+
src/pages/
|
10
|
+
src/layouts
|
11
|
+
src/stylesheets
|
12
|
+
src/helpers
|
13
|
+
}
|
14
|
+
|
15
|
+
# Templates for setup and their location
|
16
|
+
TEMPLATES = {
|
17
|
+
'application.haml' => 'layouts',
|
18
|
+
'application.sass' => 'stylesheets',
|
19
|
+
'index.haml' => 'pages'
|
20
|
+
}
|
21
|
+
|
22
|
+
class Base
|
23
|
+
|
24
|
+
include StaticMatic::RenderMixin
|
25
|
+
include StaticMatic::BuildMixin
|
26
|
+
include StaticMatic::SetupMixin
|
27
|
+
include StaticMatic::HelpersMixin
|
28
|
+
include StaticMatic::ServerMixin
|
29
|
+
include StaticMatic::RescueMixin
|
30
|
+
|
31
|
+
attr_accessor :configuration
|
32
|
+
attr_reader :current_page, :src_dir, :site_dir
|
33
|
+
|
34
|
+
def current_file
|
35
|
+
@current_file_stack[0] || ""
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(base_dir, configuration = Configuration.new)
|
39
|
+
@configuration = configuration
|
40
|
+
@current_page = nil
|
41
|
+
@current_file_stack = []
|
42
|
+
@base_dir = base_dir
|
43
|
+
@src_dir = "#{@base_dir}/src"
|
44
|
+
@site_dir = "#{@base_dir}/site"
|
45
|
+
@templates_dir = File.dirname(__FILE__) + '/templates/default/'
|
46
|
+
@layout = "application"
|
47
|
+
@scope = Object.new
|
48
|
+
@scope.instance_variable_set("@staticmatic", self)
|
49
|
+
load_helpers
|
50
|
+
end
|
51
|
+
|
52
|
+
def base_dir
|
53
|
+
@base_dir
|
54
|
+
end
|
55
|
+
|
56
|
+
def run(command)
|
57
|
+
if %w(build setup preview).include?(command)
|
58
|
+
send(command)
|
59
|
+
else
|
60
|
+
puts "#{command} is not a valid StaticMatic command"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# TODO: DRY this _exists? section up
|
65
|
+
def template_exists?(name, dir = '')
|
66
|
+
File.exists?(File.join(@src_dir, 'pages', dir, "#{name}.haml")) || File.exists?(File.join(@src_dir, 'stylesheets', "#{name}.sass"))
|
67
|
+
end
|
68
|
+
|
69
|
+
def layout_exists?(name)
|
70
|
+
File.exists? full_layout_path(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def template_directory?(path)
|
74
|
+
File.directory?(File.join(@src_dir, 'pages', path))
|
75
|
+
end
|
76
|
+
|
77
|
+
def full_layout_path(name)
|
78
|
+
"#{@src_dir}/layouts/#{name}.haml"
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
def base_dirs
|
83
|
+
StaticMatic::BASE_DIRS
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StaticMatic
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :preview_server_port
|
4
|
+
|
5
|
+
attr_accessor :preview_server_host
|
6
|
+
|
7
|
+
attr_accessor :use_extensions_for_page_links
|
8
|
+
attr_accessor :sass_options
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
self.preview_server_port = 3000
|
12
|
+
self.use_extensions_for_page_links = true
|
13
|
+
self.sass_options = {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module StaticMatic
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :line
|
4
|
+
|
5
|
+
attr_reader :filename
|
6
|
+
|
7
|
+
def initialize(lineno, filename, message)
|
8
|
+
@line = lineno
|
9
|
+
@filename = filename
|
10
|
+
@message = message
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
"#{@filename}, line #{@line}: #{@message}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module StaticMatic
|
2
|
+
module Helpers
|
3
|
+
self.extend self
|
4
|
+
|
5
|
+
# Generates links to all stylesheets in the source directory
|
6
|
+
# = stylesheets
|
7
|
+
# or specific stylesheets in a specific order
|
8
|
+
# = stylesheets :reset, :application
|
9
|
+
# Can also pass options hash in at the end so you can specify :media => :print
|
10
|
+
def stylesheets(*params)
|
11
|
+
options = {}
|
12
|
+
if params.last.is_a?(Hash)
|
13
|
+
options = params.last
|
14
|
+
params.slice!(-1, 1)
|
15
|
+
end
|
16
|
+
options[:media] = 'all' unless options.has_key?(:media)
|
17
|
+
options[:rel] = 'stylesheet'; options[:type] = 'text/css'
|
18
|
+
|
19
|
+
relative_path = current_page_relative_path
|
20
|
+
|
21
|
+
output = ""
|
22
|
+
if params.length == 0
|
23
|
+
# no specific files requested so include all in no particular order
|
24
|
+
stylesheet_dir = File.join(@staticmatic.src_dir, 'stylesheets')
|
25
|
+
stylesheet_directories = Dir[File.join(stylesheet_dir, '**','*.sass')]
|
26
|
+
|
27
|
+
# Bit of a hack here - adds any stylesheets that exist in the site/ dir that haven't been generated from source sass
|
28
|
+
Dir[File.join(@staticmatic.site_dir, 'stylesheets', '*.css')].each do |filename|
|
29
|
+
search_filename = File.basename(filename).chomp(File.extname(filename))
|
30
|
+
|
31
|
+
already_included = false
|
32
|
+
stylesheet_directories.each do |path|
|
33
|
+
if File.basename(path).include?(search_filename)
|
34
|
+
already_included = true
|
35
|
+
break
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
stylesheet_directories << filename unless already_included
|
40
|
+
end
|
41
|
+
|
42
|
+
stylesheet_directories.each do |path|
|
43
|
+
filename_without_extension = File.basename(path).chomp(File.extname(path))
|
44
|
+
|
45
|
+
options[:href] = "#{relative_path}stylesheets/#{filename_without_extension}.css"
|
46
|
+
output << tag(:link, options)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
#specific files requested and in a specific order
|
50
|
+
params.each do |file|
|
51
|
+
if File.exist?(File.join(@staticmatic.src_dir, 'stylesheets', "#{file}.sass")) ||
|
52
|
+
File.exist?(File.join(@staticmatic.site_dir, 'stylesheets', "#{file}.css"))
|
53
|
+
options[:href] = "#{relative_path}stylesheets/#{file}.css"
|
54
|
+
output << tag(:link, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
output
|
60
|
+
end
|
61
|
+
|
62
|
+
# Generate javascript source tags for the specified files
|
63
|
+
#
|
64
|
+
# javascripts('test') -> <script language="javascript" src="javascripts/test.js"></script>
|
65
|
+
#
|
66
|
+
def javascripts(*files)
|
67
|
+
relative_path = current_page_relative_path
|
68
|
+
|
69
|
+
output = ""
|
70
|
+
files.each do |file|
|
71
|
+
file_str = file.to_s
|
72
|
+
src = file_str.match(%r{^((\.\.?)?/|https?://)}) ? file_str : "#{relative_path}javascripts/#{file_str}.js"
|
73
|
+
output << tag(:script, :language => 'javascript', :src => src, :type => "text/javascript") { "" }
|
74
|
+
end
|
75
|
+
output
|
76
|
+
end
|
77
|
+
|
78
|
+
# Generates a form text field
|
79
|
+
#
|
80
|
+
def text_field(name, value, options = {})
|
81
|
+
options.merge!(:type => "text", :name => name, :value => value)
|
82
|
+
tag(:input, options)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Generate a form textarea
|
86
|
+
#
|
87
|
+
def text_area(name, value, options = {})
|
88
|
+
options.merge!(:name => name)
|
89
|
+
tag(:textarea, options) { value }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Generate an HTML link
|
93
|
+
#
|
94
|
+
# If only the title is passed, it will automatically
|
95
|
+
# create a link from this value:
|
96
|
+
#
|
97
|
+
# link('Test') -> <a href="test.html">Test</a>
|
98
|
+
#
|
99
|
+
def link(title, href = "", options = {})
|
100
|
+
if href.is_a?(Hash)
|
101
|
+
options = href
|
102
|
+
href = ""
|
103
|
+
end
|
104
|
+
|
105
|
+
if href.nil? || href.strip.length < 1
|
106
|
+
path_prefix = ''
|
107
|
+
if title.match(/^(\.\.?)?\//)
|
108
|
+
# starts with relative path so strip it off and prepend it to the urlified title
|
109
|
+
path_prefix_match = title.match(/^[^\s]*\//)
|
110
|
+
path_prefix = path_prefix_match[0] if path_prefix_match
|
111
|
+
title = title[path_prefix.length, title.length]
|
112
|
+
end
|
113
|
+
href = path_prefix + urlify(title) + ".html"
|
114
|
+
end
|
115
|
+
|
116
|
+
options[:href] = "#{current_page_relative_path(href)}#{href}"
|
117
|
+
|
118
|
+
local_page = (options[:href].match(/^(\#|.+?\:)/) == nil)
|
119
|
+
unless @staticmatic.configuration.use_extensions_for_page_links || !local_page
|
120
|
+
options[:href].chomp!(".html")
|
121
|
+
options[:href].chomp!("index") if options[:href][-5, 5] == 'index'
|
122
|
+
end
|
123
|
+
|
124
|
+
tag(:a, options) { title }
|
125
|
+
end
|
126
|
+
alias link_to link
|
127
|
+
|
128
|
+
# Generates an image tag always relative to the current page unless absolute path or http url specified.
|
129
|
+
#
|
130
|
+
# img('test_image.gif') -> <img src="/images/test_image.gif" alt="Test image"/>
|
131
|
+
# img('contact/test_image.gif') -> <img src="/images/contact/test_image.gif" alt="Test image"/>
|
132
|
+
# img('http://localhost/test_image.gif') -> <img src="http://localhost/test_image.gif" alt="Test image"/>
|
133
|
+
def img(name, options = {})
|
134
|
+
options[:src] = name.match(%r{^((\.\.?)?/|https?://)}) ? name : "#{current_page_relative_path}images/#{name}"
|
135
|
+
options[:alt] ||= name.split('/').last.split('.').first.capitalize.gsub(/_|-/, ' ')
|
136
|
+
tag :img, options
|
137
|
+
end
|
138
|
+
|
139
|
+
# Generates HTML tags:
|
140
|
+
#
|
141
|
+
# tag(:br) -> <br/>
|
142
|
+
# tag(:a, :href => 'test.html') { "Test" } -> <a href="test.html">Test</a>
|
143
|
+
#
|
144
|
+
def tag(name, options = {}, &block)
|
145
|
+
options[:id] ||= options[:name] if options[:name]
|
146
|
+
output = "<#{name}"
|
147
|
+
options.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |key|
|
148
|
+
output << " #{key}=\"#{options[key]}\"" if options[key]
|
149
|
+
end
|
150
|
+
|
151
|
+
if block_given?
|
152
|
+
output << ">"
|
153
|
+
output << yield
|
154
|
+
output << "</#{name}>"
|
155
|
+
else
|
156
|
+
output << "/>"
|
157
|
+
end
|
158
|
+
output
|
159
|
+
end
|
160
|
+
|
161
|
+
# Generates a URL friendly string from the value passed:
|
162
|
+
#
|
163
|
+
# "We love Haml" -> "we_love_haml"
|
164
|
+
# "Elf & Ham" -> "elf_and_ham"
|
165
|
+
# "Stephen's gem" -> "stephens_gem"
|
166
|
+
#
|
167
|
+
def urlify(string)
|
168
|
+
string.tr(" ", "_").
|
169
|
+
sub("&", "and").
|
170
|
+
sub("@", "at").
|
171
|
+
tr("^A-Za-z0-9_", "").
|
172
|
+
sub(/_{2,}/, "_").
|
173
|
+
downcase
|
174
|
+
end
|
175
|
+
|
176
|
+
# Include a partial template
|
177
|
+
def partial(name, options = {})
|
178
|
+
@staticmatic.generate_partial(name, options)
|
179
|
+
end
|
180
|
+
|
181
|
+
def current_page
|
182
|
+
@staticmatic.current_page
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def current_page_relative_path(current_path = nil)
|
188
|
+
if current_path.nil? || current_path.match(/^((\.\.?)?\/|\#|.+?\:)/) == nil
|
189
|
+
current_page_depth = current_page.split('/').length - 2;
|
190
|
+
(current_page_depth > 0) ? ([ '..' ] * current_page_depth).join('/') + '/' : ''
|
191
|
+
else
|
192
|
+
''
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module StaticMatic::BuildMixin
|
2
|
+
|
3
|
+
def build
|
4
|
+
build_css
|
5
|
+
build_html
|
6
|
+
end
|
7
|
+
|
8
|
+
# Build HTML from the source files
|
9
|
+
def build_html
|
10
|
+
Dir["#{@src_dir}/pages/**/*.haml"].each do |path|
|
11
|
+
next if File.basename(path) =~ /^\_/ # skip partials
|
12
|
+
file_dir, template = source_template_from_path(path.sub(/^#{@src_dir}\/pages/, ''))
|
13
|
+
save_page(File.join(file_dir, template), generate_html_with_layout(template, file_dir))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Build CSS from the source files
|
18
|
+
def build_css
|
19
|
+
Dir["#{@src_dir}/stylesheets/**/*.sass"].each do |path|
|
20
|
+
file_dir, template = source_template_from_path(path.sub(/^#{@src_dir}\/stylesheets/, ''))
|
21
|
+
save_stylesheet(File.join(file_dir, template), generate_css(template, file_dir))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_file(from, to)
|
26
|
+
FileUtils.cp(from, to)
|
27
|
+
end
|
28
|
+
|
29
|
+
def save_page(filename, content)
|
30
|
+
generate_site_file(filename, 'html', content)
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_stylesheet(filename, content)
|
34
|
+
generate_site_file(File.join('stylesheets', filename), 'css', content)
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_site_file(filename, extension, content)
|
38
|
+
path = File.join(@site_dir,"#{filename}.#{extension}")
|
39
|
+
FileUtils.mkdir_p(File.dirname(path))
|
40
|
+
File.open(path, 'w+') do |f|
|
41
|
+
f << content
|
42
|
+
end
|
43
|
+
|
44
|
+
puts "created #{path}"
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module StaticMatic::HelpersMixin
|
2
|
+
# Loads any helpers present in the helpers dir and mixes them into the template helpers
|
3
|
+
def load_helpers
|
4
|
+
|
5
|
+
Dir["#{@src_dir}/helpers/**/*_helper.rb"].each do |helper|
|
6
|
+
load_helper(helper)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_helper(helper)
|
11
|
+
load helper
|
12
|
+
module_name = File.basename(helper, '.rb').gsub(/(^|\_)./) { |c| c.upcase }.gsub(/\_/, '')
|
13
|
+
Haml::Helpers.class_eval("include #{module_name}")
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module StaticMatic::RenderMixin
|
2
|
+
|
3
|
+
def source_for_layout
|
4
|
+
if layout_exists?(@layout)
|
5
|
+
File.read(full_layout_path(@layout))
|
6
|
+
else
|
7
|
+
raise StaticMatic::Error.new("", full_layout_path(@layout), "Layout not found")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Generate html from source file:
|
12
|
+
# generate_html("index")
|
13
|
+
def generate_html(source_file, source_dir = '')
|
14
|
+
full_file_path = File.join(@src_dir, 'pages', source_dir, "#{source_file}.haml")
|
15
|
+
|
16
|
+
begin
|
17
|
+
# clear all scope variables except @staticmatic
|
18
|
+
@scope.instance_variables.each do |var|
|
19
|
+
@scope.instance_variable_set(var, nil) unless var == '@staticmatic'
|
20
|
+
end
|
21
|
+
html = generate_html_from_template_source(File.read(full_file_path))
|
22
|
+
|
23
|
+
@layout = determine_layout(source_dir)
|
24
|
+
rescue StaticMatic::TemplateError => e
|
25
|
+
raise e # re-raise inline errors
|
26
|
+
rescue Exception => e
|
27
|
+
raise StaticMatic::TemplateError.new(full_file_path, e)
|
28
|
+
end
|
29
|
+
|
30
|
+
html
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_html_with_layout(source, source_dir = '')
|
34
|
+
@current_page = File.join(source_dir, "#{source}.html")
|
35
|
+
@current_file_stack.unshift(File.join(source_dir, "#{source}.haml"))
|
36
|
+
begin
|
37
|
+
template_content = generate_html(source, source_dir)
|
38
|
+
@layout = determine_layout(source_dir)
|
39
|
+
generate_html_from_template_source(source_for_layout) { template_content }
|
40
|
+
rescue Exception => e
|
41
|
+
render_rescue_from_error(e)
|
42
|
+
ensure
|
43
|
+
@current_page = nil
|
44
|
+
@current_file_stack.shift
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_partial(name, options = {})
|
49
|
+
partial_dir, partial_name = File.dirname(self.current_file), name # default relative to current file
|
50
|
+
partial_dir, partial_name = File.split(name) if name.index('/') # contains a path so it's absolute from src/pages dir
|
51
|
+
partial_name = "_#{partial_name}.haml"
|
52
|
+
|
53
|
+
partial_path = File.join(@src_dir, 'pages', partial_dir, partial_name)
|
54
|
+
unless File.exists?(partial_path)
|
55
|
+
# couldn't find it in the pages subdirectory tree so try old way (ignoring the path)
|
56
|
+
partial_dir = 'partials'
|
57
|
+
partial_name = "#{File.basename(name)}.haml"
|
58
|
+
partial_path = File.join(@src_dir, partial_dir, partial_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
if File.exists?(partial_path)
|
62
|
+
partial_rel_path = "/#{partial_dir}/#{partial_name}".gsub(/\/+/, '/')
|
63
|
+
@current_file_stack.unshift(partial_rel_path)
|
64
|
+
begin
|
65
|
+
generate_html_from_template_source(File.read(partial_path), options)
|
66
|
+
rescue Exception => e
|
67
|
+
raise StaticMatic::TemplateError.new(partial_path, e)
|
68
|
+
ensure
|
69
|
+
@current_file_stack.shift
|
70
|
+
end
|
71
|
+
else
|
72
|
+
raise StaticMatic::Error.new("", name, "Partial not found")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_css(source, source_dir = '')
|
77
|
+
full_file_path = File.join(@src_dir, 'stylesheets', source_dir, "#{source}.sass")
|
78
|
+
begin
|
79
|
+
sass_options = { :load_paths => [ File.join(@src_dir, 'stylesheets') ] }.merge(self.configuration.sass_options)
|
80
|
+
stylesheet = Sass::Engine.new(File.read(full_file_path), sass_options)
|
81
|
+
stylesheet.to_css
|
82
|
+
rescue Exception => e
|
83
|
+
render_rescue_from_error(StaticMatic::TemplateError.new(full_file_path, e))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Generates html from the passed source string
|
88
|
+
#
|
89
|
+
# generate_html_from_template_source("%h1 Welcome to My Site") -> "<h1>Welcome to My Site</h1>"
|
90
|
+
#
|
91
|
+
# Pass a block containing a string to yield within in the passed source:
|
92
|
+
#
|
93
|
+
# generate_html_from_template_source("content:\n= yield") { "blah" } -> "content: blah"
|
94
|
+
#
|
95
|
+
def generate_html_from_template_source(source, options = {})
|
96
|
+
html = Haml::Engine.new(source, self.configuration.haml_options.merge(options))
|
97
|
+
|
98
|
+
html.render(@scope, options) { yield }
|
99
|
+
end
|
100
|
+
|
101
|
+
def determine_layout(dir = '')
|
102
|
+
layout_name = "application"
|
103
|
+
|
104
|
+
if @scope.instance_variable_get("@layout")
|
105
|
+
layout_name = @scope.instance_variable_get("@layout")
|
106
|
+
elsif dir
|
107
|
+
dirs = dir.split("/")
|
108
|
+
dir_layout_name = dirs[1]
|
109
|
+
|
110
|
+
if layout_exists?(dir_layout_name)
|
111
|
+
layout_name = dir_layout_name
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
layout_name
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a raw template name from a source file path:
|
119
|
+
# source_template_from_path("/path/to/site/src/stylesheets/application.sass") -> "application"
|
120
|
+
def source_template_from_path(path)
|
121
|
+
file_dir, file_name = File.split(path)
|
122
|
+
file_name.chomp!(File.extname(file_name))
|
123
|
+
[ file_dir, file_name ]
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module StaticMatic::RescueMixin
|
2
|
+
# Pass back an error template for the given exception
|
3
|
+
def render_rescue_from_error(exception)
|
4
|
+
rescue_template = (exception.is_a?(StaticMatic::TemplateError)) ? "template" : "default"
|
5
|
+
|
6
|
+
error_template_path = File.expand_path(File.dirname(__FILE__) + "/../templates/rescues/#{rescue_template}.haml")
|
7
|
+
|
8
|
+
@scope.instance_variable_set("@exception", exception)
|
9
|
+
|
10
|
+
generate_html_from_template_source(File.read(error_template_path))
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module StaticMatic::SetupMixin
|
2
|
+
|
3
|
+
def setup
|
4
|
+
Dir.mkdir(@base_dir) unless File.exists?(@base_dir)
|
5
|
+
|
6
|
+
StaticMatic::BASE_DIRS.each do |directory|
|
7
|
+
directory = "#{@base_dir}/#{directory}"
|
8
|
+
if !File.exists?(directory)
|
9
|
+
Dir.mkdir(directory)
|
10
|
+
puts "created #{directory}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
StaticMatic::TEMPLATES.each do |template, destination|
|
15
|
+
copy_file("#{@templates_dir}/#{template}", "#{@src_dir}/#{destination}")
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "Done"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module StaticMatic
|
2
|
+
class Server < Mongrel::HttpHandler
|
3
|
+
@@file_only_methods = ["GET","HEAD"]
|
4
|
+
|
5
|
+
def initialize(staticmatic)
|
6
|
+
@files = Mongrel::DirHandler.new(staticmatic.site_dir, false)
|
7
|
+
@staticmatic = staticmatic
|
8
|
+
end
|
9
|
+
|
10
|
+
def process(request, response)
|
11
|
+
@staticmatic.load_helpers
|
12
|
+
path_info = request.params[Mongrel::Const::PATH_INFO]
|
13
|
+
get_or_head = @@file_only_methods.include? request.params[Mongrel::Const::REQUEST_METHOD]
|
14
|
+
|
15
|
+
file_dir, file_name, file_ext = expand_path(path_info)
|
16
|
+
|
17
|
+
# remove stylesheets/ directory if applicable
|
18
|
+
file_dir.gsub!(/^\/stylesheets\/?/, "")
|
19
|
+
|
20
|
+
file_dir = CGI::unescape(file_dir)
|
21
|
+
file_name = CGI::unescape(file_name)
|
22
|
+
|
23
|
+
if file_ext && file_ext.match(/html|css/)
|
24
|
+
response.start(200) do |head, out|
|
25
|
+
head["Content-Type"] = "text/#{file_ext}"
|
26
|
+
output = ""
|
27
|
+
|
28
|
+
if @staticmatic.template_exists?(file_name, file_dir) && !(File.basename(file_name) =~ /^\_/)
|
29
|
+
|
30
|
+
begin
|
31
|
+
if file_ext == "css"
|
32
|
+
output = @staticmatic.generate_css(file_name, file_dir)
|
33
|
+
else
|
34
|
+
output = @staticmatic.generate_html_with_layout(file_name, file_dir)
|
35
|
+
end
|
36
|
+
rescue StaticMatic::Error => e
|
37
|
+
output = e.message
|
38
|
+
end
|
39
|
+
else
|
40
|
+
if @files.can_serve(path_info)
|
41
|
+
@files.process(request,response)
|
42
|
+
else
|
43
|
+
output = "File not Found"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
out.write output
|
47
|
+
end
|
48
|
+
else
|
49
|
+
# try to serve static file from site dir
|
50
|
+
if @files.can_serve(path_info)
|
51
|
+
@files.process(request,response)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def expand_path(path_info)
|
57
|
+
dirname, basename = File.split(path_info)
|
58
|
+
|
59
|
+
extname = File.extname(path_info).sub(/^\./, '')
|
60
|
+
filename = basename.chomp(".#{extname}")
|
61
|
+
|
62
|
+
if extname.empty?
|
63
|
+
dir = File.join(dirname, filename)
|
64
|
+
is_dir = path_info[-1, 1] == '/' || (@staticmatic.template_directory?(dir) && !@staticmatic.template_exists?(filename, dirname))
|
65
|
+
if is_dir
|
66
|
+
dirname = dir
|
67
|
+
filename = 'index'
|
68
|
+
end
|
69
|
+
extname = 'html'
|
70
|
+
end
|
71
|
+
|
72
|
+
[ dirname, filename, extname ]
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
# Starts the StaticMatic preview server
|
77
|
+
def start(staticmatic)
|
78
|
+
port = staticmatic.configuration.preview_server_port || 3000
|
79
|
+
|
80
|
+
host = staticmatic.configuration.preview_server_host || ""
|
81
|
+
|
82
|
+
config = Mongrel::Configurator.new :host => host do
|
83
|
+
puts "Running Preview of #{staticmatic.base_dir} on #{host}:#{port}"
|
84
|
+
listener :port => port do
|
85
|
+
uri "/", :handler => Server.new(staticmatic)
|
86
|
+
end
|
87
|
+
trap("INT") { stop }
|
88
|
+
run
|
89
|
+
end
|
90
|
+
config.join
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class StaticMatic::TemplateError < StandardError
|
2
|
+
SOURCE_CODE_RADIUS = 3
|
3
|
+
|
4
|
+
attr_reader :original_exception, :backtrace
|
5
|
+
|
6
|
+
def initialize(template, original_exception)
|
7
|
+
@template, @original_exception = template, original_exception
|
8
|
+
@backtrace = original_exception.backtrace
|
9
|
+
|
10
|
+
@source = File.read(template)
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: Replace 'haml|sass' with any registered engines
|
14
|
+
def line_number
|
15
|
+
@line_number ||= $2 if backtrace.find { |line| line =~ /\((haml|sass)\)\:(\d+)/ }
|
16
|
+
end
|
17
|
+
|
18
|
+
def filename
|
19
|
+
@template
|
20
|
+
end
|
21
|
+
|
22
|
+
def source_extract(indentation = 0)
|
23
|
+
return unless num = line_number
|
24
|
+
num = num.to_i
|
25
|
+
|
26
|
+
source_code = @source.split("\n")
|
27
|
+
|
28
|
+
start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
|
29
|
+
end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
|
30
|
+
|
31
|
+
indent = ' ' * indentation
|
32
|
+
line_counter = start_on_line
|
33
|
+
return unless source_code = source_code[start_on_line..end_on_line]
|
34
|
+
|
35
|
+
source_code.collect do |line|
|
36
|
+
line_counter += 1
|
37
|
+
"#{indent}#{line_counter}: #{line}\n"
|
38
|
+
end.to_s
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
%h1 StaticMatic!
|
@@ -0,0 +1,18 @@
|
|
1
|
+
%html
|
2
|
+
%head
|
3
|
+
%style{:type => "text/css"}
|
4
|
+
body { font-family: helvetica, verdana, arial; font-size: 10pt;}
|
5
|
+
%body
|
6
|
+
%h1
|
7
|
+
= @exception.class.name
|
8
|
+
in
|
9
|
+
= @exception.filename
|
10
|
+
|
11
|
+
%p= @exception.original_exception.message
|
12
|
+
|
13
|
+
= @exception.source_extract.gsub(/\n/, "<br/>")
|
14
|
+
|
15
|
+
|
16
|
+
%h2 Backtrace
|
17
|
+
|
18
|
+
= @exception.backtrace.join("<br/>")
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'stringio'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + '/../lib/staticmatic'
|
6
|
+
|
7
|
+
TEST_SITE_PATH = File.join(File.dirname(__FILE__), "sandbox", "test_site")
|
8
|
+
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
def self.should(description, &block)
|
11
|
+
test_name = "test_should_#{description.gsub(/[\s]/,'_')}".to_sym
|
12
|
+
raise "#{test_name} is already defined in #{self}" if self.instance_methods.include?(test_name.to_s)
|
13
|
+
define_method(test_name, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_staticmatic
|
18
|
+
@staticmatic = StaticMatic::Base.new(TEST_SITE_PATH)
|
19
|
+
end
|
20
|
+
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mattt-staticmatic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.10.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen Bartholomew
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-29 00:00:00 -08:00
|
13
|
+
default_executable: staticmatic
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: haml
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "2.0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: mongrel
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "1.0"
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: newgem
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.2.3
|
41
|
+
version:
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: hoe
|
44
|
+
version_requirement:
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.8.3
|
50
|
+
version:
|
51
|
+
description:
|
52
|
+
email:
|
53
|
+
- steve@curve21.com
|
54
|
+
executables:
|
55
|
+
- staticmatic
|
56
|
+
extensions: []
|
57
|
+
|
58
|
+
extra_rdoc_files:
|
59
|
+
- History.txt
|
60
|
+
- Manifest.txt
|
61
|
+
files:
|
62
|
+
- History.txt
|
63
|
+
- Manifest.txt
|
64
|
+
- README.markdown
|
65
|
+
- Rakefile
|
66
|
+
- bin/staticmatic
|
67
|
+
- lib/staticmatic.rb
|
68
|
+
- lib/staticmatic/base.rb
|
69
|
+
- lib/staticmatic/configuration.rb
|
70
|
+
- lib/staticmatic/error.rb
|
71
|
+
- lib/staticmatic/helpers.rb
|
72
|
+
- lib/staticmatic/mixins/build.rb
|
73
|
+
- lib/staticmatic/mixins/helpers.rb
|
74
|
+
- lib/staticmatic/mixins/render.rb
|
75
|
+
- lib/staticmatic/mixins/server.rb
|
76
|
+
- lib/staticmatic/mixins/setup.rb
|
77
|
+
- lib/staticmatic/server.rb
|
78
|
+
- lib/staticmatic/templates/default/application.haml
|
79
|
+
- lib/staticmatic/templates/default/application.sass
|
80
|
+
- lib/staticmatic/templates/default/index.haml
|
81
|
+
- lib/staticmatic/templates/rescues/default.haml
|
82
|
+
- lib/staticmatic/templates/rescues/template.haml
|
83
|
+
- lib/staticmatic/template_error.rb
|
84
|
+
- lib/staticmatic/mixins/rescue.rb
|
85
|
+
- test/test_helper.rb
|
86
|
+
has_rdoc: true
|
87
|
+
homepage:
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options:
|
90
|
+
- --main
|
91
|
+
- README.markdown
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: "0"
|
99
|
+
version:
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: "0"
|
105
|
+
version:
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project: staticmatic
|
109
|
+
rubygems_version: 1.2.0
|
110
|
+
signing_key:
|
111
|
+
specification_version: 2
|
112
|
+
summary: The Lightweight Static Content framework
|
113
|
+
test_files:
|
114
|
+
- test/test_helper.rb
|