bdimcheff-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.
Files changed (48) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +50 -0
  3. data/Rakefile +31 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/staticmatic +23 -0
  6. data/lib/staticmatic.rb +20 -0
  7. data/lib/staticmatic/base.rb +87 -0
  8. data/lib/staticmatic/configuration.rb +18 -0
  9. data/lib/staticmatic/error.rb +17 -0
  10. data/lib/staticmatic/helpers.rb +197 -0
  11. data/lib/staticmatic/mixins/build.rb +46 -0
  12. data/lib/staticmatic/mixins/helpers.rb +15 -0
  13. data/lib/staticmatic/mixins/render.rb +125 -0
  14. data/lib/staticmatic/mixins/rescue.rb +12 -0
  15. data/lib/staticmatic/mixins/server.rb +6 -0
  16. data/lib/staticmatic/mixins/setup.rb +20 -0
  17. data/lib/staticmatic/server.rb +94 -0
  18. data/lib/staticmatic/template_error.rb +40 -0
  19. data/lib/staticmatic/templates/default/application.haml +7 -0
  20. data/lib/staticmatic/templates/default/application.sass +4 -0
  21. data/lib/staticmatic/templates/default/index.haml +1 -0
  22. data/lib/staticmatic/templates/rescues/default.haml +7 -0
  23. data/lib/staticmatic/templates/rescues/template.haml +18 -0
  24. data/test/base_test.rb +13 -0
  25. data/test/helpers_test.rb +12 -0
  26. data/test/render_test.rb +30 -0
  27. data/test/rescue_test.rb +36 -0
  28. data/test/sandbox/test_site/configuration.rb +0 -0
  29. data/test/sandbox/test_site/site/index.html +10 -0
  30. data/test/sandbox/test_site/site/stylesheets/application.css +2 -0
  31. data/test/sandbox/test_site/src/helpers/application_helper.rb +5 -0
  32. data/test/sandbox/test_site/src/layouts/alternate_layout.haml +3 -0
  33. data/test/sandbox/test_site/src/layouts/application.haml +7 -0
  34. data/test/sandbox/test_site/src/layouts/projects.haml +1 -0
  35. data/test/sandbox/test_site/src/pages/hello_world.erb +1 -0
  36. data/test/sandbox/test_site/src/pages/index.haml +5 -0
  37. data/test/sandbox/test_site/src/pages/layout_test.haml +2 -0
  38. data/test/sandbox/test_site/src/pages/page_with_error.haml +5 -0
  39. data/test/sandbox/test_site/src/pages/page_with_partial_error.haml +3 -0
  40. data/test/sandbox/test_site/src/partials/menu.haml +1 -0
  41. data/test/sandbox/test_site/src/partials/partial_with_error.haml +1 -0
  42. data/test/sandbox/test_site/src/stylesheets/application.sass +2 -0
  43. data/test/sandbox/test_site/src/stylesheets/css_with_error.sass +5 -0
  44. data/test/server_test.rb +12 -0
  45. data/test/setup_test.rb +25 -0
  46. data/test/template_error_test.rb +23 -0
  47. data/test/test_helper.rb +20 -0
  48. metadata +128 -0
@@ -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
+ locals = options[:locals] || {}
98
+ html.render(@scope, locals) { 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/>")
data/test/base_test.rb ADDED
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+
3
+ class StaticMatic::BaseTest < Test::Unit::TestCase
4
+ def setup
5
+ setup_staticmatic
6
+ end
7
+
8
+ should "set initial configuration settings" do
9
+ assert_equal true, @staticmatic.configuration.use_extensions_for_page_links
10
+ assert_equal 3000, @staticmatic.configuration.preview_server_port
11
+ end
12
+
13
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+
3
+ class StaticMatic::HelpersTest < Test::Unit::TestCase
4
+ def setup
5
+ setup_staticmatic
6
+ end
7
+
8
+ should "include custom helper" do
9
+ content = @staticmatic.generate_html_with_layout("index")
10
+ assert_match "Hello, Steve!", content
11
+ end
12
+ end