macournoyer-invisible 0.0.1
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 +63 -0
- data/Rakefile +64 -0
- data/app_generators/flat/flat_generator.rb +40 -0
- data/app_generators/flat/templates/README +10 -0
- data/app_generators/flat/templates/app.rb +16 -0
- data/app_generators/flat/templates/rack.ru +17 -0
- data/app_generators/full/full_generator.rb +80 -0
- data/app_generators/full/templates/README +10 -0
- data/app_generators/full/templates/Rakefile +16 -0
- data/app_generators/full/templates/app.rb +7 -0
- data/app_generators/full/templates/config/boot.rb +5 -0
- data/app_generators/full/templates/config/env.rb +18 -0
- data/app_generators/full/templates/config/env/development.rb +6 -0
- data/app_generators/full/templates/config/env/production.rb +7 -0
- data/app_generators/full/templates/config/env/test.rb +1 -0
- data/app_generators/full/templates/config/rack.ru +6 -0
- data/app_generators/full/templates/gitignore +3 -0
- data/app_generators/full/templates/public/stylesheets/ie.css +16 -0
- data/app_generators/full/templates/public/stylesheets/print.css +21 -0
- data/app_generators/full/templates/public/stylesheets/screen.css +242 -0
- data/app_generators/full/templates/spec/app_spec.rb +7 -0
- data/app_generators/full/templates/spec/spec_helper.rb +14 -0
- data/app_generators/full/templates/test/app_test.rb +7 -0
- data/app_generators/full/templates/test/test_helper.rb +15 -0
- data/app_generators/full/templates/views/layout.erb +18 -0
- data/bin/invisible +18 -0
- data/invisible.gemspec +119 -0
- data/lib/invisible.rb +212 -0
- data/lib/invisible/core_ext.rb +12 -0
- data/lib/invisible/erb.rb +16 -0
- data/lib/invisible/erubis.rb +14 -0
- data/lib/invisible/haml.rb +14 -0
- data/lib/invisible/helpers.rb +5 -0
- data/lib/invisible/mock.rb +20 -0
- data/lib/invisible/reloader.rb +22 -0
- metadata +119 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
ENV["RACK_ENV"] = "test"
|
2
|
+
require File.dirname(__FILE__) + "/../config/boot"
|
3
|
+
require "invisible/mock"
|
4
|
+
require "spec"
|
5
|
+
|
6
|
+
Spec::Runner.configure do |config|
|
7
|
+
config.prepend_before do
|
8
|
+
@app = Invisible.new do
|
9
|
+
root File.dirname(__FILE__) + "/.."
|
10
|
+
load "config/env"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
config.include Invisible::MockMethods
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
ENV["RACK_ENV"] = "test"
|
2
|
+
require File.dirname(__FILE__) + "/../config/boot"
|
3
|
+
require "invisible/mock"
|
4
|
+
require "test/unit"
|
5
|
+
|
6
|
+
class Test::Unit::TestCase
|
7
|
+
include Invisible::MockMethods
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@app = Invisible.new do
|
11
|
+
root File.dirname(__FILE__) + "/.."
|
12
|
+
load "config/env"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
5
|
+
<title>Invisible</title>
|
6
|
+
<link rel="stylesheet" href="/stylesheets/screen.css" type="text/css" media="screen, projection" />
|
7
|
+
<link rel="stylesheet" href="/stylesheets/print.css" type="text/css" media="print" />
|
8
|
+
<!--[if IE]>
|
9
|
+
<link rel="stylesheet" href="/stylesheets/ie.css" type="text/css" media="screen, projection" />
|
10
|
+
<![endif]-->
|
11
|
+
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js" />
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<div id="content" class="container">
|
15
|
+
<%= @content %>
|
16
|
+
</div>
|
17
|
+
</body>
|
18
|
+
</html>
|
data/bin/invisible
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubigen'
|
5
|
+
|
6
|
+
require 'rubigen/scripts/generate'
|
7
|
+
source = RubiGen::PathSource.new(:application,
|
8
|
+
File.join(File.dirname(__FILE__), "../app_generators"))
|
9
|
+
RubiGen::Base.reset_sources
|
10
|
+
RubiGen::Base.append_sources source
|
11
|
+
|
12
|
+
if ARGV.delete("--flat")
|
13
|
+
generator = "flat"
|
14
|
+
else
|
15
|
+
generator = "full"
|
16
|
+
end
|
17
|
+
|
18
|
+
RubiGen::Scripts::Generate.new.run(ARGV, :generator => generator)
|
data/invisible.gemspec
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: invisible
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marc-Andre Cournoyer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-11-12 00:00:00 -05:00
|
13
|
+
default_executable: invisible
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rack
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.4.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: markaby
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0.5"
|
34
|
+
version:
|
35
|
+
description: Invisible is like a giant robot combining the awesomeness of Rails, Merb, Camping and Sinatra. Except, it's tiny (100 sloc).
|
36
|
+
email: macournoyer@gmail.com
|
37
|
+
executables:
|
38
|
+
- invisible
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
files:
|
44
|
+
- README
|
45
|
+
- Rakefile
|
46
|
+
- invisible.gemspec
|
47
|
+
- bin/invisible
|
48
|
+
- lib/invisible
|
49
|
+
- lib/invisible/core_ext.rb
|
50
|
+
- lib/invisible/erb.rb
|
51
|
+
- lib/invisible/erubis.rb
|
52
|
+
- lib/invisible/haml.rb
|
53
|
+
- lib/invisible/helpers.rb
|
54
|
+
- lib/invisible/mock.rb
|
55
|
+
- lib/invisible/reloader.rb
|
56
|
+
- lib/invisible.rb
|
57
|
+
- app_generators/flat
|
58
|
+
- app_generators/flat/flat_generator.rb
|
59
|
+
- app_generators/flat/templates
|
60
|
+
- app_generators/flat/templates/app.rb
|
61
|
+
- app_generators/flat/templates/rack.ru
|
62
|
+
- app_generators/flat/templates/README
|
63
|
+
- app_generators/full
|
64
|
+
- app_generators/full/full_generator.rb
|
65
|
+
- app_generators/full/templates
|
66
|
+
- app_generators/full/templates/app.rb
|
67
|
+
- app_generators/full/templates/config
|
68
|
+
- app_generators/full/templates/config/boot.rb
|
69
|
+
- app_generators/full/templates/config/env
|
70
|
+
- app_generators/full/templates/config/env/development.rb
|
71
|
+
- app_generators/full/templates/config/env/production.rb
|
72
|
+
- app_generators/full/templates/config/env/test.rb
|
73
|
+
- app_generators/full/templates/config/env.rb
|
74
|
+
- app_generators/full/templates/config/rack.ru
|
75
|
+
- app_generators/full/templates/gitignore
|
76
|
+
- app_generators/full/templates/public
|
77
|
+
- app_generators/full/templates/public/stylesheets
|
78
|
+
- app_generators/full/templates/public/stylesheets/ie.css
|
79
|
+
- app_generators/full/templates/public/stylesheets/print.css
|
80
|
+
- app_generators/full/templates/public/stylesheets/screen.css
|
81
|
+
- app_generators/full/templates/Rakefile
|
82
|
+
- app_generators/full/templates/README
|
83
|
+
- app_generators/full/templates/spec
|
84
|
+
- app_generators/full/templates/spec/app_spec.rb
|
85
|
+
- app_generators/full/templates/spec/spec_helper.rb
|
86
|
+
- app_generators/full/templates/test
|
87
|
+
- app_generators/full/templates/test/app_test.rb
|
88
|
+
- app_generators/full/templates/test/test_helper.rb
|
89
|
+
- app_generators/full/templates/views
|
90
|
+
- app_generators/full/templates/views/layout.erb
|
91
|
+
has_rdoc: true
|
92
|
+
homepage: http://github.com/macournoyer/invisible
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options:
|
95
|
+
- --main
|
96
|
+
- README
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
version:
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.2.0
|
115
|
+
signing_key:
|
116
|
+
specification_version: 2
|
117
|
+
summary: The Invisible Web Framework
|
118
|
+
test_files: []
|
119
|
+
|
data/lib/invisible.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
%w(rubygems time rack markaby invisible/core_ext invisible/helpers).each { |f| require f }
|
2
|
+
# = The Invisible Framework
|
3
|
+
# Invisible is like a giant robot combining the awesomeness of Rails,
|
4
|
+
# Merb, Camping and Sinatra. Except, it's tiny (100 sloc).
|
5
|
+
#
|
6
|
+
# == Build an app in an object
|
7
|
+
# Invisible supports multiple applications running in the same VM. Each instance
|
8
|
+
# of this class represents a runnable application.
|
9
|
+
#
|
10
|
+
# app = Invisible.new do
|
11
|
+
# get "/" do
|
12
|
+
# render "ohaie"
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# You can then run it as a Rack application.
|
17
|
+
#
|
18
|
+
# run app
|
19
|
+
#
|
20
|
+
class Invisible
|
21
|
+
HTTP_METHODS = [:get, :post, :head, :put, :delete]
|
22
|
+
attr_reader :actions, :request, :response, :params
|
23
|
+
|
24
|
+
# Creates a new Invisible Rack application. You can build your app
|
25
|
+
# in the yielded block or using the app instance.
|
26
|
+
def initialize(&block)
|
27
|
+
@actions, @with, @loaded, @layouts, @views, @helpers, @app = [], [], [], {}, {}, self, method(:_call)
|
28
|
+
@root = File.dirname(eval("__FILE__", block.binding))
|
29
|
+
instance_eval(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set the root of the app.
|
33
|
+
# Defaults to the file the Invisible class is instancianted in.
|
34
|
+
def root(value=@root)
|
35
|
+
@root = value
|
36
|
+
end
|
37
|
+
|
38
|
+
# Register an action for a specified +route+.
|
39
|
+
#
|
40
|
+
# get "/" do
|
41
|
+
# # ...
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
def action(method, route, &block)
|
45
|
+
@actions << [method.to_s, build_route(@with * "/" + route), block]
|
46
|
+
end
|
47
|
+
HTTP_METHODS.each { |m| class_eval "def #{m}(r='/',&b); action('#{m}', r, &b) end" }
|
48
|
+
|
49
|
+
# Wrap actions sharing a common base route.
|
50
|
+
#
|
51
|
+
# with "/lol" do
|
52
|
+
# get "/cat" # ...
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# Will register an action on GET /lol/cat.
|
56
|
+
# You can nested as many level as you want.
|
57
|
+
def with(route)
|
58
|
+
@with.push(route)
|
59
|
+
yield
|
60
|
+
@with.pop
|
61
|
+
end
|
62
|
+
|
63
|
+
# Render the response inside an action.
|
64
|
+
# Render markaby by passing a block:
|
65
|
+
#
|
66
|
+
# render do
|
67
|
+
# h1 "Poop"
|
68
|
+
# p "Smells!"
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# or simple text as the first argument:
|
72
|
+
#
|
73
|
+
# render "crap"
|
74
|
+
#
|
75
|
+
# or render a markaby view, created using the +view+ method:
|
76
|
+
#
|
77
|
+
# view :lolcat do
|
78
|
+
# p "i can has?"
|
79
|
+
# end
|
80
|
+
# render :lolcat
|
81
|
+
#
|
82
|
+
# You can also pass some options or headers:
|
83
|
+
#
|
84
|
+
# render "heck", :status => 201, :layout => :none, 'X-Crap-Level' => 'ubersome'
|
85
|
+
#
|
86
|
+
# Supported options are:
|
87
|
+
# status: Status code of the response.
|
88
|
+
# layout: Name of the layout to use.
|
89
|
+
# All other options will be sent as a response header.
|
90
|
+
def render(*args, &block)
|
91
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
92
|
+
|
93
|
+
# Extract options
|
94
|
+
layout = @layouts[options.delete(:layout) || :default]
|
95
|
+
@response.status = options.delete(:status) || 200
|
96
|
+
|
97
|
+
# Render inside the layout
|
98
|
+
@content = args.last.is_a?(String) ? args.last : mab(&(block || @views[args.last]))
|
99
|
+
@content = mab(&layout).to_s if layout
|
100
|
+
|
101
|
+
# Set headers
|
102
|
+
@response.headers.merge!(options)
|
103
|
+
@response.headers["Content-Length"] ||= @content.size.to_s
|
104
|
+
@response.headers["Last-Modified"] ||= Time.now.httpdate
|
105
|
+
@response.body = @content
|
106
|
+
end
|
107
|
+
|
108
|
+
# Redirect to a path.
|
109
|
+
#
|
110
|
+
# redirect_to "/login"
|
111
|
+
#
|
112
|
+
# To do a permanent redirect, specify the status code as the last argument.
|
113
|
+
#
|
114
|
+
# redirect_to "/", 301
|
115
|
+
#
|
116
|
+
def redirect_to(path, status=302)
|
117
|
+
render(:status => status, "Location" => path) { p { text("You are redirected to "); a(path, :href => path) } }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Register a layout to be used around +render+ed text.
|
121
|
+
# Use markaby inside your block.
|
122
|
+
def layout(name=:default, &block)
|
123
|
+
@layouts[name] = block
|
124
|
+
end
|
125
|
+
|
126
|
+
# Register a named view to be used from <tt>render :name</tt>.
|
127
|
+
# Use markaby inside your block.
|
128
|
+
def view(name, &block)
|
129
|
+
@views[name] = block
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return HTML rendered by evaluating the block using Markaby.
|
133
|
+
def mab(&block)
|
134
|
+
Markaby::Builder.new({}, self, &block).to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
# Return the current session.
|
138
|
+
# Add `use Rack::Session::Cookie` to use.
|
139
|
+
def session
|
140
|
+
@request.env["rack.session"]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Register a Rack middleware wrapping the
|
144
|
+
# current application.
|
145
|
+
def use(middleware, *args)
|
146
|
+
@app = middleware.new(@app, *args)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Called by the Rack handler to process a request.
|
150
|
+
def call(env)
|
151
|
+
@app.call(env)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Load an external file inside the context of Invisible,
|
155
|
+
# which means you can use the get,post,with methods.
|
156
|
+
# The files loaded with this method will be reloaded in
|
157
|
+
# You're using the Invisible Reloader middleware.
|
158
|
+
# Options:
|
159
|
+
# reload: false to not reload the file when Reloader middleware is in use.
|
160
|
+
# (default: true)
|
161
|
+
def load(file, options={})
|
162
|
+
return if @loaded.include?(file)
|
163
|
+
@loaded << file unless FalseClass === options[:reload]
|
164
|
+
path = File.join(@root, file) + ".rb"
|
165
|
+
eval(File.read(path), binding, path)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Shortcut to Rack builder +run+ method.
|
169
|
+
# Equivalent to:
|
170
|
+
#
|
171
|
+
# app = Invisible.new do
|
172
|
+
# ...
|
173
|
+
# end
|
174
|
+
# run app
|
175
|
+
#
|
176
|
+
def self.run(*args, &block)
|
177
|
+
eval("self", block.binding).run new(&block)
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
def _call(env)
|
182
|
+
@request = Rack::Request.new(env)
|
183
|
+
@response = Rack::Response.new
|
184
|
+
@params = @request.params.symbolize_keys
|
185
|
+
if action = recognize(@request.path_info, @request.POST["_method"] || @request.request_method)
|
186
|
+
@params.merge!(@path_params)
|
187
|
+
instance_eval(&action.last)
|
188
|
+
else
|
189
|
+
@response.status = 404
|
190
|
+
@response.body = "Not found"
|
191
|
+
end
|
192
|
+
@response.finish
|
193
|
+
end
|
194
|
+
|
195
|
+
def build_route(route)
|
196
|
+
pattern = '\/*' + route.gsub("*", '.*').gsub("/", '\/*').gsub(/:\w+/, '(\w+)') + '\/*'
|
197
|
+
[/^#{pattern}$/i, route.scan(/\:(\w+)/).flatten]
|
198
|
+
end
|
199
|
+
|
200
|
+
def recognize(url, method)
|
201
|
+
method = method.to_s.downcase
|
202
|
+
@actions.detect do |m, (pattern, keys), _|
|
203
|
+
method == m && @path_params = match_route(pattern, keys, url)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def match_route(pattern, keys, url)
|
208
|
+
matches, params = (url.match(pattern) || return)[1..-1], {}
|
209
|
+
keys.each_with_index { |key, i| params[key] = matches[i] }
|
210
|
+
params.symbolize_keys
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Mostly taken from ActiveSupport
|
2
|
+
unless defined?(ActiveSupport)
|
3
|
+
class Hash
|
4
|
+
# Return a new hash with all keys converted to symbols.
|
5
|
+
def symbolize_keys
|
6
|
+
inject({}) do |options, (key, value)|
|
7
|
+
options[(key.to_sym rescue key) || key] = value
|
8
|
+
options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
class Invisible
|
4
|
+
include ERB::Util
|
5
|
+
|
6
|
+
# Evaluate the ERB template in +file+ and returns the
|
7
|
+
# result as a string.
|
8
|
+
# Use with +render+:
|
9
|
+
#
|
10
|
+
# render erb(:muffin)
|
11
|
+
#
|
12
|
+
def erb(file)
|
13
|
+
path = File.join(@root, "views", file.to_s)
|
14
|
+
ERB.new(File.read("#{path}.erb")).result(binding)
|
15
|
+
end
|
16
|
+
end
|