nyara 0.0.1.pre.9 → 0.1.pre.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/bin/nyara +3 -3
  3. data/changes +1 -0
  4. data/ext/event.c +16 -22
  5. data/ext/hashes.c +222 -4
  6. data/ext/inc/rdtsc.h +56 -0
  7. data/ext/inc/status_codes.inc +64 -0
  8. data/ext/inc/version.inc +1 -1
  9. data/ext/nyara.c +12 -10
  10. data/ext/nyara.h +5 -5
  11. data/ext/request.c +18 -24
  12. data/ext/request_parse.c +1 -1
  13. data/ext/route.cc +2 -4
  14. data/ext/url_encoded.c +51 -193
  15. data/lib/nyara/command.rb +39 -12
  16. data/lib/nyara/config.rb +10 -10
  17. data/lib/nyara/controller.rb +60 -14
  18. data/lib/nyara/cookie.rb +1 -1
  19. data/lib/nyara/hashes/config_hash.rb +2 -24
  20. data/lib/nyara/nyara.rb +33 -19
  21. data/lib/nyara/part.rb +7 -3
  22. data/lib/nyara/reload.rb +85 -0
  23. data/lib/nyara/request.rb +1 -1
  24. data/lib/nyara/route.rb +55 -19
  25. data/lib/nyara/templates/Gemfile +10 -1
  26. data/lib/nyara/templates/Rakefile +6 -1
  27. data/lib/nyara/templates/app/controllers/application_controller.rb +3 -0
  28. data/lib/nyara/templates/app/controllers/welcome_controller.rb +5 -0
  29. data/lib/nyara/templates/app/views/layouts/application.erb +12 -0
  30. data/lib/nyara/templates/app/views/welcome/index.erb +1 -0
  31. data/lib/nyara/templates/config/application.rb +34 -0
  32. data/lib/nyara/templates/config/boot.rb +4 -0
  33. data/lib/nyara/templates/config/development.rb +5 -0
  34. data/lib/nyara/templates/config/production.rb +8 -0
  35. data/lib/nyara/templates/config/test.rb +2 -0
  36. data/lib/nyara/templates/public/css/app.css +1 -0
  37. data/lib/nyara/templates/public/js/app.js +1 -0
  38. data/lib/nyara/templates/spec/spec_helper.rb +9 -0
  39. data/lib/nyara/test.rb +10 -2
  40. data/lib/nyara/view.rb +116 -67
  41. data/nyara.gemspec +3 -1
  42. data/rakefile +1 -1
  43. data/readme.md +1 -1
  44. data/spec/command_spec.rb +28 -24
  45. data/spec/config_spec.rb +24 -1
  46. data/spec/dummy/app/controllers/dummy_controller.rb +2 -0
  47. data/spec/dummy/app/models/dmmy_model.rb +2 -0
  48. data/spec/evented_io_spec.rb +2 -1
  49. data/spec/ext_route_spec.rb +2 -2
  50. data/spec/flash_spec.rb +8 -0
  51. data/spec/hashes_spec.rb +127 -0
  52. data/spec/integration_spec.rb +15 -0
  53. data/spec/path_helper_spec.rb +17 -5
  54. data/spec/performance/escape.rb +15 -4
  55. data/spec/performance/layout_render.rb +15 -10
  56. data/spec/performance/parse_accept_value.rb +24 -8
  57. data/spec/performance/parse_param.rb +14 -8
  58. data/spec/performance/performance_helper.rb +8 -21
  59. data/spec/performance_spec.rb +5 -4
  60. data/spec/route_spec.rb +7 -2
  61. data/spec/url_encoded_spec.rb +18 -74
  62. data/spec/view_spec.rb +1 -3
  63. data/spec/views/_partial.slim +1 -0
  64. data/spec/views/_partial_with_yield.erb +1 -0
  65. metadata +73 -43
  66. data/example/factorial.rb +0 -19
  67. data/example/hello.rb +0 -5
  68. data/example/project.rb +0 -11
  69. data/example/stream.rb +0 -14
  70. data/lib/nyara/controllers/public_controller.rb +0 -14
  71. data/lib/nyara/templates/config/session.key +0 -1
  72. data/tools/bench-cookie.rb +0 -22
  73. data/tools/foo.rb +0 -9
  74. data/tools/hello.rb +0 -46
  75. data/tools/memcheck.rb +0 -33
  76. data/tools/s.rb +0 -11
@@ -0,0 +1,85 @@
1
+ require "listen"
2
+
3
+ module Nyara
4
+ # listen to fs events and reload code / views
5
+ # todo add to development env: require 'nyara/reload'; Reload.listen
6
+ module Reload
7
+ # init, should require all files that needs to be reloaded in the given block
8
+ def self.init
9
+ @new_classes = []
10
+ @trace_point = TracePoint.new :class do |tp|
11
+ @new_classes << tp.self.to_s.to_sym
12
+ end
13
+ @file_list = {}
14
+ @first_load = true
15
+ yield
16
+ @first_load = false
17
+ end
18
+
19
+ # NOTE file should end with '.rb'
20
+ def self.load_file file
21
+ if consts = @file_list[file]
22
+ consts.reverse_each do |const|
23
+ Object.send :remove_const, const
24
+ end
25
+ end
26
+
27
+ @trace_point.enable
28
+ old_consts = Object.send :constants
29
+ if @first_load
30
+ require file
31
+ else
32
+ if l = Nyara.logger
33
+ l.info "reloading: #{file}"
34
+ end
35
+ begin
36
+ load file
37
+ @last_error = nil
38
+ rescue Exception
39
+ @last_error = $!
40
+ end
41
+ end
42
+ @trace_point.disable
43
+ added_consts = Object.send(:constants) - old_consts
44
+
45
+ added_consts.concat @new_classes
46
+ @new_classes.clear
47
+ added_consts.uniq!
48
+ added_consts.sort!
49
+
50
+ @file_list[file] = added_consts
51
+ @last_error
52
+ end
53
+
54
+ # start listening
55
+ def self.listen
56
+ views_path = Config.views_path('/')
57
+ if views_path
58
+ if l = Nyara.logger
59
+ l.info "watching views change under #{views_path}"
60
+ end
61
+ Listen.to Config.views_path('/'), relative_paths: true do |modified, added, removed|
62
+ modified.each do |file|
63
+ View.on_modified file
64
+ end
65
+ removed.each do |file|
66
+ View.on_removed file
67
+ end
68
+ end
69
+ end
70
+
71
+ return unless Config.development?
72
+ if l = Nyara.logger
73
+ l.info "watching app change under #{Config['root']}"
74
+ end
75
+ Listen.to Config['root'], filter: /\.rb$/, relative_paths: false do |modified, added, removed|
76
+ (added + modified).uniq.each do |file|
77
+ load_file file
78
+ end
79
+ # (1.0) todo send notification on bad files
80
+ end
81
+ end
82
+
83
+ # todo (don't forget wiki doc!)
84
+ end
85
+ end
data/lib/nyara/request.rb CHANGED
@@ -126,7 +126,7 @@ module Nyara
126
126
  b = body # read all the message
127
127
  case b
128
128
  when String
129
- Ext.parse_param q, body
129
+ ParamHash.parse_param q, body
130
130
  when Array
131
131
  b.each do |part|
132
132
  part.merge_into q
data/lib/nyara/route.rb CHANGED
@@ -24,6 +24,14 @@ module Nyara
24
24
  m
25
25
  end
26
26
 
27
+ # nil for get / post
28
+ def http_method_override
29
+ m = http_method_to_s
30
+ if m != 'GET' and m != 'POST'
31
+ m
32
+ end
33
+ end
34
+
27
35
  # enum all combinations of matching selectors
28
36
  def selectors
29
37
  if classes
@@ -96,6 +104,9 @@ module Nyara
96
104
  # private
97
105
  # +++
98
106
 
107
+ TOKEN = /%(?:[sz]|(?>\.\d+)?[dfux])/
108
+ FORWARD_SPLIT = /(?=#{TOKEN})/
109
+
99
110
  # #### Returns
100
111
  #
101
112
  # [str_re, conv]
@@ -103,21 +114,32 @@ module Nyara
103
114
  def compile_re suffix
104
115
  return ['', []] unless suffix
105
116
  conv = []
106
- re_segs = suffix.split(/(?<=%[dfsuxz])|(?=%[dfsuxz])/).map do |s|
117
+ segs = suffix.split(FORWARD_SPLIT).flat_map do |s|
118
+ if (s =~ TOKEN) == 0
119
+ part1 = s[TOKEN]
120
+ [part1, s.slice(part1.size..-1)]
121
+ else
122
+ s
123
+ end
124
+ end
125
+ re_segs = segs.map do |s|
107
126
  case s
108
- when '%d'
109
- conv << :to_i
110
- '(-?[0-9]+)'
111
- when '%f'
112
- conv << :to_f
113
- # just copied from scanf
114
- '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))'
115
- when '%u'
116
- conv << :to_i
117
- '([0-9]+)'
118
- when '%x'
119
- conv << :hex
120
- '(\h+)'
127
+ when /\A%(?>\.\d+)?([dfux])\z/
128
+ case $1
129
+ when 'd'
130
+ conv << :to_i
131
+ '(-?\d+)'
132
+ when 'f'
133
+ conv << :to_f
134
+ # just copied from scanf
135
+ '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))'
136
+ when 'u'
137
+ conv << :to_i
138
+ '(\d+)'
139
+ when 'x'
140
+ conv << :hex
141
+ '(\h+)'
142
+ end
121
143
  when '%s'
122
144
  conv << :to_s
123
145
  '([^/]+)'
@@ -138,12 +160,16 @@ module Nyara
138
160
  raise 'path must start with /' unless path.start_with? '/'
139
161
  path = path.sub(/\/$/, '') if path != '/'
140
162
 
141
- path.split(/(?=%[dfsuxz])/, 2)
163
+ path.split(FORWARD_SPLIT, 2)
142
164
  end
143
165
  end
144
166
 
145
167
  # class methods
146
168
  class << Route
169
+ def routes
170
+ @routes || []
171
+ end
172
+
147
173
  # #### Param
148
174
  #
149
175
  # * `controller` - string or class which inherits [Nyara::Controller](Controller.html)
@@ -161,7 +187,7 @@ module Nyara
161
187
  @global_path_templates = {} # "name#id" => path
162
188
  mapped_controllers = {}
163
189
 
164
- routes = @controllers.flat_map do |scope, c|
190
+ @routes = @controllers.flat_map do |scope, c|
165
191
  if c.is_a?(String)
166
192
  c = name2const c
167
193
  end
@@ -177,15 +203,15 @@ module Nyara
177
203
  @global_path_templates[name + e.id] = e.path_template
178
204
  end
179
205
  end
180
- routes.sort_by! &:prefix
181
- routes.reverse!
206
+ @routes.sort_by! &:prefix
207
+ @routes.reverse!
182
208
 
183
209
  mapped_controllers.each do |c, _|
184
210
  c.path_templates = @global_path_templates.merge c.path_templates
185
211
  end
186
212
 
187
213
  Ext.clear_route
188
- routes.each do |e|
214
+ @routes.each do |e|
189
215
  Ext.register_route e
190
216
  end
191
217
  end
@@ -213,6 +239,16 @@ module Nyara
213
239
  @controllers = []
214
240
  end
215
241
 
242
+ def print_routes
243
+ puts "all routes:"
244
+ Nyara::Route.routes.each do |route|
245
+ print (route.id || "").gsub("#", "").rjust(30), " "
246
+ print route.http_method_to_s.ljust(6), " "
247
+ print route.path
248
+ puts ""
249
+ end
250
+ end
251
+
216
252
  # private
217
253
 
218
254
  def const2name c
@@ -1,4 +1,13 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'nyara', '<%= nyara_version %>'
3
+ gem 'erubis'
4
+ gem 'nyara', '<%= Nyara::VERSION %>', require: 'nyara/nyara'
4
5
  gem 'mongoid', '3.1.4'
6
+
7
+ group :deploy do
8
+ gem 'rake'
9
+ end
10
+
11
+ group :test do
12
+ gem 'rspec'
13
+ end
@@ -1 +1,6 @@
1
- require 'rake'
1
+ desc "display all routes"
2
+ task :routes do
3
+ require_relative 'config/application'
4
+ Nyara.setup
5
+ Nyara::Route.print_routes
6
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationController < Nyara::Controller
2
+ set_default_layout 'layouts/application'
3
+ end
@@ -0,0 +1,5 @@
1
+ class WelcomeController < ApplicationController
2
+ get '/' do
3
+ render 'welcome/index'
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
5
+ <title><%= app_name %></title>
6
+ <link type="text/css" rel="stylesheet" href="/css/app.css">
7
+ <script type="text/javascript" src="/js/app.js"></script>
8
+ </head>
9
+ <body>
10
+ <%%== yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1 @@
1
+ Welcome to use Nyara.
@@ -0,0 +1,34 @@
1
+ require 'bundler'
2
+ Bundler.require :default, ENV['NYARA_ENV'] || 'development'
3
+
4
+ configure do
5
+ set :env, ENV['NYARA_ENV'] || 'development'
6
+
7
+ set :views, 'app/views'
8
+
9
+ set :session, :name, '_aaa'
10
+
11
+ ## If you've configured https with nginx:
12
+ # set :session, :secure, true
13
+
14
+ ## Default session expires when browser closes.
15
+ ## If you need time-based expiration, 30 minutes for example:
16
+ # set :session, :expires, 30 * 60
17
+
18
+ # Routing
19
+ map '/', 'welcome'
20
+
21
+ # Application loading order
22
+ set :app_files, %w|
23
+ app/controllers/application_controller.rb
24
+ app/{helpers,models,controllers}/**/*.rb
25
+ |
26
+
27
+ # Environment specific configure at last
28
+ require_relative env
29
+ end
30
+
31
+ # Configure Mongoid
32
+ Mongoid.load!(Nyara.config.project_path('config/database.yml'), Nyara.config.env)
33
+
34
+ Nyara.load_app
@@ -0,0 +1,4 @@
1
+ require_relative "application"
2
+
3
+ Nyara.setup
4
+ Nyara.start_server
@@ -0,0 +1,5 @@
1
+ configure do
2
+ set :port, 3000
3
+ set :logger, true
4
+ set :public, 'public'
5
+ end
@@ -0,0 +1,8 @@
1
+ configure do
2
+ set :port, 3000
3
+ ## worker number can be detected by CPU count
4
+ # set :workers, 4
5
+ set :logger, true
6
+
7
+ # todo after_fork
8
+ end
@@ -0,0 +1,2 @@
1
+ configure do
2
+ end
@@ -0,0 +1 @@
1
+ /* Put You css in here */
@@ -0,0 +1 @@
1
+ // Put you Javascript in here
@@ -0,0 +1,9 @@
1
+ configure do
2
+ set :env, 'test'
3
+ end
4
+ require_relative "../config/application"
5
+ require "rspec/autorun"
6
+
7
+ RSpec.configure do |config|
8
+ # your configure here
9
+ end
data/lib/nyara/test.rb CHANGED
@@ -49,7 +49,7 @@ module Nyara
49
49
  # merge Set-Cookie
50
50
  response.set_cookies.each do |cookie_seg|
51
51
  # todo distinguish delete, value and set
52
- Ext.parse_url_encoded_seg cookie, cookie_seg, false
52
+ ParamHash.parse_cookie cookie, cookie_seg
53
53
  end
54
54
  end
55
55
 
@@ -83,7 +83,7 @@ module Nyara
83
83
  Session.encode_to_cookie session, cookie
84
84
  headers['Cookie'] = Cookie.encode cookie
85
85
 
86
- request_data = ["#{meth.upcase} #{Ext.escape path, true} HTTP/1.1\r\n"]
86
+ request_data = ["#{meth.to_s.upcase} #{Ext.escape path, true} HTTP/1.1\r\n"]
87
87
  headers.each do |k, v|
88
88
  request_data << "#{k}: #{v}\r\n"
89
89
  end
@@ -97,6 +97,14 @@ module Nyara
97
97
  @_env ||= Env.new
98
98
  end
99
99
 
100
+ # #### Call-seq
101
+ #
102
+ # http :trace, '/'
103
+ #
104
+ def http *xs
105
+ env.http *xs
106
+ end
107
+
100
108
  # #### Call-seq
101
109
  #
102
110
  # get '/', headers
data/lib/nyara/view.rb CHANGED
@@ -1,7 +1,4 @@
1
1
  module Nyara
2
- module Renderable
3
- end
4
-
5
2
  # A support class which provides:
6
3
  #
7
4
  # - layout / locals for rendering
@@ -25,27 +22,104 @@ module Nyara
25
22
  # Path extension (without dot) => stream friendly
26
23
  ENGINE_STREAM_FRIENDLY = ParamHash.new
27
24
 
25
+ # meth name => method obj
26
+ RENDER = {}
27
+
28
+ # nested level => layout render, 0 means no layout
29
+ LAYOUT = {}
30
+
28
31
  autoload :ERB, File.join(__dir__, "view_handlers/erb")
29
32
  autoload :Erubis, File.join(__dir__, "view_handlers/erubis")
30
33
  autoload :Haml, File.join(__dir__, "view_handlers/haml")
31
34
  autoload :Slim, File.join(__dir__, "view_handlers/slim")
32
35
 
33
36
  class Buffer < Array
37
+ def initialize parent=nil
38
+ @parent = parent
39
+ end
40
+ attr_reader :parent
41
+
34
42
  alias safe_append= <<
35
43
 
36
44
  def append= thingy
37
45
  self << CGI.escape_html(thingy.to_s)
38
46
  end
39
47
 
48
+ alias _join join
40
49
  def join
41
50
  r = super
42
51
  clear
43
52
  r
44
53
  end
54
+
55
+ def push_level
56
+ Buffer.new self
57
+ end
58
+
59
+ def pop_level
60
+ @parent << _join
61
+ end
62
+
63
+ def flush instance
64
+ parents = [self]
65
+ buf = self
66
+ while buf = buf.parent
67
+ parents << buf
68
+ end
69
+ parents.reverse_each do |buf|
70
+ instance.send_chunk buf._join
71
+ buf.clear
72
+ end
73
+ end
74
+ end
75
+
76
+ module Renderable
77
+ def self.make_render_method file, line, sig, src
78
+ class_eval <<-RUBY, file, line
79
+ def render#{sig}
80
+ #{src}
81
+ end
82
+ RUBY
83
+ instance_method :render
84
+ end
85
+
86
+ def self.make_layout_method nested_level=0
87
+ sig = 'e'
88
+ src = "e.call locals"
89
+ nested_level.times do |i|
90
+ sig << ", e#{i}"
91
+ src = "e#{i}.call{ #{src} }"
92
+ end
93
+ sig = "#{sig}"
94
+ class_eval <<-RUBY
95
+ def layout #{sig}, locals
96
+ #{src}
97
+ end
98
+ RUBY
99
+ instance_method(:layout).bind self
100
+ end
101
+ end
102
+
103
+ class TiltRenderable
104
+ def initialize tilt
105
+ @tilt = tilt
106
+ end
107
+
108
+ def bind instance
109
+ @instance = instance
110
+ self
111
+ end
112
+
113
+ def call locals=nil, &p
114
+ inst = @instance
115
+ @instance = nil
116
+ @tilt.render inst, locals, &p
117
+ end
45
118
  end
46
119
 
47
120
  class << self
48
121
  def init
122
+ RENDER.delete_if{|k, v| k.start_with?('!') }
49
123
  @root = Config['views']
50
124
  @meth2ext = {} # meth => ext (without dot)
51
125
  @meth2sig = {}
@@ -58,22 +132,18 @@ module Nyara
58
132
  attr_reader :root
59
133
 
60
134
  # NOTE: `path` needs extension
61
- def on_delete path
135
+ def on_removed path
62
136
  meth = path2meth path
63
- Renderable.class_eval do
64
- undef meth
65
- end
137
+ RENDER.delete meth
66
138
 
67
139
  @meth2ext.delete meth
68
140
  @meth2sig.delete meth
69
141
  end
70
142
 
71
- def on_delete_all
143
+ def on_removed_all
72
144
  meths = @meth2sig
73
- Renderable.class_eval do
74
- meths.each do |meth, _|
75
- undef meth
76
- end
145
+ meths.each do |meth, _|
146
+ RENDER.delete meth
77
147
  end
78
148
  @meth2sig.clear
79
149
  @meth2ext.clear
@@ -84,7 +154,7 @@ module Nyara
84
154
  # #### Returns
85
155
  #
86
156
  # dot_ext for further use
87
- def on_update path
157
+ def on_modified path
88
158
  meth = path2meth path
89
159
  return unless @meth2sig[meth] # has not been searched before, see also View.template
90
160
 
@@ -98,21 +168,14 @@ module Nyara
98
168
  sig = @meth2sig[meth].map{|k| "#{k}: nil" }.join ','
99
169
  sig = '_={}' if sig.empty?
100
170
  sig = "(#{sig})" # 2.0.0-p0 requirement
101
- Renderable.class_eval <<-RUBY, path, 0
102
- def render#{sig}
103
- #{src}
104
- end
105
- alias :#{meth.inspect} render
106
- RUBY
171
+ RENDER[meth] = Renderable.make_render_method path, 0, sig, src
107
172
  else
108
173
  t = Dir.chdir @root do
109
174
  # todo display template error
110
175
  Tilt.new path rescue return
111
176
  end
112
177
  # partly precompiled
113
- Renderable.send :define_method, meth do |locals=nil, &p|
114
- t.render self, locals, &p
115
- end
178
+ RENDER[meth] = TiltRenderable.new t
116
179
  end
117
180
 
118
181
  @meth2ext[meth] = ext
@@ -126,22 +189,16 @@ module Nyara
126
189
  line = 1
127
190
 
128
191
  if stream_friendly
129
- Renderable.class_eval <<-RUBY, __FILE__, __LINE__
130
- def render locals={}
131
- @_nyara_locals = locals
132
- src = locals.map{|k, _| "\#{k} = @_nyara_locals[:\#{k}];" }.join
133
- src << View.precompile(#{ext.inspect}){ @_nyara_view.in }
134
- instance_eval src, #{file}, #{line}
135
- end
136
- alias :#{meth.inspect} render
192
+ RENDER[meth] = Renderable.make_render_method __FILE__, __LINE__, '(locals={})', <<-RUBY
193
+ @_nyara_locals = locals
194
+ src = locals.map{|k, _| "\#{k} = @_nyara_locals[:\#{k}];" }.join
195
+ src << View.precompile(#{ext.inspect}){ @_nyara_view.in }
196
+ instance_eval src, #{file}, #{line}
137
197
  RUBY
138
198
  ENGINE_STREAM_FRIENDLY[ext] = true
139
199
  else
140
- Renderable.class_eval <<-RUBY, __FILE__, __LINE__
141
- def render locals=nil
142
- Tilt[#{ext.inspect}].new(#{file}, #{line}){ @_nyara_view.in }.render self, locals
143
- end
144
- alias :#{meth.inspect} render
200
+ RENDER[meth] = Renderable.make_render_method __FILE__, __LINE__, '(locals=nil)', <<-RUBY
201
+ Tilt[#{ext.inspect}].new(#{file}, #{line}){ @_nyara_view.in }.render self, locals
145
202
  RUBY
146
203
  end
147
204
  ENGINE_DEFAULT_CONTENT_TYPES[ext] = default_content_type
@@ -151,7 +208,7 @@ module Nyara
151
208
  #
152
209
  # #### Returns
153
210
  #
154
- # `[meth, ext_without_dot]`
211
+ # `[meth_obj, ext_without_dot]`
155
212
  def template path, locals={}
156
213
  if File.extname(path).empty?
157
214
  Dir.chdir @root do
@@ -165,12 +222,12 @@ module Nyara
165
222
 
166
223
  meth = path2meth path
167
224
  ext = @meth2ext[meth]
168
- return [meth, ext] if ext
225
+ return [RENDER[meth], ext] if ext
169
226
 
170
227
  @meth2sig[meth] = locals.keys
171
- ext = on_update path
228
+ ext = on_modified path
172
229
  raise "template not found or not valid in Tilt: #{path}" unless ext
173
- [meth, ext]
230
+ [RENDER[meth], ext]
174
231
  end
175
232
 
176
233
  # private
@@ -192,11 +249,11 @@ module Nyara
192
249
  end
193
250
 
194
251
  def path2meth path
195
- "!!#{path}"
252
+ "!#{path}"
196
253
  end
197
254
 
198
255
  def engine2meth engine
199
- "!:#{engine}"
256
+ ":#{engine}"
200
257
  end
201
258
  end
202
259
 
@@ -225,59 +282,52 @@ module Nyara
225
282
  unless @deduced_content_type = ENGINE_DEFAULT_CONTENT_TYPES[ext]
226
283
  raise ArgumentError, "unkown template engine: #{ext.inspect}"
227
284
  end
228
-
229
- @layouts = [[meth, ext]]
230
285
  else
231
286
  raise ArgumentError, "too many options, expected only 1: #{opts.inspect}" if opts.size > 1
232
- ext, template = opts.first
233
- meth = View.engine2meth ext
287
+ ext, @in = opts.first
234
288
 
235
289
  unless @deduced_content_type = ENGINE_DEFAULT_CONTENT_TYPES[ext]
236
290
  raise ArgumentError, "unkown template engine: #{ext.inspect}"
237
291
  end
238
292
 
239
- @layouts = [meth]
240
- @in = template
293
+ meth = RENDER[View.engine2meth(ext)]
241
294
  end
242
295
 
296
+ @args = [meth.bind(instance)]
243
297
  unless layout.is_a?(Array)
244
298
  layout = layout ? [layout] : []
245
299
  end
246
300
  layout.each do |l|
247
301
  pair = View.template(l)
248
302
  # see notes on View
249
- raise "can not use #{meth} as layout" unless ENGINE_STREAM_FRIENDLY[pair[1]]
250
- @layouts << pair
303
+ raise "can not use #{pair[1]} as layout" unless ENGINE_STREAM_FRIENDLY[pair[1]]
304
+ @args << pair.first.bind(instance)
251
305
  end
252
306
 
253
- @locals = locals
307
+ @layout_render = (LAYOUT[@args.size] ||= Renderable.make_layout_method(@args.size - 1))
308
+ @args << locals
309
+
254
310
  @instance = instance
255
311
  @instance.instance_variable_set :@_nyara_view, self
256
312
  @out = Buffer.new
257
313
  end
258
314
  attr_reader :deduced_content_type, :in, :out
259
315
 
260
- def render
261
- @rest_layouts = @layouts.dup
262
- @instance.send_chunk _render
263
- Fiber.yield :term_close
316
+ def partial
317
+ @out = @out.push_level
318
+ res = @layout_render.call *@args
319
+ @out = @out.pop_level
320
+ res
264
321
  end
265
322
 
266
- def _render # :nodoc:
267
- t, _ = @rest_layouts.pop
268
- if @rest_layouts.empty?
269
- @instance.send t, @locals
270
- else
271
- @instance.send t do
272
- _render
273
- end
274
- end
323
+ def render
324
+ @instance.send_chunk @layout_render.call *@args
325
+ Fiber.yield :term_close
275
326
  end
276
327
 
277
328
  def stream
278
- @rest_layouts = @layouts.dup
279
329
  @fiber = Fiber.new do
280
- @rest_result = _render
330
+ @rest_result = @layout_render.call *@args
281
331
  nil
282
332
  end
283
333
  self
@@ -287,8 +337,7 @@ module Nyara
287
337
  r = @fiber.resume
288
338
  Fiber.yield r if r
289
339
  unless @out.empty?
290
- @instance.send_chunk @out.join
291
- @out.clear
340
+ @out.flush @instance
292
341
  end
293
342
  end
294
343