frank 0.1.0

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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ pkg
3
+ lib/frank/output_old.rb
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Travis Dunn
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ Frank
2
+ =========
3
+
4
+ Inspired by [Sinatra][0]'s simplicity and ease of use, Frank lets you build
5
+ static sites using your favorite libs, painlessly. It uses [Tilt][1], so it
6
+ comes with support for [Haml & Sass][2], [Builder][3], [ERB][4],
7
+ [Liquid][5], & [Mustache][6].
8
+
9
+ Frank also supports [CoffeeScript][7] for writing JavaScript in style, and
10
+ [LESS][8] is planned for the near future.
11
+
12
+ Overview
13
+ --------
14
+
15
+ Create a new project with:
16
+
17
+ $ frank <project_name>
18
+
19
+ Then start up the server with:
20
+
21
+ $ frankup
22
+
23
+ -----------------------
24
+ Frank's holdin' it down...
25
+ 0.0.0.0:3601
26
+
27
+ And you're ready to get to work. Feel free to use as much or a little of
28
+ the available libs as you please. Frank works just as well with Mustache /
29
+ CSS / JavasScript as Haml / Sass / CoffeeScript.
30
+
31
+
32
+ Views & Layouts
33
+ -------------------------
34
+
35
+ All of your templates, and less/sass/coffeescript go into `<project>/dynamic`,
36
+ by default. You are more than welcome to organize them into subfolders if you've
37
+ got lots.
38
+
39
+ ### Views
40
+
41
+ Writing views is simple. Say you've got a `blog.haml`, in `<project>/dynamic` just browse to
42
+ `http://0.0.0.0:3601/blog` and your view will be parsed and returned as html.
43
+
44
+ ### Layouts
45
+
46
+ Layouts are also simple with Frank. By default, just create a `layout.haml`
47
+ (or whichever language you like best), that contains a `yield`, and any
48
+ views will be inserted into it at that point.
49
+
50
+ Multiple layouts are also easy. In your `settings.yml`, do something like:
51
+
52
+ layouts:
53
+ - name: blog_layout
54
+ only: [blog]
55
+ - name: normal
56
+ not: [blog, ajax]
57
+ This tells Frank to use `blog_layout.haml` for `/blog`, and `normal.haml`
58
+ for everything but `/blog' and '/ajax`.
59
+
60
+
61
+ Partials & Helpers
62
+ ------------------
63
+
64
+ Frank comes with a helper method, `render_partial`, for including partials
65
+ in your views.
66
+
67
+ In addition, you can also easily add your own helper methods to use.
68
+
69
+ ### Partials
70
+
71
+ To create a partial, make a new file like any of your other views, but
72
+ prefix its name with an underscore.
73
+
74
+ For example, if I have a partial named `_footer.haml`, I can include this
75
+ in my Haml views like this:
76
+
77
+ = render_partial 'footer'
78
+
79
+ ### Helpers
80
+
81
+ Helper methods are also easy. Just open up `helpers.rb` and add your methods
82
+ to the `FrankHelpers` module; that's it. Use them just like `render_partial`.
83
+
84
+
85
+ GET/POST params
86
+ ---------------
87
+
88
+ Sometimes it's nice to include user input in your mock-ups. It's especially
89
+ handy when mocking-up Ajax-driven elements. For this reason, the `request`
90
+ and `params` are available in your templates.
91
+
92
+ For example, to use a person's name submitted through a form you might do:
93
+
94
+ %h1= "Hello, #{params.name}"
95
+
96
+ Configuration
97
+ -------------
98
+
99
+ In `settings.yml`, you can change your folder names, and server port & host name.
100
+ Check the comments there if you need help.
101
+
102
+ Once you've gotten comfortable with Frank, you will probably no longer want
103
+ the example files included whenever you start a new project. You may also have
104
+ preferred folder names, and languages that you always want to start
105
+ projects with.
106
+
107
+ To do this, create a new base project. Then just copy your base project to `~/.frank`.
108
+ This folder will then be copied for you whenever you run the `frank` command.
109
+
110
+ Installation
111
+ ------------
112
+
113
+ ### [Gemcutter](http://gemcutter.org/)
114
+
115
+ $ gem install frank
116
+
117
+
118
+ [0]: http://www.sinatrarb.com/
119
+ [1]: http://github.com/rtomayko/tilt
120
+ [2]: http://haml-lang.com/
121
+ [3]: http://builder.rubyforge.org/
122
+ [4]: http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/
123
+ [5]: http://www.liquidmarkup.org/
124
+ [6]: http://github.com/defunkt/mustache
125
+ [7]: http://jashkenas.github.com/coffee-script/
126
+ [8]: http://lesscss.org/
127
+ [9]: http://rack.rubyforge.org/
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "frank"
5
+ gemspec.summary = "Stupidly Simple Static Slinger"
6
+ gemspec.description = "Create/Dump static builds using whatever templating/helper languages you wish"
7
+ gemspec.email = "travis.dunn@thisismedium.com"
8
+ gemspec.homepage = "http://github.com/blahed/frank"
9
+ gemspec.authors = ["blahed", "nwah"]
10
+ end
11
+ Jeweler::GemcutterTasks.new
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: gem install jeweler"
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/frank ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.empty?
4
+ puts "Usage: frank [PROJECT_NAME]"
5
+ puts "Stubs out a template project; if a .frank folder was found, copies it over as your project base"
6
+ exit
7
+ end
8
+
9
+ puts "\n-----------------------\n Frank:\n"
10
+
11
+ project = ARGV.first
12
+ libdir = File.join(File.dirname(File.expand_path(__FILE__)), '../lib')
13
+ dot_frank = File.join(ENV['HOME'], '.frank')
14
+
15
+ puts " - Creating '#{project}'"
16
+ begin
17
+ Dir.mkdir project
18
+ rescue Errno::EEXIST
19
+ puts " uh oh, #{project} already exists..."
20
+ exit
21
+ end
22
+
23
+ # If they have a .frank, copy contents to new project folder
24
+ if File.directory? dot_frank
25
+ puts " - Copying .frank template"
26
+ FileUtils.cp_r( File.join(dot_frank, '.'), project )
27
+ else # Otherwise just copy over the template project
28
+ puts " - Copying Frank template"
29
+ FileUtils.cp_r( Dir.glob(File.join(libdir, 'template/*')), project )
30
+ end
31
+
32
+ puts "\n Congratulations, '#{project}' is ready to go.\n\n"
33
+
data/bin/frankout ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ require 'frank'
3
+
4
+ if ARGV.empty?
5
+ puts "Usage: frankout [DUMP_DIR]"
6
+ puts "Compiles and dumps a project and it\'s static files to given folder"
7
+ exit
8
+ end
9
+
10
+ output_folder = ARGV.first
11
+
12
+ if File.exists? output_folder
13
+ print "Folder: #{output_folder} already exists... do you want to overwrite it? (y|n) "
14
+ resp = STDIN.gets.chomp.strip
15
+ exit if resp.downcase.strip != 'y'
16
+ FileUtils.rm_rf(output_folder)
17
+ end
18
+
19
+ begin
20
+ settings = YAML.load_file('settings.yml')
21
+ proj_dir = Dir.pwd
22
+
23
+ Frank::Output.new do
24
+ settings.each do |name, value|
25
+ set name.to_s, value
26
+ end
27
+ set :output_folder, output_folder
28
+ set :proj_dir, proj_dir
29
+ end.dump
30
+
31
+ rescue Errno::ENOENT
32
+ puts "Frank could not find settings.yml."
33
+ end
data/bin/frankup ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'frank'
3
+
4
+ begin
5
+ settings = YAML.load_file('settings.yml')
6
+ Frank.new do
7
+ settings.each do |name, value|
8
+ set name.to_s, value
9
+ end
10
+ end
11
+ rescue Errno::ENOENT
12
+ puts "Frank could not find settings.yml."
13
+ end
data/frank.gemspec ADDED
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{frank}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["blahed", "nwah"]
12
+ s.date = %q{2010-01-15}
13
+ s.description = %q{Create/Dump static builds using whatever templating/helper languages you wish}
14
+ s.email = %q{travis.dunn@thisismedium.com}
15
+ s.executables = ["frank", "frankout", "frankup"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "bin/frank",
27
+ "bin/frankout",
28
+ "bin/frankup",
29
+ "frank.gemspec",
30
+ "lib/frank.rb",
31
+ "lib/frank/base.rb",
32
+ "lib/frank/output.rb",
33
+ "lib/frank/rescue.rb",
34
+ "lib/frank/statik.rb",
35
+ "lib/frank/template_helpers.rb",
36
+ "lib/frank/templates/404.haml",
37
+ "lib/frank/templates/500.haml",
38
+ "lib/frank/templates/frank-404.png",
39
+ "lib/frank/templates/frank-500.png",
40
+ "lib/frank/tilt.rb",
41
+ "lib/template/dynamic/css/frank.sass",
42
+ "lib/template/dynamic/index.haml",
43
+ "lib/template/dynamic/js/frank.coffee",
44
+ "lib/template/dynamic/layout.haml",
45
+ "lib/template/helpers.rb",
46
+ "lib/template/settings.yml",
47
+ "lib/template/static/images/frank-med.png"
48
+ ]
49
+ s.homepage = %q{http://github.com/blahed/frank}
50
+ s.rdoc_options = ["--charset=UTF-8"]
51
+ s.require_paths = ["lib"]
52
+ s.rubygems_version = %q{1.3.5}
53
+ s.summary = %q{Stupidly Simple Static Slinger}
54
+
55
+ if s.respond_to? :specification_version then
56
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
60
+ else
61
+ end
62
+ else
63
+ end
64
+ end
65
+
data/lib/frank.rb ADDED
@@ -0,0 +1,7 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require File.join(Dir.pwd, 'helpers')
5
+ require 'rubygems'
6
+ require 'frank/base'
7
+ require 'frank/output'
data/lib/frank/base.rb ADDED
@@ -0,0 +1,185 @@
1
+ require 'rack'
2
+ require 'frank/tilt'
3
+ require 'frank/template_helpers'
4
+ require 'frank/rescue'
5
+ require 'frank/statik'
6
+
7
+ module Frank
8
+
9
+ module Render; end
10
+
11
+ class Base
12
+ include Rack::Utils
13
+ include Frank::Rescue
14
+ include Frank::TemplateHelpers
15
+ include Frank::Render
16
+
17
+ attr_accessor :server, :static_folder, :dynamic_folder, :templates
18
+
19
+ def initialize(&block)
20
+ instance_eval &block
21
+ end
22
+
23
+ def call(env)
24
+ dup.call!(env)
25
+ end
26
+
27
+ def call!(env)
28
+ @env = env
29
+ @request = Rack::Request.new(env)
30
+ @response = Rack::Response.new
31
+ process
32
+ @response.close
33
+ @response.finish
34
+ end
35
+
36
+ private
37
+
38
+ # setter for options
39
+ def set(option, value)
40
+ if respond_to?("#{option}=")
41
+ send "#{option}=", value
42
+ end
43
+ end
44
+
45
+ # attempt to render with the request path,
46
+ # if it cannot be found, render error page
47
+ def process
48
+ begin
49
+ ext = File.extname(@request.path.split('/').last || '')
50
+ @response['Content-Type'] = Rack::Mime.mime_type(ext, 'text/html')
51
+ @response.write render_path(@request.path)
52
+ rescue Errno::ENOENT
53
+ render_404
54
+ rescue Exception => e
55
+ render_500 e
56
+ end
57
+ end
58
+
59
+ # prints requests and errors to STDOUT
60
+ def log_request(status, excp=nil)
61
+ out = "[#{Time.now.strftime('%Y-%m-%d %H:%M')}] (#{@server['handler'].capitalize}) http://#{@request.host}:#{@request.port}#{@request.fullpath} [#{@request.request_method}] - #{status}"
62
+ out += "\n\n**QUACK** #{excp.message}\n\n#{excp.backtrace.join("\n")} " if excp
63
+ STDOUT.puts out
64
+ end
65
+
66
+ end
67
+
68
+ module Render
69
+
70
+ def name_ext(path)
71
+ return path.split(/\.(?=[^.]+$)/)
72
+ end
73
+
74
+ def render_path(path)
75
+ path.sub!(/^\//,'')
76
+ template, ext = find_template_ext(path)
77
+ raise Errno::ENOENT if template.nil?
78
+
79
+ if template.match(/^_/) or (ext||'').match(/^(js|css)$/)
80
+ render_template template
81
+ else
82
+ render_with_layout template
83
+ end
84
+ end
85
+
86
+ def render_template(tmpl, *args)
87
+ tilt_with_request(File.join(@dynamic_folder, tmpl), *args) {"CONTENT"}
88
+ end
89
+
90
+ def render_with_layout(tmpl, *args)
91
+ if layout = get_layout_for(tmpl)
92
+ tilt_with_request(File.join(@dynamic_folder, layout), *args) do
93
+ render_template tmpl
94
+ end
95
+ else
96
+ render_template tmpl
97
+ end
98
+ end
99
+
100
+ TMPL_EXTS = { :html => %w[haml erb rhtml builder liquid mustache],
101
+ :css => %w[sass less],
102
+ :js => %w[coffee] }
103
+
104
+ def reverse_ext_lookup(ext)
105
+ TMPL_EXTS.each do |kind, exts|
106
+ return kind.to_s if exts.index(ext)
107
+ end
108
+ nil
109
+ end
110
+
111
+ def find_template_ext(filename)
112
+ name, kind = name_ext(filename)
113
+ kind = reverse_ext_lookup(kind) if kind && TMPL_EXTS[kind.intern].nil?
114
+
115
+ TMPL_EXTS[ kind.nil? ? :html : kind.intern ].each do |ext|
116
+ tmpl = "#{(name||'')}.#{ext}"
117
+ return [tmpl, kind] if File.exists? File.join(@dynamic_folder, tmpl)
118
+ end
119
+
120
+ TMPL_EXTS[ kind.nil? ? :html : kind.intern ].each do |ext|
121
+ default = File.join((name||''), "#{@templates['default']}.#{ext}")
122
+ return [default, kind] if File.exists? File.join(@dynamic_folder, default)
123
+ end
124
+ nil
125
+ rescue
126
+ nil
127
+ end
128
+
129
+ def get_layout_for(view)
130
+ view, ext = name_ext(view)
131
+ layouts = @templates['layouts'] || []
132
+
133
+ onlies = layouts.select {|l| l['only'] }
134
+ nots = layouts.select {|l| l['not'] }
135
+ blanks = layouts - onlies - nots
136
+
137
+ layout = onlies.select {|l| l['only'].index(view) }.first
138
+ layout = nots.reject {|l| l['not'].index(view) }.first unless layout
139
+ layout = blanks.first unless layout
140
+
141
+ layout.nil? ? nil : layout['name'] + '.' + ext
142
+ end
143
+
144
+ def tilt_lang(file, lang, *tilt_args, &block)
145
+ Tilt[lang].new(file, 1).render(*tilt_args, &block)
146
+ end
147
+
148
+ def tilt_with_request(file, *args, &block)
149
+ locals = @request.nil? ? {} : { :request => @env, :params => @request.params }
150
+ obj = Object.new.extend(TemplateHelpers).extend(Render)
151
+ obj.instance_variable_set(:@dynamic_folder, @dynamic_folder)
152
+ obj.instance_variable_set(:@templates, @templates)
153
+ Tilt.new(file, 1).render(obj, locals, &block)
154
+ end
155
+
156
+ def remove_ext(path)
157
+ path.gsub(File.extname(path), '')
158
+ end
159
+
160
+ end
161
+
162
+ def self.new(&block)
163
+ base = Base.new(&block) if block_given?
164
+ server_settings = base.instance_variable_get(:@server)
165
+
166
+ builder = Rack::Builder.new do
167
+ use Rack::Statik, :root => base.instance_variable_get(:@static_folder)
168
+ run base
169
+ end
170
+
171
+ m = "got it under control \n got your back \n holdin' it down
172
+ takin' care of business \n workin' some magic".split("\n").sort_by{rand}.first.strip
173
+ puts "\n-----------------------\n" +
174
+ " Frank's #{ m }...\n" +
175
+ " #{server_settings['hostname']}:#{server_settings['port']} \n\n"
176
+
177
+ server = Rack::Handler.get(server_settings['handler'])
178
+ server.run(builder, :Port => server_settings['port'], :Host => server_settings['hostname']) do
179
+ trap(:INT) { puts "\n\n-----------------------\n Show's over, fellas.\n\n"; exit }
180
+ end
181
+
182
+ base
183
+ end
184
+
185
+ end