frank 0.3.2 → 0.4.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.
@@ -1,12 +1,12 @@
1
1
  module Frank
2
2
  module Middleware
3
3
  class Refresh
4
-
4
+
5
5
  def initialize(app, options={})
6
6
  @app = app
7
7
  @folders = options[:watch]
8
8
  end
9
-
9
+
10
10
  # catch __refrank__ path and
11
11
  # return the most recent timestamp
12
12
  def call(env)
@@ -16,18 +16,17 @@ module Frank
16
16
  else
17
17
  @app.call(env)
18
18
  end
19
-
20
19
  end
21
-
20
+
22
21
  private
23
-
22
+
24
23
  # build list of mtimes for watched files
25
24
  # return the most recent
26
25
  def get_mtime
27
26
  pwd = Dir.pwd
28
27
  timestamps = []
29
28
  helpers = File.join(pwd, 'helpers.rb')
30
-
29
+
31
30
  timestamps << File.mtime(helpers).to_i if File.exist? helpers
32
31
  @folders.each do |folder|
33
32
  Dir[File.join(pwd, folder, '**/*.*')].each do |found|
@@ -36,7 +35,7 @@ module Frank
36
35
  end
37
36
  timestamps.sort.last
38
37
  end
39
-
38
+
40
39
  end
41
40
  end
42
41
  end
data/lib/frank/output.rb CHANGED
@@ -1,63 +1,63 @@
1
1
  module Frank
2
2
  class Output < Frank::Base
3
3
  include Frank::Render
4
-
5
- attr_accessor :environment, :proj_dir, :static_folder, :dynamic_folder, :templates, :output_folder
6
-
4
+
5
+ attr_accessor :environment, :output_folder
6
+
7
7
  def initialize(&block)
8
8
  instance_eval &block
9
9
  end
10
-
10
+
11
11
  # compile the templates
12
- # if production and template isn't index and is html
12
+ # if production and template isn't index and is html
13
13
  # name a folder based on the template and compile to index.html
14
14
  # otherwise compile as is
15
15
  def compile_templates(production)
16
- dir = File.join(@proj_dir, @dynamic_folder)
17
-
18
- Dir[File.join(dir, '**/*')].each do |path|
16
+ dir = File.join(Frank.root, Frank.dynamic_folder)
17
+
18
+ Dir[File.join(dir, '**{,/*/**}/*')].each do |path|
19
19
  if File.file?(path) && !File.basename(path).match(/^(\.|_)/)
20
20
  path = path[ (dir.size + 1)..-1 ]
21
21
  ext = File.extname(path)
22
- new_ext = ext_from_handler(ext)
22
+ new_ext = ext_from_handler(ext)
23
23
  name = File.basename(path, ext)
24
-
24
+
25
25
  if production == true && "#{name}.#{new_ext}" != 'index.html' && new_ext == 'html'
26
26
  new_file = File.join(@output_folder, path.sub(/(\/?[\w-]+)\.[\w-]+$/, "\\1/index.#{new_ext}"))
27
27
  else
28
- new_file = File.join(@output_folder, path.sub(/\.[\w-]+$/, ".#{new_ext}"))
28
+ new_file = File.join(@output_folder, path.sub(/\.[\w-]+$/, ".#{new_ext}"))
29
29
  end
30
-
30
+
31
31
  create_dirs(new_file)
32
32
  File.open(new_file, 'w') {|f| f.write render(path) }
33
33
  puts " - \033[32mCreating\033[0m '#{new_file}'"
34
34
  end
35
35
  end
36
36
  end
37
-
37
+
38
38
  # use path to determine folder name and
39
39
  # create the required folders if they don't exist
40
40
  def create_dirs(path)
41
41
  FileUtils.makedirs path.split('/').reverse[1..-1].reverse.join('/')
42
42
  end
43
-
43
+
44
44
  # copies over static content
45
45
  def copy_static
46
46
  puts " - \033[32mCopying\033[0m static content"
47
- static_folder = File.join(@proj_dir, @static_folder)
48
- FileUtils.cp_r(File.join(static_folder, '/.'), @output_folder)
47
+ static_folder = File.join(Frank.root, Frank.static_folder)
48
+ FileUtils.cp_r(File.join(static_folder, '/.'), @output_folder)
49
49
  end
50
-
50
+
51
51
  # create the dump dir, compile templates, copy over static assets
52
52
  def dump(production = false)
53
53
  FileUtils.mkdir(@output_folder)
54
54
  puts "\nFrank is..."
55
55
  puts " - \033[32mCreating\033[0m '#{@output_folder}'"
56
-
56
+
57
57
  compile_templates(production)
58
58
  copy_static
59
59
  puts "\n \033[32mCongratulations, project dumped to '#{@output_folder}' successfully!\033[0m"
60
60
  end
61
61
  end
62
-
62
+
63
63
  end
data/lib/frank/rescue.rb CHANGED
@@ -1,34 +1,34 @@
1
1
  module Frank
2
- module Rescue
3
-
2
+ module Rescue
3
+
4
4
  def render_404
5
5
  template = File.expand_path(File.dirname(__FILE__)) + '/templates/404.haml'
6
- locals = { :request => @env,
7
- :dynamic_folder => @dynamic_folder,
8
- :static_folder => @static_folder,
9
- :environment => @environment }
10
-
6
+ locals = { :request => @env,
7
+ :dynamic_folder => Frank.dynamic_folder,
8
+ :static_folder => Frank.static_folder,
9
+ :environment => Frank.environment }
10
+
11
11
  @response['Content-Type'] = 'text/html'
12
12
  @response.status = 404
13
13
  obj = Object.new.extend(TemplateHelpers)
14
14
  @response.body = Tilt::HamlTemplate.new(template).render(obj, locals = locals)
15
-
15
+
16
16
  log_request('404')
17
17
  end
18
-
18
+
19
19
  def render_500(excp)
20
20
  template = File.expand_path(File.dirname(__FILE__)) + '/templates/500.haml'
21
- locals = { :request => @env,
22
- :params => @request.params,
21
+ locals = { :request => @env,
22
+ :params => @request.params,
23
23
  :exception => excp }
24
24
 
25
25
  @response['Content-Type'] = 'text/html'
26
26
  @response.status = 500
27
27
  obj = Object.new.extend(TemplateHelpers)
28
- @response.body = Tilt::HamlTemplate.new(template).render(obj, locals = locals)
29
-
28
+ @response.body = Tilt::HamlTemplate.new(template).render(obj, locals = locals)
29
+
30
30
  log_request('500', excp)
31
31
  end
32
-
32
+
33
33
  end
34
34
  end
@@ -0,0 +1,52 @@
1
+ require 'singleton'
2
+ require 'ostruct'
3
+ module Frank
4
+ class Settings
5
+ include Singleton
6
+
7
+ attr_accessor :environment
8
+ attr_accessor :root
9
+
10
+ attr_accessor :server
11
+ attr_accessor :options
12
+ attr_accessor :static_folder
13
+ attr_accessor :dynamic_folder
14
+ attr_accessor :layouts_folder
15
+ attr_accessor :sass_options
16
+
17
+ def initialize
18
+ reset
19
+ end
20
+
21
+ # Reset settings to the defaults
22
+ def reset
23
+ # reset server settings
24
+ @server = OpenStruct.new
25
+ @server.handler = "mongrel"
26
+ @server.hostname = "0.0.0.0"
27
+ @server.port = "3601"
28
+
29
+ # reset options
30
+ @options = OpenStruct.new
31
+
32
+ # setup folders
33
+ @static_folder = "static"
34
+ @dynamic_folder = "dynamic"
35
+ @layouts_folder = "layouts"
36
+
37
+ # setup 3rd party configurations
38
+ @sass_options = {}
39
+ end
40
+
41
+ # Check to see if we're in production mode
42
+ def production?
43
+ @production
44
+ end
45
+
46
+ # Mark this Frank run as production
47
+ def production!
48
+ @production = true
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ require 'tilt'
2
+
3
+ module Frank
4
+
5
+ # Scss template implementation. See:
6
+ # http://haml.hamptoncatlin.com/
7
+ #
8
+ # Sass templates do not support object scopes, locals, or yield.
9
+ class SassTemplate < Tilt::SassTemplate
10
+ def prepare
11
+ @engine = ::Sass::Engine.new(data, sass_options.merge(Frank.sass_options || {}).merge(:syntax => :sass))
12
+ end
13
+ end
14
+ Tilt.register 'sass', SassTemplate
15
+
16
+ # Scss template implementation. See:
17
+ # http://haml.hamptoncatlin.com/
18
+ #
19
+ # Sass templates do not support object scopes, locals, or yield.
20
+ class ScssTemplate < Tilt::SassTemplate
21
+ def prepare
22
+ @engine = ::Sass::Engine.new(data, sass_options.merge(Frank.sass_options || {}).merge(:syntax => :scss))
23
+ end
24
+ end
25
+ Tilt.register 'scss', ScssTemplate
26
+
27
+ # Radius Template
28
+ # http://github.com/jlong/radius/
29
+ class RadiusTemplate < Tilt::Template
30
+ def initialize_engine
31
+ return if defined? ::Radius
32
+ require_template_library 'radius'
33
+ end
34
+
35
+ def prepare
36
+ @context = Class.new(Radius::Context).new
37
+ end
38
+
39
+ def evaluate(scope, locals, &block)
40
+ @context.define_tag("yield") do
41
+ block.call
42
+ end
43
+ (class << @context; self; end).class_eval do
44
+ define_method :tag_missing do |tag, attr, &block|
45
+ if locals.key?(tag.to_sym)
46
+ locals[tag.to_sym]
47
+ else
48
+ scope.__send__(tag) # any way to support attr as args?
49
+ end
50
+ end
51
+ end
52
+ # TODO: how to config tag prefix?
53
+ parser = Radius::Parser.new(@context, :tag_prefix => 'r')
54
+ parser.parse(data)
55
+ end
56
+ end
57
+ Tilt.register 'radius', RadiusTemplate
58
+ end
@@ -0,0 +1,90 @@
1
+ module Frank
2
+ module Upgrades
3
+
4
+ def upgrade!
5
+ version = detect_version
6
+
7
+ if version == '0.3'
8
+ upgrade_from_0_3!
9
+ else
10
+ puts "\033[32mLooks like you're already good to go!\033[0m"
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def upgrade_from_0_3!
17
+ settings = YAML.load_file(File.join(Frank.root, 'settings.yml'))
18
+ setup = <<-SETUP
19
+ # ----------------------
20
+ # Server settings:
21
+ #
22
+ # Change the server host/port to bind rack to.
23
+ # 'server' can be any Rack-supported server, e.g.
24
+ # Mongrel, Thin, WEBrick
25
+ #
26
+ Frank.server.handler = "#{settings['server']['handler']}"
27
+ Frank.server.hostname = "#{settings['server']['hostname']}"
28
+ Frank.server.port = "#{settings['server']['port']}"
29
+
30
+ # ----------------------
31
+ # Static folder:
32
+ #
33
+ # All files in this folder will be served up
34
+ # directly, without interpretation
35
+ #
36
+ Frank.static_folder = "#{settings['static_folder']}"
37
+
38
+ # ----------------------
39
+ # Dynamic folder:
40
+ #
41
+ # Frank will try to interpret any of the files
42
+ # in this folder based on their extension
43
+ #
44
+ Frank.dynamic_folder = "#{settings['dynamic_folder']}"
45
+
46
+ # ----------------------
47
+ # Layouts folder:
48
+ #
49
+ # Frank will look for layouts in this folder
50
+ # the default layout is `default'
51
+ # it respects nested layouts that correspond to nested
52
+ # folders in the `dynamic_folder'
53
+ # for example: a template: `dynamic_folder/blog/a-blog-post.haml'
54
+ # would look for a layout: `layouts/blog/default.haml'
55
+ # and if not found use the default: `layouts/default.haml'
56
+ #
57
+ # Frank also supports defining layouts on an
58
+ # individual template basis using meta data
59
+ # you can do this by defining a meta field `layout: my_layout.haml'
60
+ #
61
+ Frank.layouts_folder = "#{settings['layouts_folder']}"
62
+
63
+
64
+ # ----------------------
65
+ # Initializers:
66
+ #
67
+ # Add any other project setup code, or requires here
68
+ # ....
69
+ SETUP
70
+
71
+ puts " - \033[32mConverting\033[0m settings.yml => setup.rb"
72
+
73
+ File.open(File.join(Frank.root, 'setup.rb'), 'w') { |file| file.write(setup.gsub(/^\s+(?=[^\s])/, '')) }
74
+ File.delete(File.join(Frank.root, 'settings.yml'))
75
+
76
+ puts "\n \033[32mUpdate is complete, enjoy!\033[0m"
77
+ end
78
+
79
+ def detect_version
80
+ if File.exist? File.join(Frank.root, 'settings.yml')
81
+ version = '0.3'
82
+ else
83
+ version = '<0.3'
84
+ end
85
+
86
+ version
87
+ end
88
+
89
+ end
90
+ end
data/lib/frank.rb CHANGED
@@ -12,5 +12,49 @@ require 'rubygems'
12
12
  require 'yaml'
13
13
  require 'fileutils'
14
14
  require 'rack'
15
+ require 'frank/settings'
15
16
  require 'frank/base'
16
17
  require 'frank/output'
18
+
19
+ # relay
20
+ module Frank
21
+
22
+ # Quickly configure Frank settings. Best used by passing a block.
23
+ #
24
+ # Example:
25
+ #
26
+ # Frank.configure do |settings|
27
+ # settings.server.handler = "mongrel"
28
+ # settings.server.hostname = "0.0.0.0"
29
+ # settings.server.port = "3601"
30
+ #
31
+ # settings.static_folder = "static"
32
+ # settings.dynamic_folder = "dynamic"
33
+ # settings.layouts_folder = "layouts"
34
+ # end
35
+ #
36
+ # Returns:
37
+ #
38
+ # The Frank +Settings+ singleton instance.
39
+ class << self
40
+ def configure
41
+ settings = Frank::Settings.instance
42
+ block_given? ? yield(settings) : settings
43
+ end
44
+ end
45
+
46
+ # Take all the public instance methods from the Settings singleton and allow
47
+ # them to be accessed through the Frank module directly.
48
+ #
49
+ # Examples:
50
+ #
51
+ # <tt>Frank.server.hander #=> "mongrel"</tt>
52
+ # <tt>Frank.static_folder #=> "static"</tt>
53
+ Frank::Settings.public_instance_methods(false).each do |name|
54
+ (class << self; self; end).class_eval <<-EOT
55
+ def #{name}(*args)
56
+ configure.send("#{name}", *args)
57
+ end
58
+ EOT
59
+ end
60
+ end
@@ -5,10 +5,10 @@
5
5
  %img{:src=>'/images/frank-med.png'}
6
6
  %h1 Frank
7
7
  %h2 Relax, fella. Frank's got it under control.
8
-
8
+
9
9
  %ol#help
10
10
  %li
11
- %p= "Edit <em>settings.yml</em> to set your server options, folder names for static/dynamic content, and layouts for your views."
11
+ %p= "Edit <em>setup.rb</em> to set your server options, folder names for static/dynamic content, and layouts for your views."
12
12
  %li
13
13
  %p= "Stash your static files (images, flash movies, etc) in the folder that you set as <em>[static_folder]</em>."
14
14
  %li
@@ -5,10 +5,9 @@
5
5
  # 'server' can be any Rack-supported server, e.g.
6
6
  # Mongrel, Thin, WEBrick
7
7
  #
8
- server:
9
- handler: mongrel
10
- hostname: 0.0.0.0
11
- port: 3601
8
+ Frank.server.handler = "mongrel"
9
+ Frank.server.hostname = "0.0.0.0"
10
+ Frank.server.port = "3601"
12
11
 
13
12
  # ----------------------
14
13
  # Static folder:
@@ -16,7 +15,7 @@ server:
16
15
  # All files in this folder will be served up
17
16
  # directly, without interpretation
18
17
  #
19
- static_folder: static
18
+ Frank.static_folder = "static"
20
19
 
21
20
  # ----------------------
22
21
  # Dynamic folder:
@@ -24,7 +23,7 @@ static_folder: static
24
23
  # Frank will try to interpret any of the files
25
24
  # in this folder based on their extension
26
25
  #
27
- dynamic_folder: dynamic
26
+ Frank.dynamic_folder = "dynamic"
28
27
 
29
28
  # ----------------------
30
29
  # Layouts folder:
@@ -36,9 +35,16 @@ dynamic_folder: dynamic
36
35
  # for example: a template: `dynamic_folder/blog/a-blog-post.haml'
37
36
  # would look for a layout: `layouts/blog/default.haml'
38
37
  # and if not found use the default: `layouts/default.haml'
39
- #
38
+ #
40
39
  # Frank also supports defining layouts on an
41
40
  # individual template basis using meta data
42
41
  # you can do this by defining a meta field `layout: my_layout.haml'
43
42
  #
44
- layouts_folder: layouts
43
+ Frank.layouts_folder = "layouts"
44
+
45
+
46
+ # ----------------------
47
+ # Initializers:
48
+ #
49
+ # Add any other project setup code, or requires here
50
+ # ....
File without changes
data/spec/base_spec.rb CHANGED
@@ -1,79 +1,79 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
3
  describe Frank::Base do
4
- include Rack::Test::Methods
5
-
4
+ include Rack::Test::Methods
5
+
6
6
  def app
7
- proj_dir = File.join(File.dirname(__FILE__), 'template')
8
- settings = YAML.load_file(File.join(proj_dir, 'settings.yml'))
7
+ Frank.bootstrap(File.join(File.dirname(__FILE__), 'template'))
9
8
  Frank.new do
10
- settings.each do |name, value|
11
- set name.to_s, value
12
- end
13
9
  set :environment, :test
14
- set :proj_dir, proj_dir
10
+
11
+ # this is just used for a test
12
+ @blowup_sometimes = true
15
13
  end
16
14
  end
17
-
15
+
18
16
  it 'has all of the required settings set' do
19
- app.proj_dir.should_not be_nil
20
- app.server.should_not be_nil
21
- app.static_folder.should_not be_nil
22
- app.dynamic_folder.should_not be_nil
23
- app.layouts_folder.should_not be_nil
17
+ app
18
+ Frank.root.should_not be_nil
19
+ Frank.server.handler.should_not be_nil
20
+ Frank.server.hostname.should_not be_nil
21
+ Frank.server.port.should_not be_nil
22
+ Frank.static_folder.should_not be_nil
23
+ Frank.dynamic_folder.should_not be_nil
24
+ Frank.layouts_folder.should_not be_nil
24
25
  end
25
-
26
+
26
27
  it 'renders a dynamic template given a request' do
27
28
  get '/'
28
-
29
+
29
30
  last_response.should be_ok
30
31
  last_response.body.should == "<div id='p'>/</div>\n<div id='layout'>\n <h1>hello worlds</h1>\n <h2>/</h2>\n</div>\n"
31
32
  end
32
-
33
+
33
34
  it 'renders a page and uses a helper' do
34
35
  get '/helper_test'
35
-
36
+
36
37
  last_response.should be_ok
37
38
  last_response.body.should == "<div id='p'>/helper_test</div>\n<div id='layout'>\n <h1>hello from helper</h1>\n</div>\n"
38
39
  end
39
-
40
+
40
41
  it 'renders a nested template given a request' do
41
42
  get '/nested/child'
42
-
43
+
43
44
  last_response.should be_ok
44
45
  last_response.body.should == "<div id='nested_layout'>\n <h1>hello from child</h1>\n</div>\n"
45
46
  end
46
-
47
+
47
48
  it 'renders dynamic css without a layout' do
48
- get '/sass.css'
49
-
49
+ get '/stylesheets/sass.css'
50
+
50
51
  last_response.should be_ok
51
- last_response.body.should == "#hello-worlds {\n background: red; }\n"
52
+ last_response.body.should include("#hello-worlds {\n background: red;\n}\n")
52
53
  end
53
-
54
+
54
55
  it 'renders a 404 page if template not found' do
55
56
  get '/not_here.css'
56
-
57
+
57
58
  last_response.should_not be_ok
58
59
  last_response.content_type.should == 'text/html'
59
60
  last_response.body.should =~ /Not Found/
60
61
  end
61
-
62
+
62
63
  it 'renders a 500 page for error' do
63
64
  capture_stdout { get '/500' }
64
-
65
65
  last_response.should_not be_ok
66
66
  last_response.content_type.should == 'text/html'
67
67
  last_response.body.should =~ /undefined local variable or method `non_method'/
68
68
  end
69
-
69
+
70
70
  it 'stubs out a project' do
71
71
  out = capture_stdout { Frank.stub('stubbed') }
72
72
  Dir.entries('stubbed').should == Dir.entries(File.join(LIBDIR, 'template'))
73
73
  response = "\nFrank is...\n - \e[32mCreating\e[0m your project 'stubbed'\n - \e[32mCopying\e[0m Frank template\n\n \e[32mCongratulations, 'stubbed' is ready to go!\e[0m\n"
74
74
  out.string.should == response
75
75
  end
76
-
76
+
77
77
  after(:all) do
78
78
  FileUtils.rm_r File.join(Dir.pwd, 'stubbed')
79
79
  end
data/spec/helper.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  testdir = File.dirname(__FILE__)
2
2
  $:.unshift testdir unless $LOAD_PATH.include?(testdir)
3
3
 
4
+ require 'bundler'
5
+ Bundler.setup
6
+
4
7
  require 'stringio'
5
- require 'rubygems'
6
8
  require 'rack/test'
7
9
  require 'template/helpers'
8
10
  require 'frank'