manveru-innate 2009.02.06 → 2009.02.21

Sign up to get free protection for your applications and to get access to all the features.
data/MANIFEST CHANGED
@@ -68,6 +68,8 @@ lib/rack/reloader.rb
68
68
  spec/example/hello.rb
69
69
  spec/example/link.rb
70
70
  spec/helper.rb
71
+ spec/innate/action/layout.rb
72
+ spec/innate/action/layout/file_layout.erb
71
73
  spec/innate/cache/common.rb
72
74
  spec/innate/cache/marshal.rb
73
75
  spec/innate/cache/memory.rb
@@ -89,6 +91,7 @@ spec/innate/helper/view/partial.erb
89
91
  spec/innate/helper/view/recursive.erb
90
92
  spec/innate/mock.rb
91
93
  spec/innate/node.rb
94
+ spec/innate/node/another_layout/another_layout.erb
92
95
  spec/innate/node/bar.html
93
96
  spec/innate/node/foo.html.erb
94
97
  spec/innate/node/with_layout.erb
data/Rakefile CHANGED
@@ -7,6 +7,7 @@ require 'pp'
7
7
  INNATE_VERSION = Date.today.strftime("%Y.%m.%d")
8
8
 
9
9
  task :default => [:spec]
10
+ task :publish => [:ydoc]
10
11
 
11
12
  CLEAN.include('*coverage*')
12
13
 
@@ -106,17 +107,21 @@ task :rcov => :clean do
106
107
  end
107
108
 
108
109
  desc 'Run all specs'
109
- task :spec do
110
+ task :spec => :setup do
110
111
  require 'open3'
112
+ require 'scanf'
111
113
 
112
114
  specs = Dir['spec/{innate,example}/**/*.rb']
113
115
  specs.delete_if{|f| f =~ /cache\/common\.rb/ }
114
116
 
117
+ some_failed = false
115
118
  total = specs.size
116
- len = specs.sort.last.size
117
- left_format = "%4d/%d: %-#{len + 11}s"
119
+ len = specs.map{|s| s.size }.sort.last
120
+ tt = ta = tf = te = 0
118
121
 
119
122
  red, green = "\e[31m%s\e[0m", "\e[32m%s\e[0m"
123
+ left_format = "%4d/%d: %-#{len + 11}s"
124
+ spec_format = "%d specifications (%d requirements), %d failures, %d errors"
120
125
 
121
126
  specs.each_with_index do |spec, idx|
122
127
  print(left_format % [idx + 1, total, spec])
@@ -125,15 +130,120 @@ task :spec do
125
130
  out = sout.read
126
131
  err = serr.read
127
132
 
128
- md = out.match(/(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors/)
129
- tests, assertions, failures, errors = all = md.captures.map{|c| c.to_i }
133
+ out.each_line do |line|
134
+ tests, assertions, failures, errors = all = line.scanf(spec_format)
135
+ next unless all.any?
136
+ tt += tests; ta += assertions; tf += failures; te += errors
137
+
138
+ if tests == 0 || failures + errors > 0
139
+ puts((red % spec_format) % all)
140
+ puts out
141
+ puts err
142
+ else
143
+ puts((green % "%6d passed") % tests)
144
+ end
130
145
 
131
- if failures + errors > 0
132
- puts((red % "%5d tests, %d assertions, %d failures, %d errors") % all)
133
- puts "", out, err, ""
134
- else
135
- puts((green % "%5d passed") % tests)
146
+ break
136
147
  end
137
148
  end
138
149
  end
150
+
151
+ puts(spec_format % [tt, ta, tf, te])
152
+ exit 1 if some_failed
153
+ end
154
+
155
+ desc 'Generate YARD documentation'
156
+ task :ydoc do
157
+ sh('yardoc -o ydoc -r README.md')
158
+ end
159
+
160
+ begin
161
+ require 'grancher/task'
162
+
163
+ Grancher::Task.new do |g|
164
+ g.branch = 'gh-pages'
165
+ g.push_to = 'origin'
166
+ g.message = 'Updated website'
167
+ g.directory 'ydoc', 'doc'
168
+ end
169
+ rescue LoadError
170
+ # oh well :)
171
+ end
172
+
173
+ desc 'install dependencies'
174
+ task :setup do
175
+ GemSetup.new do
176
+ github = 'http://gems.github.com'
177
+ Gem.sources << github
178
+
179
+ gem('rack', '>=0.9.1')
180
+ gem('bacon', '>=1.1.0')
181
+
182
+ setup
183
+ end
184
+ end
185
+
186
+ class GemSetup
187
+ def initialize(options = {}, &block)
188
+ @gems = []
189
+ @options = options
190
+
191
+ run(&block)
192
+ end
193
+
194
+ def run(&block)
195
+ instance_eval(&block) if block_given?
196
+ end
197
+
198
+ def gem(name, version = nil, options = {})
199
+ if version.respond_to?(:merge!)
200
+ options = version
201
+ else
202
+ options[:version] = version
203
+ end
204
+
205
+ @gems << [name, options]
206
+ end
207
+
208
+ def setup
209
+ require 'rubygems'
210
+ require 'rubygems/dependency_installer'
211
+
212
+ @gems.each do |name, options|
213
+ setup_gem(name, options)
214
+ end
215
+ end
216
+
217
+ def setup_gem(name, options, try_install = true)
218
+ print "activating #{name} ... "
219
+ Gem.activate(name, *[options[:version]].compact)
220
+ require(options[:lib] || name)
221
+ puts "success."
222
+ rescue LoadError => error
223
+ puts error
224
+ install_gem(name, options) if try_install
225
+ setup_gem(name, options, try_install = false)
226
+ end
227
+
228
+ def install_gem(name, options)
229
+ installer = Gem::DependencyInstaller.new(options)
230
+
231
+ temp_argv(options[:extconf]) do
232
+ print "Installing #{name} ... "
233
+ installer.install(name, options[:version])
234
+ puts "done."
235
+ end
236
+ end
237
+
238
+ def temp_argv(extconf)
239
+ if extconf ||= @options[:extconf]
240
+ old_argv = ARGV.clone
241
+ ARGV.replace(extconf.split(' '))
242
+ end
243
+
244
+ yield
245
+
246
+ ensure
247
+ ARGV.replace(old_argv) if extconf
248
+ end
139
249
  end
@@ -3,53 +3,56 @@ require 'yaml/store'
3
3
 
4
4
  STORE = YAML::Store.new('games.yaml')
5
5
 
6
- def STORE.[](key) transaction{|s| s[key] } end
7
- def STORE.[]=(key, value) transaction{|s| s[key] = value } end
6
+ def STORE.[](key) transaction{|s| super } end
7
+ def STORE.[]=(key, value) transaction{|s| super } end
8
8
  def STORE.each
9
9
  YAML.load_file('games.yaml').sort_by{|k,v| -v }.each{|(k,v)| yield(k, v) }
10
10
  end
11
11
 
12
+ STORE['Pacman'] = 1
13
+
12
14
  class Games
13
- include Innate::Node
14
- map '/'
15
- provide :html => :haml
15
+ Innate.node('/')
16
16
 
17
17
  def index
18
18
  TEMPLATE
19
19
  end
20
20
 
21
21
  def create
22
- if request.post?
23
- name = request.params['name']
24
- STORE[name] ||= 0
25
- end
22
+ STORE[request[:name]] ||= 0 if request.post?
26
23
 
27
24
  redirect_referrer
28
25
  end
29
26
 
30
27
  def vote(name)
31
- STORE[url_decode name] += 1
28
+ STORE[url_decode(name)] += 1
32
29
 
33
30
  redirect_referrer
34
31
  end
35
32
 
36
33
  TEMPLATE = <<-'T'.strip
37
- !!! XML
38
- !!!
39
-
40
- %html
41
- %head
42
- %title Top Retro Games
43
- %body
44
- %h1 Vote on your favorite Retro Game
45
- %form{:action => r(:create), :method => 'POST'}
46
- %input{:type => 'text', :name => 'name'}
47
- %input{:type => 'submit', :value => 'Add'}
48
- %ol
49
- - STORE.each do |name, votes|
50
- %li
51
- = Games.a("Vote", "/vote/#{u name}")
52
- = h("%5d => %s" % [votes, name])
34
+ <?xml version='1.0' encoding='utf-8' ?>
35
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
36
+ <html>
37
+ <head>
38
+ <title>Top Retro Games</title>
39
+ </head>
40
+ <body>
41
+ <h1>Vote on your favorite Retro Game</h1>
42
+ <form action="<%= r :create %>" method="post">
43
+ <input type="text" name="name" />
44
+ <input type="submit" value="Add" />
45
+ </form>
46
+ <ol>
47
+ <% STORE.each do |name, votes| %>
48
+ <li>
49
+ <%= Games.a("Vote", "/vote/#{u name}") %>
50
+ <%= "%5d => %s" % [votes, name] %>
51
+ </li>
52
+ <% end %>
53
+ </ol>
54
+ </body>
55
+ </html>
53
56
  T
54
57
 
55
58
  end
data/innate.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "innate"
3
- s.version = "2009.02.06"
3
+ s.version = "2009.02.21"
4
4
 
5
5
  s.summary = "Powerful web-framework wrapper for Rack."
6
6
  s.description = "Simple, straight-forward, base for web-frameworks."
@@ -84,6 +84,8 @@ Gem::Specification.new do |s|
84
84
  "spec/example/hello.rb",
85
85
  "spec/example/link.rb",
86
86
  "spec/helper.rb",
87
+ "spec/innate/action/layout.rb",
88
+ "spec/innate/action/layout/file_layout.erb",
87
89
  "spec/innate/cache/common.rb",
88
90
  "spec/innate/cache/marshal.rb",
89
91
  "spec/innate/cache/memory.rb",
@@ -105,6 +107,7 @@ Gem::Specification.new do |s|
105
107
  "spec/innate/helper/view/recursive.erb",
106
108
  "spec/innate/mock.rb",
107
109
  "spec/innate/node.rb",
110
+ "spec/innate/node/another_layout/another_layout.erb",
108
111
  "spec/innate/node/bar.html",
109
112
  "spec/innate/node/foo.html.erb",
110
113
  "spec/innate/node/with_layout.erb",
data/lib/innate.rb CHANGED
@@ -15,177 +15,229 @@ module Innate
15
15
  unless $LOAD_PATH.any?{|lp| File.expand_path(lp) == ROOT }
16
16
  $LOAD_PATH.unshift(ROOT)
17
17
  end
18
- end
19
18
 
20
- # stdlib
21
- require 'pp'
22
- require 'set'
23
- require 'pathname'
24
- require 'digest/sha1'
25
- require 'ipaddr'
26
- require 'socket'
27
- require 'logger'
28
- require 'uri'
29
-
30
- # 3rd party
31
- require 'rack'
32
-
33
- # innate core patches
34
- require 'innate/core_compatibility/string'
35
- require 'innate/core_compatibility/basic_object'
36
-
37
- # innate core
38
- require 'innate/version'
39
- require 'innate/traited'
40
- require 'innate/cache'
41
- require 'innate/node'
42
- require 'innate/options'
43
- require 'innate/log'
44
- require 'innate/state'
45
- require 'innate/trinity'
46
- require 'innate/current'
47
- require 'innate/mock'
48
- require 'innate/adapter'
49
- require 'innate/action'
50
- require 'innate/helper'
51
- require 'innate/view'
52
- require 'innate/session'
53
- require 'innate/session/flash'
54
- require 'innate/dynamap'
55
- require 'innate/route'
56
-
57
- require 'rack/reloader'
58
- require 'rack/middleware_compiler'
19
+ # stdlib
20
+ require 'pp'
21
+ require 'set'
22
+ require 'pathname'
23
+ require 'digest/sha1'
24
+ require 'ipaddr'
25
+ require 'socket'
26
+ require 'logger'
27
+ require 'uri'
28
+
29
+ # 3rd party
30
+ require 'rack'
31
+
32
+ # innate core patches
33
+ require 'innate/core_compatibility/string'
34
+ require 'innate/core_compatibility/basic_object'
35
+
36
+ # innate core
37
+ require 'innate/version'
38
+ require 'innate/traited'
39
+ require 'innate/cache'
40
+ require 'innate/node'
41
+ require 'innate/options'
42
+ require 'innate/log'
43
+ require 'innate/state'
44
+ require 'innate/trinity'
45
+ require 'innate/current'
46
+ require 'innate/mock'
47
+ require 'innate/adapter'
48
+ require 'innate/action'
49
+ require 'innate/helper'
50
+ require 'innate/view'
51
+ require 'innate/session'
52
+ require 'innate/session/flash'
53
+ require 'innate/dynamap'
54
+ require 'innate/route'
55
+
56
+ # innate lib/rack
57
+ require 'rack/reloader'
58
+ require 'rack/middleware_compiler'
59
59
 
60
- module Innate
61
60
  extend Trinity
62
61
 
62
+ # This constant holds a couple of common middlewares and allows for easy
63
+ # addition of new ones.
64
+ # The Proc to use is determined by the value of options.mode.
65
+ # The Proc value is passed to setup_middleware if no block is given to
66
+ # Innate::start.
67
+ #
68
+ # A quick overview over the middleware used here:
69
+ #
70
+ # * Rack::CommonLogger
71
+ # Logs a line in Apache common log format or <tt>rack.errors</tt>.
72
+ #
73
+ # * Rack::ShowExceptions
74
+ # Catches all exceptions raised from the app it wraps. It shows a useful
75
+ # backtrace with the sourcefile and clickable context, the whole Rack
76
+ # environment and the request data.
77
+ # Be careful when you use this on public-facing sites as it could reveal
78
+ # information helpful to attackers.
79
+ #
80
+ # * Rack::ShowStatus
81
+ # Catches all empty responses the app it wraps and replaces them with a
82
+ # site explaining the error.
83
+ # Additional details can be put into <tt>rack.showstatus.detail</tt> and
84
+ # will be shown as HTML. If such details exist, the error page is always
85
+ # rendered, even if the reply was not empty.
86
+ #
87
+ # * Rack::ConditionalGet
88
+ # Middleware that enables conditional GET using If-None-Match and
89
+ # If-Modified-Since. The application should set either or both of the
90
+ # Last-Modified or Etag response headers according to RFC 2616. When
91
+ # either of the conditions is met, the response body is set to be zero
92
+ # length and the response status is set to 304 Not Modified.
93
+ #
94
+ # * Rack::Head
95
+ # Removes the body of the response for HEAD requests.
96
+ #
97
+ # * Rack::Reloader
98
+ # Pure ruby source reloader, runs on every request with a configurable
99
+ # cooldown period.
100
+ #
101
+ # * Rack::Lint
102
+ # Rack::Lint validates your application and the requests and responses
103
+ # according to the Rack spec.
104
+ #
63
105
  # Note that `m.innate` takes away most of the boring part and leaves it up to
64
106
  # you to select your middleware in your application.
65
107
  #
66
- # This expands to:
108
+ # `m.innate` expands to:
67
109
  #
68
- # use Rack::ShowExceptions
69
- # use Rack::RouteExceptions
70
- # use Rack::ShowStatus
71
- # use Rack::Reloader
72
110
  # use Rack::Cascade.new([
73
111
  # Rack::File.new('public'),
74
112
  # Innate::Current.new(
75
113
  # Rack::Cascade.new([
76
114
  # Innate::Rewrite.new(Innate::DynaMap),
77
115
  # Innate::Route.new(Innate::DynaMap)])))
78
- DEFAULT_MIDDLEWARE = lambda{|m|
79
- m.use Rack::CommonLogger # usually fast, depending on the output
80
- m.use Rack::ShowExceptions # fast
81
- # m.use Rack::RouteExceptions # fast, use when you have custom error pages.
82
- m.use Rack::ShowStatus # fast
83
- m.use Rack::Reloader # reasonably fast depending on settings
84
- # m.use Rack::Lint # slow, use only while developing
85
-
86
- m.innate
116
+ #
117
+ # @see Rack::MiddlewareCompiler
118
+ MIDDLEWARE = {
119
+ :dev => lambda{|m|
120
+ m.use(Rack::Lint, Rack::CommonLogger, Rack::ShowExceptions,
121
+ Rack::ShowStatus, Rack::ConditionalGet, Rack::Head, Rack::Reloader)
122
+ m.innate },
123
+ :live => lambda{|m|
124
+ m.use(Rack::CommonLogger, Rack::ShowStatus, Rack::ConditionalGet,
125
+ Rack::Head)
126
+ m.innate }
87
127
  }
88
128
 
89
- module_function
129
+ module SingletonMethods
130
+ def start(parameter = {}, &block)
131
+ options[:app][:root] = go_figure_root(parameter, caller)
132
+ parameter.reject!{|k, v| [:root, :file].include?(k) }
133
+ options.merge!(parameter)
90
134
 
91
- def start(parameter = {}, &block)
92
- setup_dependencies
93
- setup_middleware(&block)
135
+ setup_dependencies
136
+ setup_middleware(&block)
94
137
 
95
- options[:app][:root] = go_figure_root(parameter, caller)
96
- parameter.reject!{|k, v| [:root, :file].include?(k) }
97
- options.merge!(parameter)
138
+ return if options.started
139
+ options.started = true
98
140
 
99
- return if options.started
100
- options.started = true
141
+ trap(options[:trap]){ stop(10) } if options[:trap]
101
142
 
102
- trap(options[:trap]){ stop(10) } if options[:trap]
143
+ start!(options)
144
+ end
103
145
 
104
- start!(options)
105
- end
146
+ def start!(options = Innate.options)
147
+ Adapter.start(middleware(:innate), options)
148
+ end
106
149
 
107
- def start!(options = Innate.options)
108
- Adapter.start(middleware(:innate), options)
109
- end
150
+ def stop(wait = 3)
151
+ Log.info("Shutdown Innate within #{wait} seconds")
152
+ Timeout.timeout(wait){ exit }
153
+ ensure
154
+ exit!
155
+ end
110
156
 
111
- def stop(wait = 3)
112
- Log.info("Shutdown Innate within #{wait} seconds")
113
- Timeout.timeout(wait){ exit }
114
- ensure
115
- exit!
116
- end
157
+ def middleware(name, &block)
158
+ Rack::MiddlewareCompiler.build(name, &block)
159
+ end
117
160
 
118
- def middleware(name, &block)
119
- Rack::MiddlewareCompiler.build(name, &block)
120
- end
161
+ def middleware!(name, &block)
162
+ Rack::MiddlewareCompiler.build!(name, &block)
163
+ end
121
164
 
122
- def middleware!(name, &block)
123
- Rack::MiddlewareCompiler.build!(name, &block)
124
- end
165
+ def middleware_recompile(name = :innate)
166
+ Rack::MiddlewareCompiler::COMPILED[name].compile!
167
+ end
125
168
 
126
- def setup_dependencies
127
- options[:setup].each{|obj| obj.setup }
128
- end
169
+ def setup_dependencies
170
+ options[:setup].each{|obj| obj.setup }
171
+ end
129
172
 
130
- # Set the default middleware for applications.
131
- def setup_middleware(&block)
132
- middleware(:innate, &(block || DEFAULT_MIDDLEWARE))
133
- end
173
+ # Set the default middleware for applications.
174
+ def setup_middleware(force = false, &block)
175
+ mode = options.mode
176
+ block ||= MIDDLEWARE[mode]
177
+ raise("No Middleware for mode: %p found" % mode) unless block
134
178
 
135
- # Pass the +env+ to this method and it will be sent to the appropriate
136
- # middleware called +mw+.
137
- # Tries to avoid recursion.
179
+ force ? middleware!(:innate, &block) : middleware(:innate, &block)
180
+ end
138
181
 
139
- def call(env, mw = :innate)
140
- this_file = File.expand_path(__FILE__)
141
- count = 0
142
- caller_lines(caller){|f, l, m| count += 1 if f == this_file }
182
+ # Pass the +env+ to this method and it will be sent to the appropriate
183
+ # middleware called +mw+.
184
+ # Tries to avoid recursion.
143
185
 
144
- raise RuntimeError, "Recursive loop in Innate::call" if count > 10
186
+ def call(env, mw = :innate)
187
+ this_file = File.expand_path(__FILE__)
188
+ count = 0
189
+ caller_lines(caller){|f, l, m| count += 1 if f == this_file }
145
190
 
146
- middleware(mw).call(env)
147
- end
191
+ raise("Recursive loop in Innate::call") if count > 10
148
192
 
149
- # Innate can be started by:
150
- #
151
- # Innate.start :file => __FILE__
152
- # Innate.start :root => '/path/to/here'
153
- #
154
- # In case these options are not passed we will try to figure out a file named
155
- # `start.rb` in the backtrace and use the directory it resides in.
156
- #
157
- # TODO: better documentation and nice defaults, don't want to rely on a
158
- # filename, bad mojo.
159
-
160
- def go_figure_root(options, backtrace)
161
- if o_file = options[:file]
162
- return File.dirname(o_file)
163
- elsif root = options[:root]
164
- return root
193
+ middleware(mw).call(env)
165
194
  end
166
195
 
167
- pwd = Dir.pwd
196
+ # Innate can be started by:
197
+ #
198
+ # Innate.start :file => __FILE__
199
+ # Innate.start :root => '/path/to/here'
200
+ #
201
+ # In case these options are not passed we will try to figure out a file named
202
+ # `start.rb` in the backtrace and use the directory it resides in.
203
+ #
204
+ # TODO: better documentation and nice defaults, don't want to rely on a
205
+ # filename, bad mojo.
206
+
207
+ def go_figure_root(options, backtrace)
208
+ if o_file = options[:file]
209
+ return File.dirname(o_file)
210
+ elsif root = options[:root]
211
+ return root
212
+ end
213
+
214
+ pwd = Dir.pwd
168
215
 
169
- return pwd if File.file?(File.join(pwd, 'start.rb'))
216
+ return pwd if File.file?(File.join(pwd, 'start.rb'))
217
+
218
+ caller_lines(backtrace) do |file, line, method|
219
+ dir, file = File.split(File.expand_path(file))
220
+ return dir if file == "start.rb"
221
+ end
170
222
 
171
- caller_lines(backtrace) do |file, line, method|
172
- dir, file = File.split(File.expand_path(file))
173
- return dir if file == "start.rb"
223
+ Log.warn("Couldn't find your application root, see Innate#go_figure_root")
224
+
225
+ return nil
174
226
  end
175
227
 
176
- return nil
177
- end
228
+ # yields +file+, +line+, +method+
229
+ def caller_lines(backtrace)
230
+ backtrace.each do |line|
231
+ if line =~ /^(.*?):(\d+):in `(.*)'$/
232
+ file, line, method = $1, $2.to_i, $3
233
+ elsif line =~ /^(.*?):(\d+)$/
234
+ file, line, method = $1, $2.to_i, nil
235
+ end
178
236
 
179
- # yields +file+, +line+, +method+
180
- def caller_lines(backtrace)
181
- backtrace.each do |line|
182
- if line =~ /^(.*?):(\d+):in `(.*)'$/
183
- file, line, method = $1, $2.to_i, $3
184
- elsif line =~ /^(.*?):(\d+)$/
185
- file, line, method = $1, $2.to_i, nil
237
+ yield(File.expand_path(file), line, method) if file and File.file?(file)
186
238
  end
187
-
188
- yield(File.expand_path(file), line, method) if file and File.file?(file)
189
239
  end
190
240
  end
241
+
242
+ extend SingletonMethods
191
243
  end