georgi-kontrol 0.1.1 → 0.1.2
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 +1 -0
- data/README.md +1 -2
- data/lib/kontrol/application.rb +38 -107
- data/lib/kontrol/router.rb +3 -4
- data/test/application_spec.rb +22 -11
- metadata +1 -1
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,7 @@ Kontrol - a micro framework
|
|
2
2
|
===========================
|
3
3
|
|
4
4
|
Kontrol is a small web framework written in Ruby, which runs directly
|
5
|
-
on [Rack][5]. It
|
6
|
-
and uses GitStore as data storage.
|
5
|
+
on [Rack][5]. It uses regular expressions for routing.
|
7
6
|
|
8
7
|
All examples can be found in the [examples folder][3] of the kontrol
|
9
8
|
project, which is hosted on [this github page][4].
|
data/lib/kontrol/application.rb
CHANGED
@@ -1,92 +1,40 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
1
3
|
module Kontrol
|
2
4
|
|
3
5
|
class Application
|
4
6
|
|
5
|
-
include Helpers
|
6
|
-
|
7
7
|
class << self
|
8
|
-
|
9
|
-
|
10
|
-
names.each do |name|
|
11
|
-
name = name.to_s
|
12
|
-
define_method(name) { config[file][name] }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :path, :store, :last_commit
|
18
|
-
|
19
|
-
config_reader 'assets.yml', :javascript_files, :stylesheet_files
|
20
|
-
|
21
|
-
def initialize(options = {})
|
22
|
-
options.each do |k, v|
|
23
|
-
send "#{k}=", v
|
8
|
+
def map(&block)
|
9
|
+
@map = Builder.new(&block)
|
24
10
|
end
|
25
11
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
initialize_repo if defined?(GitStore)
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize_repo
|
34
|
-
@store = GitStore.new(path)
|
35
|
-
@store.load
|
36
|
-
rescue Grit::InvalidGitRepositoryError
|
37
|
-
end
|
38
|
-
|
39
|
-
def assets
|
40
|
-
store['assets'] ||= GitStore::Tree.new
|
41
|
-
end
|
42
|
-
|
43
|
-
def config
|
44
|
-
store['config'] ||= GitStore::Tree.new
|
45
|
-
end
|
46
|
-
|
47
|
-
def templates
|
48
|
-
store['templates'] ||= GitStore::Tree.new
|
12
|
+
def call(env)
|
13
|
+
@map.call(env)
|
14
|
+
end
|
49
15
|
end
|
50
16
|
|
51
|
-
|
52
|
-
@store.repo
|
53
|
-
end
|
17
|
+
include Helpers
|
54
18
|
|
55
|
-
|
56
|
-
commit = store.repo.commits('master', 1)[0]
|
19
|
+
attr_reader :path, :store
|
57
20
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
store.
|
63
|
-
|
64
|
-
|
65
|
-
load_store_from_disk
|
21
|
+
def initialize(path)
|
22
|
+
@path = path
|
23
|
+
|
24
|
+
if File.directory?(File.join(path, '.git')) && ENV['RACK_ENV'] == 'production'
|
25
|
+
@store = GitStore.new(path)
|
26
|
+
else
|
27
|
+
@store = GitStore::FileStore.new(path)
|
66
28
|
end
|
67
29
|
end
|
68
30
|
|
69
|
-
def
|
70
|
-
store
|
71
|
-
path = "#{self.path}/#{path}"
|
72
|
-
if File.exist?(path)
|
73
|
-
mtime = File.mtime(path)
|
74
|
-
if mtime != @mtime[path]
|
75
|
-
@mtime[path] = mtime
|
76
|
-
@last_mtime = mtime if mtime > @last_mtime
|
77
|
-
blob.load(File.read(path))
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
31
|
+
def templates
|
32
|
+
store['templates']
|
81
33
|
end
|
82
|
-
|
34
|
+
|
83
35
|
# Render template with given variables.
|
84
36
|
def render_template(file, vars)
|
85
|
-
|
86
|
-
template = templates[file] or raise "template #{file} not found"
|
87
|
-
else
|
88
|
-
template = ERB.new(File.read("#{path}/templates/#{file}"))
|
89
|
-
end
|
37
|
+
template = templates[file] or raise "template #{file} not found"
|
90
38
|
Template.render(template, self, file, vars)
|
91
39
|
end
|
92
40
|
|
@@ -105,27 +53,30 @@ module Kontrol
|
|
105
53
|
end
|
106
54
|
end
|
107
55
|
|
108
|
-
def
|
56
|
+
def etag(string)
|
57
|
+
Digest::SHA1.hexdigest(string)
|
58
|
+
end
|
59
|
+
|
60
|
+
def if_modified_since(time)
|
61
|
+
date = time.respond_to?(:httpdate) ? time.httpdate : time
|
109
62
|
response['Last-Modified'] = date
|
63
|
+
|
110
64
|
if request.env['HTTP_IF_MODIFIED_SINCE'] == date
|
111
65
|
response.status = 304
|
112
66
|
else
|
113
|
-
yield
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def render_javascripts
|
118
|
-
if_modified_since do
|
119
|
-
javascript_files.map { |file| assets["javascripts/#{file.strip}.js"] }.join
|
67
|
+
yield
|
120
68
|
end
|
121
69
|
end
|
122
|
-
|
123
|
-
def
|
124
|
-
|
125
|
-
|
70
|
+
|
71
|
+
def if_none_match(etag)
|
72
|
+
response['Etag'] = etag
|
73
|
+
if request.env['HTTP_IF_NONE_MATCH'] == etag
|
74
|
+
response.status = 304
|
75
|
+
else
|
76
|
+
yield
|
126
77
|
end
|
127
78
|
end
|
128
|
-
|
79
|
+
|
129
80
|
def request
|
130
81
|
Thread.current['request']
|
131
82
|
end
|
@@ -157,35 +108,15 @@ module Kontrol
|
|
157
108
|
MIME_TYPES[ext] || 'text/html'
|
158
109
|
end
|
159
110
|
|
160
|
-
class << self
|
161
|
-
|
162
|
-
def map(&block)
|
163
|
-
@map = Builder.new(&block)
|
164
|
-
end
|
165
|
-
|
166
|
-
def get_map
|
167
|
-
@map
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
|
172
|
-
def find_map
|
173
|
-
if store and store['map.rb']
|
174
|
-
store['map.rb']
|
175
|
-
else
|
176
|
-
self.class.get_map
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
111
|
def call(env)
|
181
112
|
Thread.current['request'] = Rack::Request.new(env)
|
182
113
|
Thread.current['response'] = Rack::Response.new([], nil, { 'Content-Type' => '' })
|
183
114
|
|
184
|
-
|
115
|
+
store.refresh!
|
185
116
|
|
186
117
|
env['kontrol.app'] = self
|
187
118
|
|
188
|
-
status, header, body =
|
119
|
+
status, header, body = self.class.call(env)
|
189
120
|
|
190
121
|
response.status = status if response.status.nil?
|
191
122
|
response.header.merge!(header)
|
data/lib/kontrol/router.rb
CHANGED
@@ -29,15 +29,14 @@ module Kontrol
|
|
29
29
|
def call(env)
|
30
30
|
path = env["PATH_INFO"].to_s.squeeze("/")
|
31
31
|
|
32
|
-
|
33
|
-
match = path.match(/^#{pattern}/)
|
34
|
-
if match and options_match(env, options)
|
32
|
+
@routing.each do |pattern, app, options|
|
33
|
+
if (match = path.match(/^#{pattern}/)) && options_match(env, options)
|
35
34
|
env = env.dup
|
36
35
|
(env['kontrol.args'] ||= []).concat(match.to_a[1..-1])
|
37
36
|
if match[0] == pattern
|
38
37
|
env["SCRIPT_NAME"] += match[0]
|
39
38
|
env["PATH_INFO"] = path[match[0].size..-1]
|
40
|
-
end
|
39
|
+
end
|
41
40
|
return app.call(env)
|
42
41
|
end
|
43
42
|
end
|
data/test/application_spec.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'kontrol'
|
2
|
+
require 'git_store'
|
2
3
|
require 'rack/mock'
|
3
4
|
|
4
5
|
describe Kontrol::Builder do
|
5
6
|
|
6
|
-
REPO = File.expand_path(File.dirname(__FILE__) + '/
|
7
|
+
REPO = File.expand_path(File.dirname(__FILE__) + '/repo')
|
7
8
|
|
8
9
|
before do
|
9
10
|
FileUtils.rm_rf REPO
|
@@ -11,7 +12,8 @@ describe Kontrol::Builder do
|
|
11
12
|
Dir.chdir REPO
|
12
13
|
`git init`
|
13
14
|
|
14
|
-
@
|
15
|
+
@class = Class.new(Kontrol::Application)
|
16
|
+
@app = @class.new(REPO)
|
15
17
|
@request = Rack::MockRequest.new(@app)
|
16
18
|
end
|
17
19
|
|
@@ -32,7 +34,7 @@ describe Kontrol::Builder do
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def map(&block)
|
35
|
-
@
|
37
|
+
@class.map(&block)
|
36
38
|
end
|
37
39
|
|
38
40
|
def file(file, data)
|
@@ -57,6 +59,15 @@ describe Kontrol::Builder do
|
|
57
59
|
put("/two").body.should == 'four'
|
58
60
|
end
|
59
61
|
|
62
|
+
it "should redirect" do
|
63
|
+
map do
|
64
|
+
get '/' do redirect 'x' end
|
65
|
+
end
|
66
|
+
|
67
|
+
get('/')['Location'].should == 'x'
|
68
|
+
get('/').status.should == 301
|
69
|
+
end
|
70
|
+
|
60
71
|
it "should reload after a commit" do
|
61
72
|
file 'file', 'file'
|
62
73
|
|
@@ -74,24 +85,24 @@ describe Kontrol::Builder do
|
|
74
85
|
end
|
75
86
|
|
76
87
|
it "should serve assets" do
|
77
|
-
file 'config/assets.yml', '{ javascript_files: [index], stylesheet_files: [styles] }'
|
78
88
|
file 'assets/javascripts/index.js', 'index'
|
79
89
|
file 'assets/stylesheets/styles.css', 'styles'
|
80
90
|
file 'assets/file', 'file'
|
81
91
|
|
82
92
|
map do
|
83
93
|
get '/assets/javascripts\.js' do
|
84
|
-
|
94
|
+
scripts = store['assets/javascripts'].to_a.join
|
95
|
+
if_none_match(etag(scripts)) { scripts }
|
85
96
|
end
|
86
97
|
|
87
98
|
get '/assets/stylesheets\.css' do
|
88
|
-
|
99
|
+
styles = store['assets/stylesheets'].to_a.join
|
100
|
+
if_none_match(etag(styles)) { styles }
|
89
101
|
end
|
90
102
|
|
91
103
|
get '/assets/(.*)' do |path|
|
92
|
-
|
93
|
-
|
94
|
-
end
|
104
|
+
file = store['assets'][path]
|
105
|
+
if_none_match(etag(file)) { file }
|
95
106
|
end
|
96
107
|
end
|
97
108
|
|
@@ -101,9 +112,9 @@ describe Kontrol::Builder do
|
|
101
112
|
get("/assets/stylesheets.css")['Content-Type'].should == 'text/css'
|
102
113
|
|
103
114
|
get("/assets/file").body.should == 'file'
|
104
|
-
|
115
|
+
etag = get("/assets/file")['Etag']
|
105
116
|
|
106
|
-
get("/assets/file", '
|
117
|
+
get("/assets/file", 'HTTP_IF_NONE_MATCH' => etag).status.should == 304
|
107
118
|
end
|
108
119
|
|
109
120
|
it "should render templates" do
|