freddie 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/.gitignore +17 -0
- data/.yardopt +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +40 -0
- data/Rakefile +9 -0
- data/freddie.gemspec +24 -0
- data/lib/freddie/application.rb +13 -0
- data/lib/freddie/context.rb +21 -0
- data/lib/freddie/handlers.rb +223 -0
- data/lib/freddie/version.rb +3 -0
- data/lib/freddie.rb +20 -0
- data/spec/handlers_spec.rb +14 -0
- data/spec/integration_spec.rb +55 -0
- data/spec/spec_helper.rb +9 -0
- metadata +119 -0
data/.gitignore
ADDED
data/.yardopt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --protected lib/**/*.rb - README LEGAL COPYING
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Hendrik Mans
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Freddie
|
2
|
+
|
3
|
+
Yet another web application framework for Ruby. :)
|
4
|
+
|
5
|
+
## Example
|
6
|
+
|
7
|
+
```
|
8
|
+
run Freddie do
|
9
|
+
layout 'application.html.haml'
|
10
|
+
|
11
|
+
# Serve some asset files
|
12
|
+
#
|
13
|
+
path('application-:timestamp.css') { stylesheet 'application.scss' }
|
14
|
+
path('application-:timestamp.js') { javascript 'application.js' }
|
15
|
+
|
16
|
+
# Built-in OmniAuth support
|
17
|
+
#
|
18
|
+
omni_auth
|
19
|
+
|
20
|
+
# Provide a /logout action
|
21
|
+
#
|
22
|
+
path('logout') do
|
23
|
+
session['omniauth_user'] = nil
|
24
|
+
redirect_to '/'
|
25
|
+
end
|
26
|
+
|
27
|
+
# Mount a RESTful resource. This adds all the required
|
28
|
+
# paths and even applies authorization scopes for the
|
29
|
+
# currently logged in user.
|
30
|
+
#
|
31
|
+
resource :events do
|
32
|
+
can :index, :show
|
33
|
+
can :new, :create, :update, :destroy if current_user.admin?
|
34
|
+
end
|
35
|
+
|
36
|
+
# The root action is a simple redirect.
|
37
|
+
#
|
38
|
+
redirect_to '/events'
|
39
|
+
end
|
40
|
+
```
|
data/Rakefile
ADDED
data/freddie.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/freddie/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Hendrik Mans"]
|
6
|
+
gem.email = ["hendrik@mans.de"]
|
7
|
+
gem.description = %q{Expressive, Rack-loving web application DSL.}
|
8
|
+
gem.summary = %q{Expressive, Rack-loving web application DSL.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "freddie"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Freddie::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'rack', '~> 1.4.1'
|
19
|
+
gem.add_dependency 'activesupport', '~> 3.2.3'
|
20
|
+
gem.add_dependency 'tilt', '~> 1.3.3'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rack-test'
|
23
|
+
gem.add_development_dependency 'minitest-reporters'
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Freddie
|
2
|
+
class Context
|
3
|
+
attr_accessor :request, :response, :params, :path
|
4
|
+
|
5
|
+
def initialize(env)
|
6
|
+
@request = Rack::Request.new(env)
|
7
|
+
@response = Rack::Response.new
|
8
|
+
@params = @request.params.with_indifferent_access
|
9
|
+
@path = @request.path.gsub(/^\//, '').split('/')
|
10
|
+
@finished = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def current_user
|
14
|
+
session['omniauth_user'].try(:last)
|
15
|
+
end
|
16
|
+
|
17
|
+
def session
|
18
|
+
request.session
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module Freddie
|
2
|
+
module Handlers
|
3
|
+
|
4
|
+
# Base class for all handlers.
|
5
|
+
#
|
6
|
+
class Base
|
7
|
+
attr_reader :block, :env
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def call(env)
|
11
|
+
new.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(*args, &block)
|
16
|
+
@args = args
|
17
|
+
@block = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
@env = env
|
22
|
+
before_perform
|
23
|
+
perform(*@args)
|
24
|
+
after_perform
|
25
|
+
response
|
26
|
+
end
|
27
|
+
|
28
|
+
def before_perform ; end
|
29
|
+
def perform(*args) ; end
|
30
|
+
def after_perform ; end
|
31
|
+
|
32
|
+
def context
|
33
|
+
env['freddie.context'] ||= Context.new(env)
|
34
|
+
end
|
35
|
+
|
36
|
+
def response
|
37
|
+
context.response
|
38
|
+
end
|
39
|
+
|
40
|
+
def request
|
41
|
+
context.request
|
42
|
+
end
|
43
|
+
|
44
|
+
def params
|
45
|
+
context.params
|
46
|
+
end
|
47
|
+
|
48
|
+
def session
|
49
|
+
context.request.session
|
50
|
+
end
|
51
|
+
|
52
|
+
def layout(name)
|
53
|
+
env['freddie.layout'] = name
|
54
|
+
end
|
55
|
+
|
56
|
+
def finish!
|
57
|
+
env['freddie.finished'] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
def finished?
|
61
|
+
env['freddie.finished']
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing(name, *args, &block)
|
65
|
+
klass_name = "Freddie::Handlers::#{name.to_s.classify}"
|
66
|
+
|
67
|
+
unless finished?
|
68
|
+
klass = klass_name.constantize or raise MissingHandlerError, "Could not locate #{klass_name}"
|
69
|
+
klass.new(*args, &block).tap do |handler|
|
70
|
+
handler.call(env)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module Endpoint
|
77
|
+
def before_perform
|
78
|
+
raise NotFoundError if context.path.any?
|
79
|
+
end
|
80
|
+
|
81
|
+
def after_perform
|
82
|
+
finish!
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Path < Base
|
87
|
+
def perform(path)
|
88
|
+
if match_data = self.class.path_to_regexp(path).match(context.path.first)
|
89
|
+
# write parameter matches into params
|
90
|
+
match_data.names.each { |k| params[k] = match_data[k] }
|
91
|
+
|
92
|
+
# execute block
|
93
|
+
el = context.path.shift
|
94
|
+
|
95
|
+
if block
|
96
|
+
# cater for different DSL styles
|
97
|
+
if block.arity == 1
|
98
|
+
block.call(self)
|
99
|
+
else
|
100
|
+
instance_exec &block
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context.path.unshift(el)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class << self
|
109
|
+
def path_to_regexp(path)
|
110
|
+
path = ":#{path}" if path.is_a?(Symbol)
|
111
|
+
Regexp.compile('^'+path.gsub(/\)/, ')?').gsub(/\//, '\/').gsub(/\./, '\.').gsub(/:(\w+)/, '(?<\\1>\w+)')+'$')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Render < Base
|
117
|
+
include Endpoint
|
118
|
+
|
119
|
+
def perform(data)
|
120
|
+
response.body << data
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Template < Base
|
125
|
+
include Endpoint
|
126
|
+
|
127
|
+
def perform(name, variables = {})
|
128
|
+
html = Tilt.new("views/%s" % name).render(self, variables)
|
129
|
+
if env['freddie.layout'].present?
|
130
|
+
html = Tilt.new("views/#{env['freddie.layout']}").render(self) { html }
|
131
|
+
end
|
132
|
+
response.body << html
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Resource < Base
|
137
|
+
def perform(name, options = {})
|
138
|
+
options = {
|
139
|
+
name: name.to_s,
|
140
|
+
singular_name: name.to_s.singularize,
|
141
|
+
plural_name: name.to_s.pluralize,
|
142
|
+
klass: name.to_s.classify.constantize
|
143
|
+
}.merge(options)
|
144
|
+
|
145
|
+
path(options[:name]) do |n|
|
146
|
+
n.path(:id) do |n|
|
147
|
+
n.template "#{options[:plural_name]}/show.html.haml",
|
148
|
+
options[:singular_name] => options[:klass].find(params[:id])
|
149
|
+
end
|
150
|
+
|
151
|
+
n.template "#{options[:plural_name]}/index.html.haml",
|
152
|
+
options[:plural_name] => options[:klass].all
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class RedirectTo < Base
|
158
|
+
include Endpoint
|
159
|
+
|
160
|
+
def perform(destination)
|
161
|
+
response.headers['Location'] = destination
|
162
|
+
response.status = 302
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class Run < Base
|
167
|
+
include Endpoint
|
168
|
+
|
169
|
+
def perform(app)
|
170
|
+
context.response = Rack::Response.new(app.call(env))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Stylesheet < Base
|
175
|
+
include Endpoint
|
176
|
+
|
177
|
+
def perform(name)
|
178
|
+
response.headers['Content-type'] = 'text/css'
|
179
|
+
layout nil
|
180
|
+
template name
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class SendFile < Base
|
185
|
+
include Endpoint
|
186
|
+
|
187
|
+
def perform(name)
|
188
|
+
response.body << File.read(name)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Javascript < Base
|
193
|
+
include Endpoint
|
194
|
+
|
195
|
+
def perform(name)
|
196
|
+
response.headers['Content-type'] = 'text/javascript'
|
197
|
+
|
198
|
+
# insert crazy javascript packing code here... :)
|
199
|
+
response.body << File.read("views/#{name}")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class OmniAuth < Base
|
204
|
+
def perform(options = {})
|
205
|
+
options = {
|
206
|
+
session_key: :omniauth_user,
|
207
|
+
redirect_to: '/'
|
208
|
+
}.merge(options)
|
209
|
+
|
210
|
+
path('auth') do
|
211
|
+
path(:provider) do
|
212
|
+
path('callback') do
|
213
|
+
auth = env['omniauth.auth']
|
214
|
+
session[options[:session_key]] = [auth['provider'], auth['uid']]
|
215
|
+
redirect_to options[:redirect_to]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
data/lib/freddie.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_support/core_ext/hash'
|
2
|
+
require 'rack'
|
3
|
+
require 'tilt'
|
4
|
+
|
5
|
+
require 'freddie/version'
|
6
|
+
require 'freddie/context'
|
7
|
+
require 'freddie/handlers'
|
8
|
+
require 'freddie/application'
|
9
|
+
|
10
|
+
module Freddie
|
11
|
+
class FreddieError < StandardError ; end
|
12
|
+
class NotFoundError < FreddieError ; end
|
13
|
+
class MissingHandlerError < FreddieError ; end
|
14
|
+
end
|
15
|
+
|
16
|
+
def Freddie(&block)
|
17
|
+
app = Class.new(Freddie::Application)
|
18
|
+
app.block = block
|
19
|
+
app
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include Rack::Test::Methods
|
4
|
+
|
5
|
+
describe Freddie::Handlers::Render do
|
6
|
+
def app ; Freddie::Handlers::Render.new('moo') ; end
|
7
|
+
|
8
|
+
before { get '/' }
|
9
|
+
|
10
|
+
it "should add content to the response body" do
|
11
|
+
last_response.body.must_equal 'moo'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include Rack::Test::Methods
|
4
|
+
|
5
|
+
describe "integration" do
|
6
|
+
def app
|
7
|
+
Freddie do
|
8
|
+
path('foo') { render 'bar' }
|
9
|
+
path('one') { path('two') { path('three') { render '123' }}}
|
10
|
+
path('hello') { path(:name) { render "hello %s" % params[:name] }}
|
11
|
+
path('rack') { run SillyLittleRackApp.new }
|
12
|
+
|
13
|
+
render 'root'
|
14
|
+
|
15
|
+
path('should_never_work') { render 'strange' }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SillyLittleRackApp
|
20
|
+
def call(env)
|
21
|
+
Rack::Response.new.tap do |r|
|
22
|
+
r.body << "I'm a silly little Rack app"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can serve something at the root level" do
|
28
|
+
get '/'
|
29
|
+
last_response.body.must_equal 'root'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can handle paths" do
|
33
|
+
get '/foo'
|
34
|
+
last_response.body.must_equal 'bar'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can handle paths within paths" do
|
38
|
+
get '/one/two/three'
|
39
|
+
last_response.body.must_equal '123'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "support path variables" do
|
43
|
+
get '/hello/hendrik'
|
44
|
+
last_response.body.must_equal 'hello hendrik'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "supports mounting of Rack apps" do
|
48
|
+
get '/rack'
|
49
|
+
last_response.body.must_equal "I'm a silly little Rack app"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "halts processing after rendering" do
|
53
|
+
lambda { get '/should_never_work' }.must_raise Freddie::NotFoundError
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: freddie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Hendrik Mans
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: &70194826049540 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.4.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70194826049540
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
requirement: &70194826048660 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.2.3
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70194826048660
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: tilt
|
38
|
+
requirement: &70194826047820 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.3.3
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70194826047820
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack-test
|
49
|
+
requirement: &70194826047100 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70194826047100
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: minitest-reporters
|
60
|
+
requirement: &70194826046180 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70194826046180
|
69
|
+
description: Expressive, Rack-loving web application DSL.
|
70
|
+
email:
|
71
|
+
- hendrik@mans.de
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .yardopt
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- freddie.gemspec
|
83
|
+
- lib/freddie.rb
|
84
|
+
- lib/freddie/application.rb
|
85
|
+
- lib/freddie/context.rb
|
86
|
+
- lib/freddie/handlers.rb
|
87
|
+
- lib/freddie/version.rb
|
88
|
+
- spec/handlers_spec.rb
|
89
|
+
- spec/integration_spec.rb
|
90
|
+
- spec/spec_helper.rb
|
91
|
+
homepage: ''
|
92
|
+
licenses: []
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.11
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Expressive, Rack-loving web application DSL.
|
115
|
+
test_files:
|
116
|
+
- spec/handlers_spec.rb
|
117
|
+
- spec/integration_spec.rb
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
has_rdoc:
|