#!/usr/bin/env ruby
$:.unshift File.dirname(__FILE__) + '/../lib'
require 'camping'
require 'camping/ar'
require 'camping/session'
require 'redcloth'
Camping.goes :Blog
module Blog
include Camping::Session
module Models
class Post < Base
belongs_to :user
before_save do |record|
cloth = RedCloth.new(record.body)
cloth.hard_breaks = false
record.html_body = cloth.to_html
end
end
class Comment < Base; belongs_to :user; end
class User < Base; end
class BasicFields < V 1.1
def self.up
create_table :blog_posts, :force => true do |t|
t.integer :user_id, :null => false
t.string :title, :limit => 255
t.text :body, :html_body
t.timestamps
end
create_table :blog_users, :force => true do |t|
t.string :username, :password
end
create_table :blog_comments, :force => true do |t|
t.integer :post_id, :null => false
t.string :username
t.text :body, :html_body
t.timestamps
end
User.create :username => 'admin', :password => 'camping'
end
def self.down
drop_table :blog_posts
drop_table :blog_users
drop_table :blog_comments
end
end
end
module Controllers
class Index
def get
@posts = Post.all(:order => 'updated_at DESC')
render :index
end
end
class PostNew
def get
require_login!
@post = Post.new
render :add
end
def post
require_login!
post = Post.create(:title => input.post_title, :body => input.post_body,
:user_id => @state.user_id)
redirect PostN, post
end
end
class PostN
def get(post_id)
@post = Post.find(post_id)
render :view
end
end
class Edit < R '/post/(\d+)/edit'
def get(post_id)
require_login!
@post = Post.find(post_id)
render :edit
end
def post(post_id)
require_login!
@post = Post.find(post_id)
@post.update :title => input.post_title, :body => input.post_body
redirect PostN, @post
end
end
class Login
def get
render :login
end
def post
@user = User.find_by_username_and_password(input.username, input.password)
if @user
@state.user_id = @user.id
redirect R(Index)
else
@info = 'Wrong username or password.'
end
render :login
end
end
class Logout
def get
@state.user_id = nil
redirect Index
end
end
class Style < R '/styles\.css'
STYLE = File.read(__FILE__).gsub(/.*__END__/m, '')
def get
@headers['Content-Type'] = 'text/css; charset=utf-8'
STYLE
end
end
end
module Helpers
def logged_in?
!!@state.user_id
end
def require_login!
unless logged_in?
redirect Controllers::Login
throw :halt
end
end
end
module Views
def layout
html do
head do
title 'My Blog'
link :rel => 'stylesheet', :type => 'text/css',
:href => '/styles.css', :media => 'screen'
end
body do
h1 { a 'My Blog', :href => R(Index) }
div.wrapper! do
self << yield
end
hr
p.footer! do
if logged_in?
_admin_menu
else
a 'Login', :href => R(Login)
text ' to the adminpanel'
end
text! ' – Powered by '
a 'Camping', :href => 'http://camping.rubyforge.org/'
end
end
end
end
def index
if @posts.empty?
h2 'No posts'
p do
text 'Could not find any posts. Feel free to '
a 'add one', :href => R(PostNew)
text ' yourself. '
end
else
@posts.each do |post|
_post(post)
end
end
end
def login
h2 'Login'
p.info @info if @info
form :action => R(Login), :method => 'post' do
input :name => 'to', :type => 'hidden', :value => @to if @to
label 'Username', :for => 'username'
input :name => 'username', :id => 'username', :type => 'text'
label 'Password', :for => 'password'
input :name => 'password', :id => 'password', :type => 'password'
input :type => 'submit', :class => 'submit', :value => 'Login'
end
end
def add
_form(@post, :action => R(PostNew))
end
def edit
_form(@post, :action => R(Edit, @post))
end
def view
_post(@post)
end
# partials
def _admin_menu
text! [['Log out', R(Logout)], ['New', R(PostNew)]].map { |name, to|
mab { a name, :href => to}
}.join(' – ')
end
def _post(post)
h2 { a post.title, :href => R(PostN, post) }
p.info do
text! "Written by <strong>#{post.user.username}</strong> "
text post.updated_at.strftime('%B %M, %Y @ %H:%M ')
_post_menu(post)
end
text! post.html_body
end
def _post_menu(post)
if logged_in?
a '(edit)', :href => R(Edit, post)
end
end
def _form(post, opts)
form({:method => 'post'}.merge(opts)) do
label 'Title', :for => 'post_title'
input :name => 'post_title', :id => 'post_title', :type => 'text',
:value => post.title
label 'Body', :for => 'post_body'
textarea post.body, :name => 'post_body', :id => 'post_body'
input :type => 'hidden', :name => 'post_id', :value => post.id
input :type => 'submit', :class => 'submit', :value => 'Submit'
end
end
end
end
def Blog.create
Blog::Models.create_schema :assume => (Blog::Models::Post.table_exists? ? 1.0 : 0.0)
end
__END__
* {
margin: 0;
padding: 0;
}
body {
font: normal 14px Arial, 'Bitstream Vera Sans', Helvetica, sans-serif;
line-height: 1.5;
}
h1, h2, h3, h4 {
font-family: Georgia, serif;
font-weight: normal;
}
h1 {
background-color: #EEE;
border-bottom: 5px solid #6F812D;
outline: 5px solid #9CB441;
font-weight: normal;
font-size: 3em;
padding: 0.5em 0;
text-align: center;
}
h2 {
margin-top: 1em;
font-size: 2em;
}
h1 a { color: #143D55; text-decoration: none }
h1 a:hover { color: #143D55; text-decoration: underline }
h2 a { color: #287AA9; text-decoration: none }
h2 a:hover { color: #287AA9; text-decoration: underline }
#wrapper {
margin: 3em auto;
width: 700px;
}
p {
margin-bottom: 1em;
}
p.info, p#footer {
color: #999;
margin-left: 1em;
}
p.info a, p#footer a {
color: #999;
}
p.info a:hover, p#footer a:hover {
text-decoration: none;
}
a {
color: #6F812D;
}
a:hover {
color: #9CB441;
}
hr {
border-width: 5px 0;
border-style: solid;
border-color: #9CB441;
border-bottom-color: #6F812D;
height: 0;
}
p#footer {
font-size: 0.9em;
margin: 0;
padding: 1em;
text-align: center;
}
label {
display: block;
width: 100%;
}
input, textarea {
padding: 5px;
margin-bottom: 1em;
margin-right: 490px;
width: 200px;
}
input.submit {
width: auto;
}
textarea {
font: normal 14px Arial, 'Bitstream Vera Sans', Helvetica, sans-serif;
height: 300px;
width: 400px;
}