diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..d7c18ec --- /dev/null +++ b/Gemfile @@ -0,0 +1,13 @@ +source "http://rubygems.org/" + +# Framework +gem "camping" + +# Template engine +gem "tilt" +gem "haml" + +# Maruku for content +gem "maruku" +gem "ultraviolet" + diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..b05c428 --- /dev/null +++ b/config.ru @@ -0,0 +1,2 @@ +require 'timeless' +run Timeless diff --git a/maruku-uv.rb b/maruku-uv.rb new file mode 100644 index 0000000..af183d7 --- /dev/null +++ b/maruku-uv.rb @@ -0,0 +1,45 @@ +require 'uv' +require 'maruku' + +module MaRuKu::Out::HTML + def to_html_code; + source = self.raw_code + lang = self.attributes[:lang] || @doc.attributes[:code_lang] + lang = 'xml' if lang=='html' + + use_syntax = get_setting :html_use_syntax + + element = + if lang + begin + # eliminate trailing newlines otherwise Syntax crashes + source = source.gsub(/\n*\Z/,'') + + html = Uv.parse(source, "xhtml", lang, false, "idle") + html = html.gsub(/\'/,''') # IE bug + html = html.gsub(/'/,''') # IE bug + + code = Document.new(html, {:respect_whitespace =>:all}).root + code.name = 'code' + code.attributes['class'] = lang + code.attributes['lang'] = lang + + pre = Element.new 'pre' + pre << code + pre + rescue Object => e + maruku_error"Error while using the syntax library for code:\n#{source.inspect}"+ + "Lang is #{lang} object is: \n"+ + self.inspect + + "\nException: #{e.class}: #{e.message}\n\t#{e.backtrace.join("\n\t")}" + + tell_user("Using normal PRE because the syntax library did not work.") + to_html_code_using_pre(source) + end + else + to_html_code_using_pre(source) + end + + add_ws element + end +end diff --git a/timeless.rb b/timeless.rb new file mode 100644 index 0000000..33bb06f --- /dev/null +++ b/timeless.rb @@ -0,0 +1,108 @@ +require 'bundler' +Bundler.setup + +require 'yaml' +require 'time' + +require 'camping' +require 'tilt' +require 'haml' +require 'maruku' +require './maruku-uv' + +Camping.goes :Timeless + +require './timeless/models/entry' +require './timeless/models/change' + +class NotFound < StandardError; end + +module Timeless + set :views, File.dirname(__FILE__) + '/timeless/views' + set :dynamic_templates, true + + set :haml, { + :format => :html5, + :escape_html => true, + :ugly => true + } + + def service(*a) + super + rescue NotFound + @status = 404 + @method = :r404 + super(@env["PATH_INFO"]) + end +end + +module Timeless::Controllers + class Index + def get + @change = Change.last + render :index + end + + def main_class; "frontpage" end + end + + class Stylesheet < R '/style\.css' + def get + @headers['Content-Type'] = "text/css" + File.read("public/style.css") + end + end + + class Changelog < R '/changelog' + def get + @changes = Change.all + render :changes + end + end + + class ChangelogN + def get(id) + @change = Change.new(id) + case @change.type + when :entry + redirect(Entry, @change.entry) + when :text + render :layout, :layout => false do + render :_change, :locals => { :change => @change } + end + end + end + end + + class Feed < R '/changelog\.xml' + def get + @headers['Content-Type'] = 'text/xml' + @changes = Change.all.reject { |c| c["feed"] == false } + render :feed, :layout => false + end + end + + class Entry < R '/(.+?)' + def get(name) + @entry = Models::Entry.new(name) + + if @entry.file? + @headers['Content-Type'] = Rack::Mime.mime_type(File.extname(@entry.name)) + @entry.to_html + else + render :entry + end + end + end +end + +module Timeless::Helpers + def main_class; false end + + def entry_index + entries = Timeless::Models::Entry.all.reject { |e| e.file? } + half = entries.size / 2 + [entries[0...half], entries[half..-1]] + end +end + diff --git a/timeless/models/change.rb b/timeless/models/change.rb new file mode 100644 index 0000000..6500987 --- /dev/null +++ b/timeless/models/change.rb @@ -0,0 +1,50 @@ +module Timeless::Models + class Change + attr_reader :id + + def self.last + new(Dir["changes/*.yaml"].sort.last[/\d+/]) + end + + def self.all(options = {}) + limit = (options[:limit] || 0) - 1 + Dir["changes/*.yaml"].sort.reverse[0..limit].map { |c| new(c[/\d+/]) } + end + + def initialize(id) + @id = id.to_i + raise NotFound unless exists? + end + + def filename; "changes/#{@id}.yaml"; end + def exists?; File.exists?(filename) end + + def content + @content ||= YAML.load_file(filename) + end + + def [](key); content[key.to_s]; end + def created_at; Time.parse(self["created_at"]) end + + def type + content.has_key?("entry") ? :entry : :text + end + + def title + type == :entry ? entry.title : content["title"] + end + + def to_html + type == :entry ? entry.to_snip : Maruku.new(content["text"]).to_html + end + + def url + type == :entry ? "/#{entry.name}" : "/changelog/#{@id}" + end + + def entry + @entry ||= Entry.new(content["entry"]) + end + end +end + diff --git a/timeless/models/entry.rb b/timeless/models/entry.rb new file mode 100644 index 0000000..7dd45db --- /dev/null +++ b/timeless/models/entry.rb @@ -0,0 +1,54 @@ +module Timeless::Models + class Entry + attr_reader :name + + def self.all + entries.map { |name| new(name) } + end + + def self.entries + Dir["content/*"].reject { |f| f =~ /~/ }.map { |f| File.basename(f) } + end + + def initialize(name) + @name = name + raise NotFound unless exists? + end + + def to_param; @name end + def <=>(o); title <=> o.title end + def filename; "content/#{@name}" end + def file?; @name.include?(".") end + def exists?; File.exists?(filename) end + + def to_html + file? ? content : maruku.to_html + end + + def title + file? ? @name : maruku.get_setting(:title) || @name + end + + def subtitle + file? ? "" : maruku.get_setting(:subtitle) || "" + end + + def to_snip + Maruku.new(content(true)).to_html + end + + def content(snip = false) + content = File.read(filename) + if snip + content =~ /\(snip\)/ + $` || content + else + content.gsub('(snip)', '') + end + end + + def maruku + @maruku ||= Maruku.new(content) + end + end +end diff --git a/timeless/views/_change.haml b/timeless/views/_change.haml new file mode 100644 index 0000000..c4cd92b --- /dev/null +++ b/timeless/views/_change.haml @@ -0,0 +1,9 @@ +%article + %header + %h1 + %a{:href => change.url}= change.title + + != change.to_html + - if change.type == :entry + %p + %a{:href => change.url} Continue to full post. diff --git a/timeless/views/changes.haml b/timeless/views/changes.haml new file mode 100644 index 0000000..51b8363 --- /dev/null +++ b/timeless/views/changes.haml @@ -0,0 +1,7 @@ +%header + %h1 Recent Changes + +%div(class="changelog") + - @changes.each do |change| + != render :_change, :locals => { :change => change } + diff --git a/timeless/views/entry.haml b/timeless/views/entry.haml new file mode 100644 index 0000000..7ab7f9e --- /dev/null +++ b/timeless/views/entry.haml @@ -0,0 +1,4 @@ +%header + %h1= @entry.title + +%p!= @entry.to_html diff --git a/timeless/views/feed.erb b/timeless/views/feed.erb new file mode 100644 index 0000000..a972875 --- /dev/null +++ b/timeless/views/feed.erb @@ -0,0 +1,25 @@ + + + Magnus Holm's timeless repository + Thoughts on life, internet and programming + tag:judofyr.net,1992-10-15:/ + + + <%= Time.now.xmlschema %> + + Magnus Holm + judofyr@gmail.com + http://judofyr.net + +<% @changes.each do |change|;c=change.created_at.utc %> + + <%= change.title %> + tag:timeless.judofyr.net,<%=c.xmlschema[0..9]%>:<%=c.to_i%> + + <%= c.xmlschema %> + <%= c.xmlschema %> + ]]> + +<% end %> + + diff --git a/timeless/views/index.haml b/timeless/views/index.haml new file mode 100644 index 0000000..4ade841 --- /dev/null +++ b/timeless/views/index.haml @@ -0,0 +1,28 @@ +%div(class="changelog") + != render :_change, :locals => { :change => @change } + +%hr + +%p(class="front-buttons") + %a(href="http://feeds.feedburner.com/MagnusHolm" class="d") Subscribe to the feed + %a(href="/timeless" class="a") What kind of blog is this really? + %a(href="#index" class="b") Check out all the articles below ↓ + %a(href="/changelog" class="c") Have a look at the recent changes + %div(style="clear:both") + %p(style="clear:both") + +%hr + +%h2 All articles + +%nav(class="index" id="index") + - entry_index.each do |entries| + %div(class="column") + %ul + - entries.each do |entry| + %li + %a{:href => R(Entry, entry)} + = entry.title + %p= entry.subtitle + %div(style="clear:both") + diff --git a/timeless/views/layout.haml b/timeless/views/layout.haml new file mode 100644 index 0000000..20d997c --- /dev/null +++ b/timeless/views/layout.haml @@ -0,0 +1,22 @@ +!!! +%head + %meta(charset="utf-8") + %title Timeless from Magnus Holm + %link{:rel => "stylesheet", :href => R(Stylesheet)} + +%body(class=main_class) + %header(class="main") + %h1 + %a{:href => R(Index)} + Magnus Holm's timeless repository + + %article + != yield + + %footer + %p + 2010 © + %a(href="mailto:judofyr@gmail.com") Magnus Holm + — + %a{:href => R(Entry, 'comments')} Leave a comment +