0
0
Fork 0

prepare for next implementation in go

This commit is contained in:
Gibheer 2022-08-29 20:53:12 +02:00
parent 5570b0b568
commit 83cb61da71
29 changed files with 0 additions and 695 deletions

View File

@ -1,9 +0,0 @@
source 'https://rubygems.org'
gem 'sequel'
gem 'pg'
gem 'zero', :git => 'https://github.com/libzero/zero.git'
gem 'slim'
gem 'sass'
gem 'kramdown'
gem 'puma'

View File

@ -1,34 +0,0 @@
GIT
remote: https://github.com/libzero/zero.git
revision: dde9fc410fb50bc44b128fb1032b15f09c53e0f7
specs:
zero (0.2.0)
tilt
GEM
remote: https://rubygems.org/
specs:
kramdown (1.3.2)
pg (0.17.1)
puma (2.7.1)
rack (>= 1.1, < 2.0)
rack (1.5.2)
sass (3.2.14)
sequel (4.7.0)
slim (2.0.2)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
temple (0.6.7)
tilt (2.0.0)
PLATFORMS
ruby
DEPENDENCIES
kramdown
pg
puma
sass
sequel
slim
zero!

View File

@ -1,8 +0,0 @@
#!/usr/bin/env rackup
require File.expand_path('../lib/boot.rb', __FILE__)
Renderer.find_templates('templates')
run Application.new(Router, {
:renderer => Renderer,
:query => {}
})

View File

@ -1 +0,0 @@
:db: "postgres://localhost/blogdb"

View File

@ -1,7 +0,0 @@
module Routes
class Atom
def self.call(session)
Post
end
end
end

View File

@ -1,11 +0,0 @@
module Routes
class Images
def self.call(session)
file = "images/#{session.request.path.gsub(/images/, '')}"
return RouteNotFound unless File.exist?(file)
session.response.body = File.read(file)
session.response.content_type = 'image/png'
nil
end
end
end

View File

@ -1,10 +0,0 @@
module Routes
class MethodNotAllowed
def self.call(session)
session.response.status = 405
session.response.content_type = 'text/html'
session.response.body = 'Method not supported by this resource!'
nil
end
end
end

View File

@ -1,63 +0,0 @@
module Routes
class Post < Controller
PREV_AND_NEXT_QUERY = <<SQL
select (
select id from posts where written < ? and released order by written desc limit 1
) older,
(
select id from posts where written > ? and released order by written limit 1
) younger
SQL
def self.get(session)
posts = define_posts_query(session)
if session.options[:id]
posts = posts.filter(:posts__id => session.options[:id].to_i)
return RouteNotFound if posts.empty?
set_previous_and_next_post(session, posts)
else
if session.request.params['search']
posts = load_fulltextsearch(session, posts)
end
page = session.request.params['page'].to_i
per_page = session.request.params['per_page'].to_i
per_page = 10 if per_page && per_page < 1
set_page_information(session, posts, page, per_page)
posts = posts.limit(per_page).offset(page * per_page)
end
session.options[:posts] = posts
session.options[:render] = 'posts/index'
end
def self.define_posts_query(session)
posts = DB[:posts].
filter(:released => true).
select(:posts__id___post_id, :written, :title, :content, :username).
join(:accounts, :id___account_id => :account_id).
reverse_order(:written, :posts__id)
end
# load posts depending on the pagination
def self.set_page_information(session, posts, page, per_page)
# compute pages
session.options[:page] = page if page
session.options[:query][:per_page] = per_page if per_page
session.options[:pages] = posts.count / per_page
end
# load a single posts and the ids of the next and previous posts
def self.set_previous_and_next_post(session, posts)
written = posts.first[:written]
session.options[:post_ids_pn] = DB[PREV_AND_NEXT_QUERY, written, written].first
end
# adjust query to use fulltext search
def self.load_fulltextsearch(session, posts)
session.options[:query][:search] = session.request.params['search']
posts.filter(
'posts.search_field @@ to_tsquery(\'english\', ?)',
session.request.params['search'].tr(' ', '&')
)
end
end
end

View File

@ -1,8 +0,0 @@
module Routes
class RouteNotFound < Controller
def self.get(session)
session.options[:render] = 'error/route_not_found'
session.response.status = 404
end
end
end

View File

@ -1,8 +0,0 @@
module Routes
class Stylesheet
def self.call(session)
session.options[:render] = 'stylesheet/index'
session.options[:renderer]
end
end
end

View File

@ -1,7 +0,0 @@
module Routes
class Welcome
def self.call(session)
Post
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View File

@ -1,19 +0,0 @@
class Application
def initialize(start, defaults = {})
@start = start
@defaults = defaults
end
def call(env)
session = Session.new(
::Zero::Request.new(env),
::Zero::Response.new,
Render.new(@defaults.clone)
)
worker = @start
while not worker.nil?
worker = worker.call(session)
end
session.response.to_a
end
end

View File

@ -1,16 +0,0 @@
$LOAD_PATH << File.expand_path('..', __FILE__)
require 'libraries'
require 'session'
require 'application'
require 'controller'
require 'renderer'
require 'render'
require 'router'
config = YAML.load_file(File.expand_path('../../config.yml', __FILE__))
DB = Sequel.connect(config[:db])
Dir[File.expand_path('../../controller', __FILE__) + '/**'].each do |controller|
require controller
end

View File

@ -1,12 +0,0 @@
class Controller
def self.call(session)
return call_method(session) if respond_to? session.request.method
Routes::MethodNotAllowed.call(session)
end
def self.call_method(session)
result = send(session.request.method, session)
return result if result.kind_of?(Class)
session.options[:renderer]
end
end

View File

@ -1,9 +0,0 @@
require 'bundler/setup'
require 'yaml'
require 'sass'
require 'tilt'
require 'tilt/sass'
require 'sequel'
require 'zero'
require 'slim'
require 'kramdown'

View File

@ -1,46 +0,0 @@
# defines a render container with some helper functions to keep the templates
# small
class Render
SLASH = '/'
def initialize(options)
@options = options
end
def [](key)
fetch(key)
end
def []=(key, value)
@options[key] = value
end
def fetch(key)
@options[key]
end
def has_key?(key)
@options.has_key?(key)
end
def options
@options
end
def link_to(*params)
query = []
while params.last.kind_of? Hash
query.push params.pop
end
return path(params) if query.empty?
path(params) + '?' + query_string(query)
end
def path(params)
SLASH + params.join(SLASH)
end
def query_string(query)
URI.encode_www_form(query.inject(:merge))
end
end

View File

@ -1,84 +0,0 @@
class Renderer
COMPONENT_MATCHER = %r{/?(?<template>[^\.]+)\.(?<type>[^\.]+)\.(?<engine>.+)}
DEFAULT_TYPE = :default_type
def self.call(session)
template = do_checks(session)
return nil unless template
type = get_type(session, template)
session.response.body = render(template, type, session.options)
session.response.content_type = type_map[type]
nil
end
def self.render(template, type, object)
layout = templates['layout']
if layout && layout.has_key?(type)
return layout[type].render(object) do
template[type].render(object)
end
end
template[type].render(object)
end
def self.do_checks(session)
unless session.options.has_key? :render
session.response.body = ':render not set!'
return nil
end
to_render = session.options[:render]
unless templates.has_key? to_render
session.response.body = 'Template does not exist!'
return nil
end
return templates[to_render]
end
# initialize the template cache
def self.find_templates(path)
templates = {}
Dir[path + '/**/*'].each do |file|
parts = file.gsub(path, '').match(COMPONENT_MATCHER)
next unless File.file?(file) && parts
templates[parts[:template]] ||= {}
templates[parts[:template]].merge!({parts[:type].to_sym => Tilt.new(file)})
end
templates
end
def self.templates
@@templates ||= find_templates 'templates'
end
def self.set_types(types)
@@type_map = types.invert
@@types = types
end
def self.types
@@types ||= {
'*/*' => DEFAULT_TYPE,
'text/css' => :css,
'text/html' => :html,
'application/atom+xml' => :atom
}
end
def self.type_map
@@type_map ||= types.invert
end
def self.get_type(session, template)
type = get_preferred_type(session.request, template)
return template.first[0] if type == DEFAULT_TYPE
type
end
def self.get_preferred_type(request, template)
request.accept.types.each do |type|
next unless types.has_key? type
return types[type] if template.has_key? types[type]
return DEFAULT_TYPE if types[type] == DEFAULT_TYPE
end
end
end

View File

@ -1,38 +0,0 @@
class Router
ROUTE_REGEX = %r{\A(/(?<controller>[a-zA-Z]+)\.?(?<extension>[a-zA-Z]+)?(/(?<id>[^/]+)?)?)?\Z}
# get the controller handling the request
def self.call(session)
variables = session.request.path.match(ROUTE_REGEX)
return default_route unless variables && variables[:controller]
session.options[:id] = variables[:id]
find(variables[:controller])
end
# the namespace of all routes
def self.namespace
@@namespace ||= ::Routes
end
# set the namespace to a module or class which holds all controllers
def self.set_namespace(namespace)
@@namespace = namespace
end
# the default route to take when no path is given
def self.default_route
@@default ||= namespace.const_get(:Welcome)
end
# set the default route to take when no path is given
def self.set_default_route(default)
@@default = default
end
# check for the controller name and return 404 when not found
def self.find(ctrl)
ctrl = ctrl.capitalize
# make constant lookup without ancestors
return namespace.const_get(ctrl, false) if namespace.const_defined?(ctrl, false)
namespace::RouteNotFound
end
end

View File

@ -1 +0,0 @@
Session = Struct.new(:request, :response, :options)

View File

@ -1,47 +0,0 @@
Sequel.migration do
change do
create_table(:accounts) do
primary_key :id
String :username, :size=>50
String :email, :size=>50
String :crypted_password, :size=>70
String :role, :size=>50
end
create_table(:tags) do
primary_key :id
String :name, :text=>true
end
create_table(:posts, :ignore_index_errors=>true) do
primary_key :id
String :title, :text=>true, :null=>false
DateTime :written
TrueClass :released, :default=>false
String :markup, :default=>"markdown", :text=>true
String :content, :text=>true
foreign_key :account_id, :accounts, :null=>false, :key=>[:id]
index [:account_id], :name=>:index_posts_account
index [:written, :id], :name=>:posts_written_id_idx
end
create_table(:comments, :ignore_index_errors=>true) do
primary_key :id
String :author, :size=>50
String :email, :size=>50
TrueClass :acknowledged, :default=>false
String :body, :text=>true
foreign_key :post_id, :posts, :null=>false, :key=>[:id]
index [:post_id], :name=>:index_comments_post
end
create_table(:post_tags) do
foreign_key :post_id, :posts, :null=>false, :key=>[:id]
foreign_key :tag_id, :tags, :null=>false, :key=>[:id]
primary_key [:post_id, :tag_id]
end
end
end

View File

@ -1,52 +0,0 @@
# this is the schema from the old platform
Sequel.migration do
up do
drop_table :post_tags
drop_table :comments
drop_table :tags
alter_table :posts do
add_column :language, String, :size => 10
end
from(:posts).update(:language => 'english')
alter_table :posts do
set_column_not_null :language
end
run 'create function search_field(posts) returns tsvector as $$' +
"select to_tsvector($1.language::regconfig, $1.title || ' ' || $1.content);" +
'$$ language sql immutable;'
end
down do
alter_table :posts do
drop_column :language
end
run 'drop function search_field(posts);'
create_table(:tags) do
primary_key :id
String :name, :text=>true
end
create_table(:comments, :ignore_index_errors=>true) do
primary_key :id
String :author, :size=>50
String :email, :size=>50
TrueClass :acknowledged, :default=>false
String :body, :text=>true
foreign_key :post_id, :posts, :null=>false, :key=>[:id]
index [:post_id], :name=>:index_comments_post
end
create_table(:post_tags) do
foreign_key :post_id, :posts, :null=>false, :key=>[:id]
foreign_key :tag_id, :tags, :null=>false, :key=>[:id]
primary_key [:post_id, :tag_id]
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1,4 +0,0 @@
p.error
'Sorry, this article does not exist. Maybe you want to go the
a(href=link_to('')) homepage
' and see, if you can find your post there?

View File

@ -1,24 +0,0 @@
doctype 5
html
head
title zero-knowledge
meta(
http-equiv='Content-Type'
content='text/html; charset=utf-8')
link(
href='/atom.xml'
rel='alternate'
title='Atom 1.0'
type='application/atom+xml')
link(
href='/stylesheet.css'
rel='stylesheet'
type='text/css')
body
header#header
a href='/'
h1 zero-knowledge
h2 no clue at all
img src='/images/zero-knowledge.png' alt='zero-knowledge'
#content
==yield

View File

@ -1,18 +0,0 @@
doctype xml
feed xmlns='http://www.w3.org/2005/Atom'
title zero-knowledge
link href='http://zero-knowledge.org'
link type='application/atom+xml' rel='self' href='http://zero-knowledge.org'
updated=fetch(:posts).first[:written].xmlschema
id http://zero-knowledge.org/
-for post in fetch(:posts)
entry
id="http://zero-knowledge.org/post/#{post[:post_id]}"
link(type='text/html' rel='alternate'
href="http://zero-knowledge.org/post/#{post[:post_id]}")
title=post[:title]
updated=post[:written].xmlschema
author
name=post[:username]
summary type='html'=post[:content]
content type='html'=Kramdown::Document.new(post[:content]).to_html

View File

@ -1,29 +0,0 @@
- if has_key?(:page) && 0 < fetch(:page)
a.page.up(
href=link_to(:post, fetch(:query), :page => fetch(:page) - 1)
) show newer
- if has_key?(:post_ids_pn) && fetch(:post_ids_pn)[:younger]
a.page.up(
href=link_to(:post, fetch(:post_ids_pn)[:younger])
) show newer
- for post in fetch(:posts)
article
header
h1
//a(href=link_to(:post, post[:post_id], fetch(:query))) post[:title]
a(href=link_to(:post, post[:post_id]))=post[:title]
footer
.author=post[:username]
.date= post[:written].strftime('%Y-%m-%d')
.time= post[:written].strftime('%H-%M')
section==Kramdown::Document.new(post[:content]).to_html
- if has_key?(:page) && fetch(:page) < fetch(:pages)
a.page.down(
href=link_to(:post, fetch(:query), :page => fetch(:page) + 1)
) show older
- if has_key?(:post_ids_pn) && fetch(:post_ids_pn)[:older]
a.page.down(
href=link_to(:post, fetch(:post_ids_pn)[:older])
) show older

View File

@ -1,130 +0,0 @@
// normal text color
$normal_color: hsl(0,0, 90%);
// the nice orange
$action_color: hsl(32, 100%, 50%);
// the blue
$alternate_color: hsl(204, 35%, 35%);
// box color for quotes and code
$box_color: hsla(0, 0%, 0%, 0);
// standard size to size everything from that
$standard-width: 1em;
// other variables
$box_border: $standard-width solid $box_color;
html {
font-size: 99%;
font-family: sans-serif;
/* original picture from http://subtlepatterns.com/iron-grip/ */
background-image: url('/images/irongrip-zero.png');
min-width: 300px;
width: 100%;
max-width: 1100px;
margin: auto;
color: $normal_color;
}
code {
font-family: "PT mono", monospace;
}
pre {
border: $box_border;
overflow-x: auto;
}
blockquote {
border: $box_border;
margin: 0;
font-style: italic;
p:first-child {
margin-top: 0;
}
p:last-child {
margin-bottom: 0;
}
}
#header {
h1, h2 {
display: none;
margin: $standard-width / 2;
padding: 0;
}
}
#content {
padding-left: $standard-width;
padding-right: $standard-width;
}
a {
text-decoration: none;
color: $action-color;
}
.page {
display: block;
text-align: center;
padding: $standard-width;
font-size: $standard-width * 0.9;
font-weight: bold;
&.up {
border-top: $standard-width / 4 solid $alternate-color;
&:hover, &:focus, &:active {
border-top: $standard-width / 4 solid $action-color;
}
}
&.down {
border-bottom: $standard-width / 4 solid $alternate-color;
&:hover, &:focus, &:active {
border-bottom: $standard-width / 4 solid $action-color;
}
}
}
.error {
padding: $standard-width;
}
#content, pre, blockquote {
background-color: fade-out($alternate-color, 0.7);
}
article {
padding-top: $standard-width;
padding-bottom: $standard-width;
header {
padding: $standard-width / 4;
display: inline-block;
h1 { margin: 0;}
h1, h2 { display: inline-block; }
}
footer {
padding: $standard-width / 4;
display: inline-block;
font-size: $standard-width * 0.9;
div {
display: inline-block;
margin: $standard-width / 5;
font-weight: bold;
color: hsl(0, 0%, 60%);
}
.author:before { content: 'by '; }
.date:before { content: ' on '; }
.time { display: none; }
}
section {
border-top: $standard-width/5 solid $alternate-color;
word-wrap: break-word;
h1 { font-size: $standard-width * 1.3 }
h2 { font-size: $standard-width * 1.2 }
h3 { font-size: $standard-width * 1.1; font-weight: normal; }
h1, h2, h3 {
border-bottom: $standard-width / 10 solid $normal-color;
margin-right: 25%;
}
code, pre { word-wrap: initial; }
}
}