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.
@@ -0,0 +1,6 @@
1
+ == 0.10.0 2009-01-01
2
+
3
+ * Refactoring for forthcoming improvements
4
+ * Improved Error Reporting
5
+ * More useful error pages
6
+ * Removed Haml specific error handling
@@ -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
@@ -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'
@@ -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
@@ -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)
@@ -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,6 @@
1
+ module StaticMatic::ServerMixin
2
+ def preview
3
+ puts "StaticMatic Preview Server Starting..."
4
+ StaticMatic::Server.start(self)
5
+ end
6
+ 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,7 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title StaticMatic
5
+ = stylesheets
6
+ %body
7
+ = yield
@@ -0,0 +1,4 @@
1
+ body
2
+ :font
3
+ :family Verdana
4
+ :size 10pt
@@ -0,0 +1 @@
1
+ %h1 StaticMatic!
@@ -0,0 +1,7 @@
1
+ %h1= @exception.class.name
2
+
3
+ %p= @exception.message
4
+
5
+ %h2 Stack Trace
6
+
7
+ = @exception.backtrace.join("<br/>")
@@ -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/>")
@@ -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