Permalink
Browse files

. imports code from another repository

This was developed as a tool to a project, now I'd like to publish this
as a gem.
  • Loading branch information...
1 parent 3784fdf commit 941f648c330561403758e56a814192087c92ef1d @kschiess committed Jul 16, 2009
View
@@ -1,6 +1,3 @@
=== 1.0.0 / 2009-07-16
-* 1 major enhancement
-
- * Birthday!
-
+* Releasing thin_ham
View
@@ -1,26 +1,32 @@
= thin_ham
-* url
+Source:
+* http://github.com/kschiess/thin_ham
== DESCRIPTION:
-FIX (describe your package)
+thin_ham is a small tool to allow for fast prototyping of web sites. It will
+publish files written in haml & sass [1] or alternatively just publish
+whatever other files you happen to have lying around in the relevant
+directories.
-== FEATURES/PROBLEMS:
+Since it is written as a web server, it will allow running javascript in your
+prototype to use AJAX to load missing resources. This can be very useful when
+developing modern websites.
-* FIX (list of features or problems)
+Note that thin_ham should under NO circumstances be used as a public web
+server. It is not made that way.
-== SYNOPSIS:
-
- FIX (code sample of usage)
-== REQUIREMENTS:
-
-* FIX (list of requirements)
+== SYNOPSIS:
-== INSTALL:
+Move to a directory of your choice (where your prototype will be located) and
+run:
-* FIX (sudo gem install, anything else)
+ thin_ham
+
+thin_ham will then output an url under which the current directory can be
+accessed locally.
== LICENSE:
View
@@ -1,12 +1,19 @@
-# -*- ruby -*-
-
require 'rubygems'
require 'hoe'
-Hoe.spec 'thin_ham' do
- # developer('FIX', 'FIX@example.com')
+# Hoe.spec 'thin_ham' do
+# developer('Kaspar Schiess', 'kaspar.schiess@absurd.li')
+#
+# # self.rubyforge_name = 'thin_hamx' # if different than 'thin_ham'
+# end
+
+require 'rake'
+require 'spec/rake/spectask'
- # self.rubyforge_name = 'thin_hamx' # if different than 'thin_ham'
+desc "Run all examples"
+Spec::Rake::SpecTask.new('spec') do |t|
+ t.spec_files = FileList['spec/**/*.rb']
+ t.spec_opts = %w{--options spec/spec.opts}
end
-# vim: syntax=ruby
+task :default => :spec
View
@@ -1,3 +1,8 @@
#!/usr/bin/env ruby
-abort "you need to write me"
+$:.unshift File.dirname(__FILE__) + "/../lib"
+require 'thin_ham/init'
+
+
+directory = ARGV.first || '.'
+ThinHam.start_server
View
@@ -1,3 +0,0 @@
-class ThinHam
- VERSION = '1.0.0'
-end
View
@@ -0,0 +1,97 @@
+
+require 'time'
+require 'rack/mime'
+require 'rack/utils'
+
+require 'thin_ham/processors/markup'
+require 'thin_ham/processors/sass'
+require 'thin_ham/processors/haml'
+
+# Serves files from the docroot given in the constructor. If a file
+# ends with the file ending of a known processor (.haml or .sass), transform
+# the file and serve the result of the transformation.
+#
+# This is intended to be a prototyping helper and thus it performs no caching.
+#
+# CAUTION: Not for WAN exposed usage, not security reviewed !!! Sensible to
+# at least the '..'-kind of attack.
+#
+# CAUTION: All files are currently loaded into memory and then served. This
+# is probably not what you want to use for big movies.
+#
+# Based on rack/file.rb
+#
+class ThinHam::Hamlr
+ attr_reader :docroot
+ def initialize(docroot)
+ @docroot = docroot
+ end
+
+ def call(env)
+ dup.request(env)
+ end
+
+ # Handles request - this is thread-safe.
+ #
+ def request(env)
+ path_info = Rack::Utils.unescape(env["PATH_INFO"])
+ full_path = File.join(docroot, path_info)
+
+ processors.each do |processor|
+ mapped_path = processor.map_to_file(full_path)
+ if can_serve?(mapped_path)
+ return http_answer(
+ processor.serve(full_path),
+ mime_type(full_path))
+ end
+ end
+
+ return not_found(path_info)
+ end
+
+ # Returns a list of processors known to man
+ #
+ def processors
+ [
+ ThinHam::Processors::Haml.new,
+ ThinHam::Processors::Sass.new,
+ ThinHam::Processors::Markup.new # identity transform, serving files as files
+ ]
+ end
+
+ # Can we serve this file?
+ #
+ def can_serve?(file)
+ File.file?(file) && File.readable?(file)
+ end
+
+ # Returns the mime type (a guess!) for the given +filename+
+ #
+ def mime_type(filename)
+ Rack::Mime.mime_type(
+ File.extname(filename),
+ 'text/plain')
+ end
+
+ # Serves body given with mime_type and modif_date.
+ #
+ def http_answer(body, mime_type)
+ [200, {
+ 'Cache-Control' => 'max-age=0', # turns off browser cache (hopefully)
+ "Content-Type" => mime_type,
+ "Content-Length" => body.size.to_s
+ }, body]
+ end
+
+ # Returns a 404 not found answer.
+ #
+ def not_found(path)
+ body = "File not found: #{path}\n"
+ [404, {
+ 'Cache-Control' => 'max-age=3600',
+ "Content-Type" => "text/plain",
+ "Content-Length" => body.size.to_s
+ },
+ [body]]
+ end
+end
View
@@ -0,0 +1,21 @@
+
+require 'thin'
+
+module ThinHam
+ def start_server(directory='.')
+ puts "serving ham in thin slices at localhost:3000... (Ctrl+C to interrupt)"
+
+ Thin::Server.start('0.0.0.0', 3000) do
+ use Rack::CommonLogger
+
+ map '/' do
+ run ThinHam::Hamlr.new(directory)
+ end
+ end
+ end
+ module_function :start_server
+
+ module Processors; end
+end
+
+require 'thin_ham/hamlr'
@@ -0,0 +1,43 @@
+
+require 'haml'
+
+require 'thin_ham/processors/markup'
+
+class ThinHam::Processors::Haml < ThinHam::Processors::Markup
+
+ # Helper class for markup evaluation.
+ #
+ class Helper
+ attr_reader :base_path, :haml_processor
+ def initialize(base_path, haml_processor)
+ @base_path, @haml_processor = base_path, haml_processor
+ end
+
+ def include(file)
+ include_name = File.join(base_path, file)
+ haml_processor.serve(include_name)
+ end
+ end
+
+ # Serves the file by processing it and then calling #serve_with_file_origin
+ # on the controller.
+ #
+ def serve(file)
+ markup = read_markup(map_to_file(file))
+ base_path = File.dirname(file)
+
+ process(markup, base_path)
+ end
+
+ def map_to_file(path)
+ "#{path}.haml"
+ end
+
+ # Processes haml into html
+ #
+ def process(haml, base_path)
+ helper = Helper.new(base_path, self)
+
+ Haml::Engine.new(haml).render(helper)
+ end
+end
@@ -0,0 +1,53 @@
+
+
+# Generic markup processor that works by reading a file based on the
+# original filename, appending the markups extension.
+#
+# This class is abstract, implementors should override #map_to_file and
+# #process(markup), yielding target code. Using the Markup class itself
+# will give you an identity transform, meaning delivering files as themselves.
+#
+# Example:
+#
+# class FooMarkup < Markup
+# def map_to_file(filename)
+# filename + ".foo"
+# end
+# def process(foo)
+# foo.gsub('foo', 'bar')
+# end
+# end
+#
+class ThinHam::Processors::Markup
+ # Serves the file by processing it and then calling #serve_with_file_origin
+ # on the controller.
+ #
+ def serve(file)
+ markup = read_markup(map_to_file(file))
+
+ process(markup)
+ end
+
+ # Reads markup file contents
+ #
+ def read_markup(file)
+ File.read(file)
+ end
+
+ # Override these functions to implement a markup processor -----------------
+ # (NOTE: Default implementation doesn't do anything and will leave files
+ # unprocessed - this is interesting in its own right.)
+
+ # Maps a path in the filesystem below the document root to a file that
+ # contains markup - returning the new path of the markup file.
+ #
+ def map_to_file(path)
+ path
+ end
+
+ # Processes markup into the target code.
+ #
+ def process(markup)
+ markup
+ end
+end
@@ -0,0 +1,16 @@
+
+require 'sass'
+
+require 'thin_ham/processors/markup'
+
+class ThinHam::Processors::Sass < ThinHam::Processors::Markup
+ def map_to_file(path)
+ path + '.sass'
+ end
+
+ # Processes sass into css
+ #
+ def process(sass)
+ Sass::Engine.new(sass).render
+ end
+end
@@ -0,0 +1 @@
+= include 'pieces/bar'
@@ -0,0 +1 @@
+<html><body>This demonstrates a simple html file</body></html>
@@ -0,0 +1 @@
+bar
@@ -0,0 +1,7 @@
+body
+ :width 5000px
+ :background-color #3d4367
+ :font-family Helvetica
+ :font-size 14px
+ :line-height 1.2em
+ :margin 8px
@@ -0,0 +1,4 @@
+!!!
+!!! XML
+%body
+ = "This is a test"
View
@@ -0,0 +1 @@
+--colour
View
@@ -0,0 +1,23 @@
+require 'spec'
+
+$:.unshift File.dirname(__FILE__) + "/../lib"
+
+require 'thin_ham/init'
+
+Spec::Runner.configure do |config|
+ config.mock_with :flexmock
+end
+
+FIXTURES_ROOT = File.join(
+ File.dirname(__FILE__),
+ '/fixtures')
+
+# Generates a full path from a path relative to the fixtures root
+#
+def fixture(path)
+ File.join(FIXTURES_ROOT, path)
+end
+
+def anything
+ FlexMock.any
+end
Oops, something went wrong.

0 comments on commit 941f648

Please sign in to comment.