public
Last active — forked from h0rs3r4dish/README.markdown

  • Download Gist
README.markdown
Markdown

Basics

The method function will create a new page: function "home" do "Hello, world!" end Save it as any Ruby file (*.rb), then run scratch.rb and point a browser to http://localhost:8000/home to see the result

Arguments can be added (with a given prompt): function "home", [:name] do |thename| # Note the variables don't have to be the same "Hello, %s!" % thename end

You can specify the size of the input (xNumber is a text box, NxN is a textarea) function "home", [:name, :school_x20, :desc_30x10] do |name, school, desc| "Hello, %s! You go to %s, right? %s" % [name, school, desc] end

Note that the last string returned will be the resulting display. \n is automatically converted to <br />, so don't worry about manually doing that. function "count", [:to] do |to| show = String.new # AKA "" to.to_i.times { |i| show += i.to_s+"\n" } show end

Anything that starts with 'marshal_' (ex, marshal_data) will be run through Marshal and saved to its name.db (ex, data.db) for later loading: marshal_data = Array.new function "item", [:name, :desc_x40] do |name, desc| marshal_data.push([name,desc]) end That will leave a "data.db" containing the array of items you added (by visiting localhost:8000/item

As for loading those same variables? There's an easy way to load marshal_ data, though the load_marshal function: marshal_data = load_marshal "marshal_data", Array It will either return the loaded data, or a new object of the data type you specify (in thise case, an array)

You can link to pages with content: animals = ["Cat","Dog","Horse","Fish"] function "animal", [:number] do |num| animals[num.to_i-1] end function "random" do r = rand(4) "%s' % [r, animals[r]] end Visit localhost:8000/random for a random animal link

What about locahost:8000/, though? Easy: function do # stuff goes here, it's a home page end

Asthetic Stuff

Some non-core but possibly useful tools also included in scratch.rb are paths and manual page titles.

Paths let you make virtual folders. For example: path_is "/test" function "hello" do "Hello, world!" end That will create, instead of localhost:8000/hello, localhost:8000/test/hello

This lets you create pages with the same name in different places: function "found" do "YOU LOOK LOST" end path_is "/secret" function "found" do "YOU FOUND THE SECRET!" end Visiting localhost:8000/secret/found will run the "found" method, versus just localhost:8000/found ("YOU LOOK LOST")

What if you want to redirect? There's an intuitive function for that: redirect "source" => "destination" Don't limit yourself, though! redirect "foo" => "bar", "bar" => "other", "third" => "other", "/hi_mom/foo" => "other" (Which would point /foo to /bar, /bar, /hi_mom/foo and /third to /other)

What about redirecting in the middle of a page function? There's a redirect_to for that: function "test" do if rand(2) == 1 then redirect_to "/home" else "You got a 0 from rand(2)!" end end
Which would randomly send viewers of /test to /home or show them a string.

Normally, the title is just the function name, minus any paths that may be tacked on to the URL. But you can make your own, if you want: function "home" do set_title "Blog" "Hello, welcome home." end Instead of having the page title of "home - Mozilla Firefox" (or whatever your default title is), it will be known as "Blog - Mozilla Firefox". Since it's a string, you can mess with it, of course, including dynamic titles; function "home", [:user] do |user| set_title "%s's Home" % user "%s isn't here right now." % user end

Some other stuff

Let's talk special variables. These are added to pages' variable request arrays, and start with an underscore (:_varname):

  • _path => The current path (ex, /blog/view [minus ?var=val])
  • _req => The WEBrick::HTTPRequest
  • _res => The WEBrick::HTTPResponse

Conclusion

You can create entire, simple web apps from scratch.rb. It will load every Ruby file it finds in the current directory, including all of their required files. You could set up an app like: app/ |-- scratch.rb |-- app.rb |-- data/ `-- scripts/ |-- more_stuff.rb And it would run just fine, providing app.rb loaded the files inside of scripts/

Code Samples

A simple wiki: $marshal_wiki = Hash.new function "create", [:title, :text_70x10] do |title, body| $marshal_wiki[title] = body "Page '%s' created." % title end function "view", [:title] do |title| $marshal_wiki[title] end function do '''view
create''' end

A blog: class Post < Struct.new(:title,:date,:body) def to_html return "

%s

%s

%s

" % [self.title, self.date, self.body] end end $marshal_posts = load_marshal "$marshal_posts", Array path_is "/blog" function "write", [:title_x60, :text_60x10] do |title, text| $marshal_posts.push Post.new(title,Time.now.to_s,text) "Post created." end function "view", [:p] do |p| $marshal_posts[p-1].to_html or "No such post" end function do s = "New Post
" 5.times { |i| i += 1 s += $marshal_posts[-i].to_html + "
" unless $marshal_posts.length < i } s end

Changelog

0.7.4* - Added the _path variable, and allowed for empty return strings from page functions

0.7.3 - Removed the original mode of setting the title (returning [body,title] from a page) and replaced with set_title and an automatic title set

0.7.2 - Added load_marshal and fixed a bug regarding the path on file load

scratch.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
#!/usr/bin/env ruby
=begin
A simple microframework, designed for ease of use over functionality. It's not a
serious project, and probably shouldn't be used for any serious work as a result
of this.
 
See the README for a rundown of the features.
 
Based on (but not a port of) Lion Kimbro's scratch.py:
<http://www.speakeasy.org/~lion/proj/scratch/>
=end
 
SCRATCH_VERSION = "0.7.5"
 
$scratch_pathlist = Array.new
$scratch_methodlist = Hash.new
$scratch_files = Array.new
$scratch_marshallist = Array.new
$scratch_pagetitle = nil
 
$scratch_template_body = '''<html>
<body>%s</body>
</html>'''
$scratch_template_title = '''<html>
<head>
<title>%s</title>
</head>
<body>%s</body>
</html>'''
 
# Quick monkeypatch for a String#starts_with?(str) function, a la Python
class String
def starts_with?(prefix)
prefix = prefix.to_s
self[0, prefix.length] == prefix
end
def is_path?
($scratch_pathlist.index(self) || $scratch_pathlist.index(self+"/"))
end
end
 
def scratch_log(*args)
puts args.join(" ")
end
 
def scratch_quit
Server.shutdown if Server
Kernel.abort
end
 
def path_is string
string += "/" if string[-1].chr != '/'
# return if $scratch_pathlist.index(string)
$scratch_pathlist.push(string)
end
 
def show_form_for path, given, list
ret = "<form action='#' method='post'><table>"
list.each { |item|
name = item.to_s
dim = [ :auto ]
if name =~ /_(\d+)x(\d+)$/ then
dim = [ $1.to_i, $2.to_i ]
name.sub!("_#{$1}x#{$2}","")
elsif name =~ /_x(\d+)$/ then
dim = [ $1.to_i ]
name.sub!("_x#{$1}","")
end
val = ((given[item.to_s]) ? given[item.to_s] : "")
ret += "<tr><td>" + name + "</td><td>"
if dim.length == 1 then
ret += "<input type='text' name='%s' size='%s' value='%s'/>" % [item, ((dim[0] == :auto) ? 20 : dim[0]), val]
else
ret += "<textarea name='%s' cols='%s' rows='%s'>%s</textarea>" % [item, dim[0], dim[1], val]
end
ret += "</td></tr>"
}
ret += "</table><input type='submit' value='Submit' /></form>"
return ret
end
 
def handle_proc(script,args,block)
return Proc.new { |req, res|
res['Content-Type'] = 'text/html'
found_args = Array.new
qury = req.query
qury["_path"] = req.path if args.index(:_path)
if qury.keys.length < args.length then
title = ($scratch_pagetitle) ? $scratch_pagetitle : ((script == "" || script.is_path?) ? "Index" : script.split('/')[-1])
res.body = $scratch_template_title % [title, show_form_for(script, qury, args)]
else
args.each { |argument| found_args.push(qury[argument.to_s]) }
ret = block.call(*found_args) || String.new
save_marshals
if ret =~ /^meta_scratch_redirect (.+)/ then
res.set_redirect(WEBrick::HTTPStatus::Found, $1)
else
title = ($scratch_pagetitle) ? $scratch_pagetitle : ((script == "" || script.is_path?) ? "Index" : script.split('/')[-1])
res.body = $scratch_template_title % [title,ret.gsub("\n","<br />")]
$scratch_pagetitle = nil
end
end
}
end
 
def redirect hash
hash.each_pair { |from, to|
from = $scratch_pathlist[-1] + from if from == "" || from[0].chr != "/"
to = $scratch_pathlist[-1] + to if to == "" || to[0].chr != "/"
scratch_log "Redirecting '%s' to '%s'" % [from,to]
Server.mount_proc(from) { |req, res|
res.set_redirect(WEBrick::HTTPStatus::Found, to)
}
}
end
 
def redirect_to path
return "meta_scratch_redirect %s" % path
end
 
def require_scratch_version num
raise Error, "Requires scratch.rb >=#{num} (current: #{SCRATCH_VERSION})" if SCRATCH_VERSION < num
end
 
def function(name="",args=[],&block)
path = $scratch_pathlist[-1]
scratch_log "Mounting %s to %s" % [((name == "") ? "index" : name),path+name]
Server.mount_proc(path+name,handle_proc(path+name,args,block))
$scratch_methodlist[path] = Array.new if !$scratch_methodlist.has_key?(path)
$scratch_methodlist[path].push name
end
 
def list_pages
path = $scratch_pathlist[-1]
show = String.new
$scratch_methodlist[path].each { |link|
show += "<a href='#{path+link}'>#{link}</a><br />"
}
[show,"Contents of #{path}"]
end
 
def save_marshals
$scratch_marshallist.each { |var|
varnm = (var[0].chr == "$") ? var[1..var.length-1] : var
File.open(varnm[8..varnm.length-1]+".db","w") do |file|
file.puts Marshal.dump(eval(var))
end
}
end
 
def load_marshal(name,type=Hash)
name = name[1..name.length-1] if name.starts_with? "$"
name = name[8..name.length-1] if name.starts_with? "marshal_"
return (File.exists?(name+".db")) ? Marshal.load(IO.readlines(name+".db").join) : type.new
end
 
def current_path
return $scratch_pathlist[-1]
end
 
def set_title str
$scratch_pagetitle = str
end
 
 
Dir.foreach("./") { |file| $scratch_files.push(file) }
$scratch_files.each { |item| $scratch_files -= [item] if item[-3..-1] != '.rb' || item == __FILE__}
if $scratch_files.length < 1 then
scratch_log "No source files, shutting down"
scratch_quit
end
 
 
# Set up WEBrick
require 'webrick' # So much fun
scratch_log "Loaded WEBrick..."
Server = WEBrick::HTTPServer.new(:Port => 8000)
['INT', 'TERM'].each { |signal|
trap(signal){ scratch_quit }
}
 
# Go through methods
$scratch_files.each { |file|
scratch_log "Loading file '%s'" % file
path_is "/"
load file
}
 
# Collect all the to-be-Marshal'd variables
local_variables.each { |var| $scratch_marshallist.push(var) if var.starts_with?("marshal_") }
global_variables.each { |var| $scratch_marshallist.push(var) if var.starts_with?("$marshal_") }
scratch_log "Marshal list: %s" % $scratch_marshallist.join(", ")
 
# Charge!
Server.start

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.