vanilla 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +52 -0
- data/Rakefile +118 -0
- data/bin/vanilla +9 -0
- data/config.example.yml +5 -0
- data/config.ru +9 -0
- data/lib/defensio.rb +59 -0
- data/lib/tasks/vanilla.rake +178 -0
- data/lib/vanilla/app.rb +87 -0
- data/lib/vanilla/console.rb +3 -0
- data/lib/vanilla/dynasnip.rb +110 -0
- data/lib/vanilla/dynasnips/comments.rb +108 -0
- data/lib/vanilla/dynasnips/current_snip.rb +32 -0
- data/lib/vanilla/dynasnips/debug.rb +13 -0
- data/lib/vanilla/dynasnips/edit.rb +63 -0
- data/lib/vanilla/dynasnips/edit_link.rb +24 -0
- data/lib/vanilla/dynasnips/index.rb +11 -0
- data/lib/vanilla/dynasnips/kind.rb +70 -0
- data/lib/vanilla/dynasnips/link_to.rb +14 -0
- data/lib/vanilla/dynasnips/link_to_current_snip.rb +16 -0
- data/lib/vanilla/dynasnips/login.rb +56 -0
- data/lib/vanilla/dynasnips/new.rb +14 -0
- data/lib/vanilla/dynasnips/notes.rb +42 -0
- data/lib/vanilla/dynasnips/pre.rb +19 -0
- data/lib/vanilla/dynasnips/rand.rb +27 -0
- data/lib/vanilla/dynasnips/raw.rb +19 -0
- data/lib/vanilla/dynasnips/url_to.rb +7 -0
- data/lib/vanilla/renderers/base.rb +78 -0
- data/lib/vanilla/renderers/bold.rb +9 -0
- data/lib/vanilla/renderers/erb.rb +16 -0
- data/lib/vanilla/renderers/markdown.rb +13 -0
- data/lib/vanilla/renderers/raw.rb +9 -0
- data/lib/vanilla/renderers/ruby.rb +35 -0
- data/lib/vanilla/renderers/textile.rb +13 -0
- data/lib/vanilla/request.rb +68 -0
- data/lib/vanilla/routes.rb +29 -0
- data/lib/vanilla/snip_handling.rb +33 -0
- data/lib/vanilla/snips/start.rb +18 -0
- data/lib/vanilla/snips/system.rb +76 -0
- data/lib/vanilla/snips/tutorial.rb +158 -0
- data/lib/vanilla/test_snips.rb +85 -0
- data/lib/vanilla.rb +20 -0
- data/spec/dynasnip_spec.rb +31 -0
- data/spec/renderers/base_renderer_spec.rb +40 -0
- data/spec/renderers/erb_renderer_spec.rb +27 -0
- data/spec/renderers/markdown_renderer_spec.rb +29 -0
- data/spec/renderers/raw_renderer_spec.rb +21 -0
- data/spec/renderers/ruby_renderer_spec.rb +42 -0
- data/spec/renderers/vanilla_app_detecting_renderer_spec.rb +35 -0
- data/spec/soup_test.db +0 -0
- data/spec/spec_helper.rb +64 -0
- data/spec/vanilla_app_spec.rb +38 -0
- data/spec/vanilla_presenting_spec.rb +84 -0
- data/spec/vanilla_request_spec.rb +73 -0
- data/spec/vanilla_snip_finding_spec.rb +28 -0
- metadata +122 -0
data/README
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--~::{ Vanilla.rb }::~---
|
2
|
+
=========================
|
3
|
+
|
4
|
+
A wandering soul; a repo clone'd -
|
5
|
+
his meta_klass and methods honed.
|
6
|
+
But tarry he 'ponst what dark endeavour
|
7
|
+
with code of such unknowable terror?
|
8
|
+
|
9
|
+
- H.P. Gemcraft, 1914
|
10
|
+
|
11
|
+
A Preface; A Warning
|
12
|
+
====================
|
13
|
+
|
14
|
+
The base flavour. The classic ice cream. Except riddled through with blood-red gemstones - Ruby gemstones.
|
15
|
+
This mad gelato will break your teeth.
|
16
|
+
|
17
|
+
... and run the freakiest wiki-wonki-wiki-wonki-wiki-wonki-wickedy-wiki you've ever seen.
|
18
|
+
|
19
|
+
But you must not fear: fear is the mind-killer; fear is the little death that brings total oblivion;
|
20
|
+
fear is the chilling terror that makes you believe that *everything* is better when Model lays with
|
21
|
+
Controller and View in peace.
|
22
|
+
|
23
|
+
Vanilla.rb cries "HERESY!" upon that layered orgy of filth. Vanilla.rb says "EMBRACE CHAOS!". Vanilla.rb
|
24
|
+
says "EVERY SNIP FOR THEMSELVES!". Vanilla.rb waits quietly, in the dark corners.
|
25
|
+
|
26
|
+
Use at your own risk; really. I showed it to a cat, and the cat started talking in French.
|
27
|
+
It was MESSED. UP.
|
28
|
+
|
29
|
+
|
30
|
+
Thee Darke Invocation
|
31
|
+
=====================
|
32
|
+
|
33
|
+
$ gem install soup sqlite3-ruby rack ratom RedCloth BlueCloth
|
34
|
+
|
35
|
+
$ rake setup
|
36
|
+
|
37
|
+
... a bunch of stuff gets created
|
38
|
+
|
39
|
+
$ rackup
|
40
|
+
|
41
|
+
... the wonki starts. THE PAIN BEGINS.
|
42
|
+
|
43
|
+
(Try going to http://localhost:9292/vanilla-rb-tutorial to salve the chafing.)
|
44
|
+
|
45
|
+
|
46
|
+
Working with Vanilla
|
47
|
+
====================
|
48
|
+
|
49
|
+
Currently unknowable; soon I hope to compile such a nercronomicon as to allow mortal kind to
|
50
|
+
conjure up Snips and Dynasnips and Renderers...
|
51
|
+
|
52
|
+
For the moment, see http://interblah.net/vanilla-rb-tutorial
|
data/Rakefile
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
|
+
require 'vanilla'
|
3
|
+
|
4
|
+
require 'spec'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
Spec::Rake::SpecTask.new do |t|
|
7
|
+
t.spec_opts = %w(--format specdoc --colour)
|
8
|
+
t.libs = ["spec"]
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "rake/gempackagetask"
|
16
|
+
require "rake/rdoctask"
|
17
|
+
|
18
|
+
# This builds the actual gem. For details of what all these options
|
19
|
+
# mean, and other ones you can add, check the documentation here:
|
20
|
+
#
|
21
|
+
# http://rubygems.org/read/chapter/20
|
22
|
+
#
|
23
|
+
spec = Gem::Specification.new do |s|
|
24
|
+
|
25
|
+
# Change these as appropriate
|
26
|
+
s.name = "vanilla"
|
27
|
+
s.version = "1.0.0"
|
28
|
+
s.summary = "A bliki-type web content thing."
|
29
|
+
s.author = "James Adam"
|
30
|
+
s.email = "james@lazyatom.com.com"
|
31
|
+
s.homepage = "http://github.com/lazyatom/vanilla-rb"
|
32
|
+
|
33
|
+
s.has_rdoc = true
|
34
|
+
s.extra_rdoc_files = %w(README)
|
35
|
+
s.rdoc_options = %w(--main README)
|
36
|
+
|
37
|
+
# Add any extra files to include in the gem
|
38
|
+
s.files = %w(config.example.yml config.ru Rakefile README) + Dir.glob("{spec,lib,bin}/**/*")
|
39
|
+
s.executables = ['vanilla']
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
|
42
|
+
# If you want to depend on other gems, add them here, along with any
|
43
|
+
# relevant versions
|
44
|
+
# s.add_dependency("some_other_gem", "~> 0.1.0")
|
45
|
+
|
46
|
+
s.add_development_dependency("rspec") # add any other gems for testing/development
|
47
|
+
|
48
|
+
# If you want to publish automatically to rubyforge, you'll may need
|
49
|
+
# to tweak this, and the publishing task below too.
|
50
|
+
s.rubyforge_project = "vanilla"
|
51
|
+
end
|
52
|
+
|
53
|
+
# This task actually builds the gem. We also regenerate a static
|
54
|
+
# .gemspec file, which is useful if something (i.e. GitHub) will
|
55
|
+
# be automatically building a gem for this project. If you're not
|
56
|
+
# using GitHub, edit as appropriate.
|
57
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
58
|
+
pkg.gem_spec = spec
|
59
|
+
|
60
|
+
# Generate the gemspec file for github.
|
61
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
62
|
+
File.open(file, "w") {|f| f << spec.to_ruby }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Generate documentation
|
66
|
+
Rake::RDocTask.new do |rd|
|
67
|
+
rd.main = "README"
|
68
|
+
rd.rdoc_files.include("README", "lib/**/*.rb")
|
69
|
+
rd.rdoc_dir = "rdoc"
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'Clear out RDoc and generated packages'
|
73
|
+
task :clean => [:clobber_rdoc, :clobber_package] do
|
74
|
+
rm "#{spec.name}.gemspec"
|
75
|
+
end
|
76
|
+
|
77
|
+
# If you want to publish to RubyForge automatically, here's a simple
|
78
|
+
# task to help do that. If you don't, just get rid of this.
|
79
|
+
# Be sure to set up your Rubyforge account details with the Rubyforge
|
80
|
+
# gem; you'll need to run `rubyforge setup` and `rubyforge config` at
|
81
|
+
# the very least.
|
82
|
+
begin
|
83
|
+
require "rake/contrib/sshpublisher"
|
84
|
+
namespace :rubyforge do
|
85
|
+
|
86
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
87
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
88
|
+
|
89
|
+
namespace :release do
|
90
|
+
desc "Release a new version of this gem"
|
91
|
+
task :gem => [:package] do
|
92
|
+
require 'rubyforge'
|
93
|
+
rubyforge = RubyForge.new
|
94
|
+
rubyforge.configure
|
95
|
+
rubyforge.login
|
96
|
+
rubyforge.userconfig['release_notes'] = spec.summary
|
97
|
+
path_to_gem = File.join(File.dirname(__FILE__), "pkg", "#{spec.name}-#{spec.version}.gem")
|
98
|
+
puts "Publishing #{spec.name}-#{spec.version.to_s} to Rubyforge..."
|
99
|
+
rubyforge.add_release(spec.rubyforge_project, spec.name, spec.version.to_s, path_to_gem)
|
100
|
+
end
|
101
|
+
|
102
|
+
desc "Publish RDoc to RubyForge."
|
103
|
+
task :docs => [:rdoc] do
|
104
|
+
config = YAML.load(
|
105
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
106
|
+
)
|
107
|
+
|
108
|
+
host = "#{config['username']}@rubyforge.org"
|
109
|
+
remote_dir = "/var/www/gforge-projects/vanilla-rb/" # Should be the same as the rubyforge project name
|
110
|
+
local_dir = 'rdoc'
|
111
|
+
|
112
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
rescue LoadError
|
117
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
118
|
+
end
|
data/bin/vanilla
ADDED
data/config.example.yml
ADDED
data/config.ru
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'vanilla'
|
2
|
+
|
3
|
+
app = Vanilla::App.new(ENV['VANILLA_CONFIG'])
|
4
|
+
use Rack::Session::Cookie, :key => 'vanilla.session',
|
5
|
+
:path => '/',
|
6
|
+
:expire_after => 2592000,
|
7
|
+
:secret => app.config[:secret]
|
8
|
+
use Rack::Static, :urls => ["/public"], :root => File.join(File.dirname(__FILE__))
|
9
|
+
run app
|
data/lib/defensio.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
class Defensio
|
2
|
+
class << self
|
3
|
+
attr_accessor :format
|
4
|
+
attr_accessor :service_type
|
5
|
+
attr_accessor :api_version
|
6
|
+
attr_accessor :api_key
|
7
|
+
attr_accessor :owner_url
|
8
|
+
end
|
9
|
+
|
10
|
+
self.format = :xml
|
11
|
+
self.service_type = :app # Can be :blog
|
12
|
+
self.api_version = '1.2'
|
13
|
+
|
14
|
+
def self.configure(confhash)
|
15
|
+
if confhash['test']
|
16
|
+
@mock = true
|
17
|
+
self.owner_url = 'http://www.example.com'
|
18
|
+
return
|
19
|
+
else
|
20
|
+
confhash.each do |prop, val|
|
21
|
+
self.send("#{prop}=", val)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.method_missing(name, *args)
|
27
|
+
self.post(name.to_s.dasherize, *args)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def self.connection
|
32
|
+
uri = URI.parse('http://api.defensio.com/')
|
33
|
+
Net::HTTP.start(uri.host, uri.port)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.post(action, params = {})
|
37
|
+
resp = connection.post(real_path(action), params_from_hash(params))
|
38
|
+
raise "Problem with request: #{action}" unless resp.code == '200'
|
39
|
+
parse_response(resp.body)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.real_path(action)
|
43
|
+
"/#{service_type}/#{api_version}/#{action}/#{api_key}.#{format}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.params_from_hash(params = {})
|
47
|
+
# Thanks Net::HTTPHeader
|
48
|
+
params.stringify_keys.merge('owner-url' => owner_url).map {|k,v| "#{CGI.escape(k.dasherize.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.parse_response(body)
|
52
|
+
case format
|
53
|
+
when :yaml
|
54
|
+
YAML.load(body)
|
55
|
+
when :xml
|
56
|
+
Hash.from_xml(body)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..')))
|
2
|
+
require 'vanilla'
|
3
|
+
|
4
|
+
namespace :vanilla do
|
5
|
+
desc "Open an irb session preloaded with this library"
|
6
|
+
task :console do
|
7
|
+
sh "irb -Ilib -rubygems -rvanilla -rvanilla/console"
|
8
|
+
end
|
9
|
+
|
10
|
+
task :clean do
|
11
|
+
# TODO: get the database name from Soup
|
12
|
+
FileUtils.rm "soup.db" if File.exist?("soup.db")
|
13
|
+
end
|
14
|
+
|
15
|
+
task :prepare do
|
16
|
+
Soup.prepare
|
17
|
+
end
|
18
|
+
|
19
|
+
task :load_snips => :prepare do
|
20
|
+
Dynasnip.persist_all!(overwrite=true)
|
21
|
+
|
22
|
+
Dir[File.join(File.dirname(__FILE__), '..', 'vanilla', 'snips', '*.rb')].each do |f|
|
23
|
+
p "loading #{f}"
|
24
|
+
load f
|
25
|
+
end
|
26
|
+
|
27
|
+
load File.join(File.dirname(__FILE__), *%w[.. vanilla test_snips.rb])
|
28
|
+
|
29
|
+
puts "The soup is simmering. Loaded #{Soup.tuple_class.count} tuples"
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Resets the soup to contain the base snips only. Dangerous!'
|
33
|
+
task :reset => [:clean, :load_snips]
|
34
|
+
|
35
|
+
namespace :upgrade do
|
36
|
+
desc 'Upgrade the dynasnips'
|
37
|
+
task :dynasnips => :prepare do
|
38
|
+
Dynasnip.all.each do |dynasnip|
|
39
|
+
print "Upgrading #{dynasnip.snip_name}... "
|
40
|
+
# TODO: our confused Soup interface might return an array.
|
41
|
+
snip = Soup[dynasnip.snip_name]
|
42
|
+
if snip.empty? || snip.nil?
|
43
|
+
# it's a new dyna
|
44
|
+
Soup << dynasnip.snip_attributes
|
45
|
+
puts "(new)"
|
46
|
+
elsif snip.created_at == snip.updated_at
|
47
|
+
# it's not been changed, let's upgrade
|
48
|
+
snip.destroy
|
49
|
+
Soup << dynasnip.snip_attributes
|
50
|
+
puts "(unedited)"
|
51
|
+
else
|
52
|
+
# the dyna exists and has been changed
|
53
|
+
dynasnip.snip_attributes.each do |name, value|
|
54
|
+
unless (existing_value = snip.get_value(name)) == value
|
55
|
+
puts "Conflict in attribute '#{name}':"
|
56
|
+
puts "> Your soup: '#{existing_value}'"
|
57
|
+
puts "> New soup: '#{value}"
|
58
|
+
print "Upgrade? [Y/n]: "
|
59
|
+
upgrade_value = ["Y", "y", ""].include? STDIN.gets.chomp.strip
|
60
|
+
snip.set_value(name, value) if upgrade_value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
snip.save
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'Upgrade dynasnips and system snips'
|
70
|
+
task :upgrade => ["upgrade:dynasnips"]
|
71
|
+
|
72
|
+
desc 'Add a user (or change an existing password)'
|
73
|
+
task :add_user => :prepare do
|
74
|
+
puts "Adding a new user"
|
75
|
+
# config_file = ENV['VANILLA_CONFIG'] || 'config.yml'
|
76
|
+
# config_file = YAML.load(File.open(config_file)) rescue {}
|
77
|
+
app = Vanilla::App.new(ENV['VANILLA_CONFIG'])
|
78
|
+
print "Username: "
|
79
|
+
username = STDIN.gets.chomp.strip
|
80
|
+
print "Password: "
|
81
|
+
password = STDIN.gets.chomp.strip
|
82
|
+
print "Confirm password: "
|
83
|
+
confirm_password = STDIN.gets.chomp.strip
|
84
|
+
if password != confirm_password
|
85
|
+
raise "Passwords don't match!"
|
86
|
+
else
|
87
|
+
app.config[:credentials] ||= {}
|
88
|
+
app.config[:credentials][username] = MD5.md5(password).to_s
|
89
|
+
app.config.save!
|
90
|
+
puts "User '#{username}' added."
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Generate file containing secret for cookie-based session storage'
|
95
|
+
task :generate_secret do
|
96
|
+
# Adapted from old rails secret generator.
|
97
|
+
require 'openssl'
|
98
|
+
if !File.exist?("/dev/urandom")
|
99
|
+
# OpenSSL transparently seeds the random number generator with
|
100
|
+
# data from /dev/urandom. On platforms where that is not
|
101
|
+
# available, such as Windows, we have to provide OpenSSL with
|
102
|
+
# our own seed. Unfortunately there's no way to provide a
|
103
|
+
# secure seed without OS support, so we'll have to do with
|
104
|
+
# rand() and Time.now.usec().
|
105
|
+
OpenSSL::Random.seed(rand(0).to_s + Time.now.usec.to_s)
|
106
|
+
end
|
107
|
+
data = OpenSSL::BN.rand(2048, -1, false).to_s
|
108
|
+
secret = OpenSSL::Digest::SHA512.new(data).hexdigest
|
109
|
+
app = Vanilla::App.new(ENV['VANILLA_CONFIG'])
|
110
|
+
app.config[:secret] = secret
|
111
|
+
app.config.save!
|
112
|
+
puts "Secret generated."
|
113
|
+
end
|
114
|
+
|
115
|
+
desc 'Prepare standard files to run Vanilla'
|
116
|
+
task :prepare_files do
|
117
|
+
cp File.join(File.dirname(__FILE__), *%w[.. .. config.ru]), 'config.ru'
|
118
|
+
cp_r File.join(File.dirname(__FILE__), *%w[.. .. public]), 'public'
|
119
|
+
mkdir 'tmp'
|
120
|
+
File.open("Rakefile", "w") do |f|
|
121
|
+
rakefile =<<-EOF
|
122
|
+
require 'vanilla'
|
123
|
+
load 'tasks/vanilla.rake'
|
124
|
+
|
125
|
+
# Add any other tasks here.
|
126
|
+
EOF
|
127
|
+
f.write rakefile.strip
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
desc 'Prepare a new vanilla.rb installation'
|
133
|
+
task :setup do
|
134
|
+
puts <<-EOM
|
135
|
+
|
136
|
+
===================~ Vanilla.rb ~====================
|
137
|
+
|
138
|
+
Congratulations! You have elected to try out the weirdest web thing ever.
|
139
|
+
Lets get started. Firstly, I'm going to cook you some soup:
|
140
|
+
|
141
|
+
|
142
|
+
EOM
|
143
|
+
Rake::Task['vanilla:load_snips'].invoke
|
144
|
+
|
145
|
+
puts <<-EOM
|
146
|
+
|
147
|
+
Now I'm going to generate your configuration. This will be stored either in
|
148
|
+
'config.yml' in the current directory, or in the path you provide via the
|
149
|
+
environment variable VANILLA_CONFIG.
|
150
|
+
|
151
|
+
Generating the secret for cookie-based session storage.
|
152
|
+
EOM
|
153
|
+
Rake::Task['vanilla:prepare_files'].invoke
|
154
|
+
Rake::Task['vanilla:generate_secret'].invoke
|
155
|
+
|
156
|
+
puts <<-EOM
|
157
|
+
|
158
|
+
|
159
|
+
Now that we've got our broth, you'll want to add a user, so you can edit stuff.
|
160
|
+
Lets do that now:
|
161
|
+
|
162
|
+
|
163
|
+
EOM
|
164
|
+
Rake::Task['vanilla:add_user'].invoke
|
165
|
+
puts <<-EOM
|
166
|
+
|
167
|
+
|
168
|
+
OK! You're ready to go. To start vanilla.rb, you'll want to get it running under
|
169
|
+
a webserver that supports Rack. The easiest way to do this locally is via 'rackup':
|
170
|
+
|
171
|
+
$ rackup
|
172
|
+
|
173
|
+
Then go to http://localhost:9292
|
174
|
+
|
175
|
+
I'm going now, Goodbye!
|
176
|
+
EOM
|
177
|
+
end
|
178
|
+
end
|
data/lib/vanilla/app.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'vanilla'
|
2
|
+
require 'vanilla/request'
|
3
|
+
|
4
|
+
module Vanilla
|
5
|
+
class App
|
6
|
+
|
7
|
+
attr_reader :request, :response, :config
|
8
|
+
|
9
|
+
def initialize(config_file=nil)
|
10
|
+
prepare_configuration(config_file)
|
11
|
+
Soup.prepare
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns a Rack-appropriate 3-element array (via Rack::Response#finish)
|
15
|
+
def call(env)
|
16
|
+
@request = Vanilla::Request.new(env)
|
17
|
+
@response = Rack::Response.new
|
18
|
+
|
19
|
+
begin
|
20
|
+
output = formatted_render(request.snip, request.part, request.format)
|
21
|
+
rescue => e
|
22
|
+
@response.status = 500
|
23
|
+
output = e.to_s
|
24
|
+
end
|
25
|
+
response_format = request.format
|
26
|
+
response_format = 'plain' if response_format == 'raw'
|
27
|
+
@response['Content-Type'] = "text/#{response_format}"
|
28
|
+
@response.write(output)
|
29
|
+
@response.finish # returns the array
|
30
|
+
end
|
31
|
+
|
32
|
+
def formatted_render(snip, part=nil, format=nil)
|
33
|
+
case format
|
34
|
+
when 'html', nil
|
35
|
+
Renderers::Erb.new(self).render(Vanilla.snip('system'), :main_template)
|
36
|
+
when 'raw', 'css', 'js'
|
37
|
+
Renderers::Raw.new(self).render(snip, part || :content)
|
38
|
+
when 'text', 'atom', 'xml'
|
39
|
+
render(snip, part || :content)
|
40
|
+
else
|
41
|
+
raise "Unknown format '#{format}'"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# render a snip using either the renderer given, or the renderer
|
46
|
+
# specified by the snip's "render_as" property, or Render::Base
|
47
|
+
# if nothing else is given.
|
48
|
+
def render(snip, part=:content, args=[])
|
49
|
+
rendering(snip) do |renderer|
|
50
|
+
renderer.render(snip, part, args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Given the snip and parameters, yield an instance of the appropriate
|
55
|
+
# Vanilla::Render::Base subclass
|
56
|
+
def rendering(snip)
|
57
|
+
renderer_instance = renderer_for(snip).new(self)
|
58
|
+
yield renderer_instance
|
59
|
+
rescue Exception => e
|
60
|
+
"<pre>[Error rendering '#{snip.name}' - \"" +
|
61
|
+
e.message.gsub("<", "<").gsub(">", ">") + "\"]\n" +
|
62
|
+
e.backtrace.join("\n").gsub("<", "<").gsub(">", ">") + "</pre>"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the renderer class for a given snip
|
66
|
+
def renderer_for(snip)
|
67
|
+
return Renderers::Base unless snip.render_as && !snip.render_as.empty?
|
68
|
+
Vanilla::Renderers.const_get(snip.render_as)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Other things can call this when a snip cannot be loaded.
|
72
|
+
def render_missing_snip(snip_name)
|
73
|
+
"[snip '#{snip_name}' cannot be found]"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def prepare_configuration(config_file)
|
79
|
+
config_file ||= "config.yml"
|
80
|
+
@config = YAML.load(File.open(config_file)) rescue {}
|
81
|
+
@config[:filename] = config_file
|
82
|
+
def @config.save!
|
83
|
+
File.open(self[:filename], 'w') { |f| f.puts self.to_yaml }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'vanilla/renderers/base'
|
2
|
+
require 'enumerator'
|
3
|
+
|
4
|
+
class Dynasnip < Vanilla::Renderers::Base
|
5
|
+
|
6
|
+
def self.all
|
7
|
+
ObjectSpace.enum_for(:each_object, class << self; self; end).to_a - [self]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.snip_name(new_name=nil)
|
11
|
+
if new_name
|
12
|
+
@snip_name = new_name.to_s
|
13
|
+
else
|
14
|
+
# borrowed from ActiveSupport
|
15
|
+
@snip_name ||= self.name.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
16
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
17
|
+
tr("-", "_").
|
18
|
+
downcase
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.attribute(attribute_name, attribute_value=nil)
|
23
|
+
@attributes ||= {}
|
24
|
+
@attributes[attribute_name.to_sym] = attribute_value if attribute_value
|
25
|
+
@attributes[attribute_name.to_sym]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.usage(str)
|
29
|
+
attribute :usage, escape_curly_braces(str).strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.persist_all!(overwrite=false)
|
33
|
+
all.each do |dynasnip|
|
34
|
+
dynasnip.persist!(overwrite)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.build_snip
|
39
|
+
Snip.new(snip_attributes)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.snip_attributes
|
43
|
+
full_snip_attributes = {:name => snip_name, :content => self.name, :render_as => "Ruby"}
|
44
|
+
@attributes ? full_snip_attributes.merge!(@attributes) : full_snip_attributes
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.persist!(overwrite=false)
|
48
|
+
if overwrite
|
49
|
+
snip = Soup[snip_name]
|
50
|
+
if snip
|
51
|
+
if snip.is_a?(Array)
|
52
|
+
snip.each { |s| s.destroy }
|
53
|
+
else
|
54
|
+
snip.destroy
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
snip = Soup[snip_name]
|
59
|
+
snip = snip[0] if snip.is_a?(Array)
|
60
|
+
if snip
|
61
|
+
snip_attributes.each do |name, value|
|
62
|
+
snip.set_value(name, value)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
snip = build_snip
|
66
|
+
end
|
67
|
+
snip.save
|
68
|
+
snip
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_missing(method, *args)
|
72
|
+
if snip = Vanilla.snip(snip_name)
|
73
|
+
snip.get_value(method)
|
74
|
+
elsif part = self.class.attribute(method)
|
75
|
+
part
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# dynasnips gain access to the app in the same way as Render::Base
|
82
|
+
# subclasses
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def snip_name
|
87
|
+
self.class.snip_name
|
88
|
+
end
|
89
|
+
|
90
|
+
def snip
|
91
|
+
Snip[snip_name]
|
92
|
+
end
|
93
|
+
|
94
|
+
def show_usage
|
95
|
+
if snip.usage
|
96
|
+
Vanilla::Renderers::Markdown.render(snip_name, :usage)
|
97
|
+
else
|
98
|
+
"No usage information for #{snip_name}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def cleaned_params
|
103
|
+
p = app.request.params.dup
|
104
|
+
p.delete(:snip)
|
105
|
+
p.delete(:format)
|
106
|
+
p.delete(:method)
|
107
|
+
p.delete(:part)
|
108
|
+
p
|
109
|
+
end
|
110
|
+
end
|