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

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.
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