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 CHANGED
@@ -1 +1,2 @@
1
1
  *~
2
+ test/repo
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 provides a simple pattern matching algorithm for routing
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].
@@ -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
- def config_reader(file, *names)
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
- @mtime = {}
27
- @last_mtime = Time.now
28
- @path = File.expand_path('.')
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
- def repo
52
- @store.repo
53
- end
17
+ include Helpers
54
18
 
55
- def check_reload
56
- commit = store.repo.commits('master', 1)[0]
19
+ attr_reader :path, :store
57
20
 
58
- if commit and (last_commit.nil? or last_commit.id != commit.id)
59
- @last_commit = commit
60
- @last_mtime = last_commit.committed_date
61
- @mtime = {}
62
- store.load
63
- load_store_from_disk
64
- elsif ENV['RACK_ENV'] != 'production'
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 load_store_from_disk
70
- store.each_with_path do |blob, path|
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
- if store
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 if_modified_since(date = @last_mtime.httpdate)
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 render_stylesheets
124
- if_modified_since do
125
- stylesheet_files.map { |file| assets["stylesheets/#{file.strip}.css"] }.join
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
- check_reload if store
115
+ store.refresh!
185
116
 
186
117
  env['kontrol.app'] = self
187
118
 
188
- status, header, body = find_map.call(env)
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)
@@ -29,15 +29,14 @@ module Kontrol
29
29
  def call(env)
30
30
  path = env["PATH_INFO"].to_s.squeeze("/")
31
31
 
32
- for pattern, app, options in @routing
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
@@ -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__) + '/test_repo')
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
- @app = Kontrol::Application.new
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
- @app.map(&block)
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
- render_javascripts
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
- render_stylesheets
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
- if_modified_since do
93
- assets[path]
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
- last_mod = get("/assets/file")['Last-Modified']
115
+ etag = get("/assets/file")['Etag']
105
116
 
106
- get("/assets/file", 'HTTP_IF_MODIFIED_SINCE' => last_mod).status.should == 304
117
+ get("/assets/file", 'HTTP_IF_NONE_MATCH' => etag).status.should == 304
107
118
  end
108
119
 
109
120
  it "should render templates" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: georgi-kontrol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Georgi