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
data/lib/nyara/command.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'optparse'
1
2
  module Nyara
2
3
  module Command
3
4
  extend self
@@ -9,9 +10,6 @@ module Nyara
9
10
  commands:
10
11
  nyara help\t\t\tShow this message
11
12
  nyara new APP_NAME\t\tTo initialize a new project with default template in current directory.
12
- options:
13
- -f\t\t\tForce override if same name path existed
14
-
15
13
  nyara version\t\t\tDisplay current version
16
14
  )
17
15
  end
@@ -20,23 +18,33 @@ commands:
20
18
  puts "Nyara #{Nyara::VERSION}"
21
19
  end
22
20
 
23
- def new_project(name = nil,*args)
21
+ def new_project(*args)
24
22
  args ||= []
25
- force_create = args.include?("-f")
23
+ opts = {
24
+ force: false
25
+ }
26
+ OptionParser.new do |opt|
27
+ opt.banner = 'Usage: nyara new APP_NAME [options]'
28
+ opt.on('-f', 'Force override old') do
29
+ opts[:force] = true
30
+ end
31
+ end.parse(args)
32
+
26
33
  require 'fileutils'
27
34
  require "erb"
28
35
  require 'ostruct'
29
36
  require_relative "view_handlers/erb"
30
37
 
38
+ name = args.shift
31
39
  if name.blank?
32
40
  puts "Need project name: \n\tnyara new xxx"
33
41
  return
34
42
  end
35
43
 
36
- app_dir = File.join(Dir.pwd,name)
37
- templte_dir = File.join(File.dirname(__FILE__),"templates")
44
+ app_dir = File.join(Dir.pwd, name)
45
+ templte_dir = File.join(File.dirname(__FILE__), "templates")
38
46
 
39
- FileUtils.rm_rf(app_dir) if force_create
47
+ FileUtils.rm_rf(app_dir) if opts[:force]
40
48
 
41
49
  if Dir.exist?(app_dir)
42
50
  puts "This has same dir name's '#{name}' existed, Nyara can not override it."
@@ -46,8 +54,9 @@ commands:
46
54
  Dir.mkdir(app_dir)
47
55
 
48
56
  puts "Generate Nyara project..."
49
- # Copy source template
50
- FileUtils.cp_r(Dir.glob("#{templte_dir}/*"),app_dir)
57
+ source_templates = Dir.glob("#{templte_dir}/*")
58
+ puts source_templates.map{|f| File.basename f }
59
+ FileUtils.cp_r(source_templates, app_dir)
51
60
 
52
61
  # render template
53
62
  files = Dir.glob("#{app_dir}/**/*")
@@ -56,13 +65,31 @@ commands:
56
65
  }
57
66
  files.each do |fname|
58
67
  if not File.directory?(fname)
59
- render_template(fname,render_opts)
68
+ render_template(fname, render_opts)
69
+ end
70
+ end
71
+
72
+ Dir.chdir app_dir do
73
+ puts "config/session.key"
74
+ File.open 'config/session.key', 'wb' do |f|
75
+ f << Session.generate_key
76
+ end
77
+
78
+ puts ".gitignore"
79
+ File.open '.gitignore', 'w' do |f|
80
+ f.puts ".DS_Store"
81
+ f.puts "config/session.key"
60
82
  end
61
83
  end
62
84
 
63
85
  puts "Enjoy!"
64
86
  end
65
87
 
88
+ def run_server(*args)
89
+ args ||= []
90
+ system("bundle exec ruby config/boot.rb")
91
+ end
92
+
66
93
  private
67
94
  def render_template(fname, opts = {})
68
95
  renderer = ERB.new(File.read(fname))
@@ -70,7 +97,7 @@ commands:
70
97
  app_name: opts[:app_name],
71
98
  nyara_version: Nyara::VERSION
72
99
  }
73
- File.open(fname,'w+') do |f|
100
+ File.open(fname, 'w+') do |f|
74
101
  f.write renderer.result(OpenStruct.new(locals).instance_eval { binding })
75
102
  end
76
103
  end
data/lib/nyara/config.rb CHANGED
@@ -54,16 +54,14 @@ module Nyara
54
54
  self['root'] = File.expand_path self['root']
55
55
 
56
56
  self['views'] = project_path(self['views'] || 'views')
57
- self['public'] = project_path(self['public'] || 'public')
57
+ if self['public']
58
+ self['public'] = project_path(self['public'])
59
+ end
58
60
 
59
61
  self.logger = create_logger
60
62
 
61
- assert !self['before_fork'] or self['before_fork'].respond_to?('call')
62
- assert !self['after_fork'] or self['after_fork'].respond_to?('call')
63
-
64
- if self['public']
65
- map '/', PublicController
66
- end
63
+ assert !self['before_fork'] || self['before_fork'].respond_to?('call')
64
+ assert !self['after_fork'] || self['after_fork'].respond_to?('call')
67
65
  end
68
66
 
69
67
  attr_accessor :logger
@@ -73,11 +71,8 @@ module Nyara
73
71
  l = self['logger']
74
72
 
75
73
  if l == true or l.nil?
76
- # see Nyara.summary_request
77
- Ext.summary_request true if development?
78
74
  ::Logger.new(production? ? project_path('production.log') : STDOUT)
79
75
  elsif l.is_a?(Class)
80
- Ext.summary_request true if development?
81
76
  l.new(production? ? project_path('production.log') : STDOUT)
82
77
  elsif l.is_a?(Proc)
83
78
  l.call
@@ -140,6 +135,10 @@ module Nyara
140
135
  self['env'].to_s
141
136
  end
142
137
 
138
+ def root
139
+ self['root'].to_s
140
+ end
141
+
143
142
  def development?
144
143
  e = env
145
144
  e.empty? or e == 'development'
@@ -179,4 +178,5 @@ configure do
179
178
  set 'env', 'development'
180
179
  set 'views', 'views'
181
180
  set 'public', 'public'
181
+ set 'root', Dir.pwd
182
182
  end
@@ -1,8 +1,4 @@
1
1
  module Nyara
2
- # Contain render methods
3
- module Renderable
4
- end
5
-
6
2
  Controller = Struct.new :request
7
3
  class Controller
8
4
  module ClassMethods
@@ -15,9 +11,12 @@ module Nyara
15
11
  def http method, path, &blk
16
12
  @routes ||= []
17
13
  @used_ids = {}
14
+ method = method.to_s.upcase
18
15
 
19
16
  action = Route.new
20
- action.http_method = HTTP_METHODS[method]
17
+ unless action.http_method = HTTP_METHODS[method]
18
+ raise ArgumentError, "missing http method: #{method.inspect}"
19
+ end
21
20
  action.path = path
22
21
  action.set_accept_exts @formats
23
22
  action.id = @curr_id if @curr_id
@@ -132,8 +131,6 @@ module Nyara
132
131
  class_eval <<-RUBY
133
132
  def __nyara_tmp_action *xs
134
133
  #{senders.join}
135
- rescue Exception => e
136
- handle_error e
137
134
  end
138
135
  alias :#{e.id.inspect} __nyara_tmp_action
139
136
  undef __nyara_tmp_action
@@ -141,7 +138,7 @@ module Nyara
141
138
 
142
139
  e.compile self, scope
143
140
  e.validate
144
- @path_templates[e.id] = e.path_template
141
+ @path_templates[e.id] = [e.path_template, e.http_method_override]
145
142
  end
146
143
  @routes
147
144
  end
@@ -149,8 +146,6 @@ module Nyara
149
146
  attr_accessor :path_templates
150
147
  end
151
148
 
152
- include Renderable
153
-
154
149
  def self.inherited klass
155
150
  # note: klass will also have this inherited method
156
151
 
@@ -169,17 +164,62 @@ module Nyara
169
164
  end
170
165
  end
171
166
 
167
+ def self.dispatch request, instance, args
168
+ if cookie_str = request.header._aref('Cookie')
169
+ ParamHash.parse_cookie request.cookie, cookie_str
170
+ end
171
+ request.flash = Flash.new(
172
+ request.session = Session.decode(request.cookie)
173
+ )
174
+
175
+ if instance
176
+ if l = Nyara.logger
177
+ l.info "#{request.http_method} #{request.path} => #{instance.class}"
178
+ end
179
+ instance.send *args
180
+ return
181
+ elsif request.http_method == 'GET' and Config['public']
182
+ path = Config.public_path request.path
183
+ if File.file?(path)
184
+ if l = Nyara.logger
185
+ l.info "GET #{path} => public 200"
186
+ end
187
+ instance = Controller.new request
188
+ instance.send_file path
189
+ return
190
+ end
191
+ end
192
+
193
+ if l = Nyara.logger
194
+ l.info "#{request.http_method} #{request.path} => 404"
195
+ end
196
+ Ext.request_send_data request, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
197
+ Fiber.yield :term_close
198
+
199
+ rescue Exception
200
+ instance.handle_error($!) if instance
201
+ end
202
+
172
203
  # Path helper
173
204
  def path_to id, *args
174
205
  if args.last.is_a?(Hash)
175
206
  opts = args.pop
176
207
  end
177
208
 
178
- r = self.class.path_templates[id.to_s] % args
209
+ template, meth = self.class.path_templates[id.to_s]
210
+ r = template % args
179
211
 
180
212
  if opts
181
213
  format = opts.delete :format
182
214
  r << ".#{format}" if format
215
+ if meth and !opts.key?(:_method) and !opts.key?('_method')
216
+ opts['_method'] = meth
217
+ end
218
+ elsif meth
219
+ opts = {'_method' => meth}
220
+ end
221
+
222
+ if opts
183
223
  r << '?' << opts.to_query unless opts.empty?
184
224
  end
185
225
  r
@@ -210,7 +250,7 @@ module Nyara
210
250
  raise "unsupported redirect status: #{status}" unless HTTP_REDIRECT_STATUS.include?(status)
211
251
 
212
252
  r = request
213
- header = r.header
253
+ header = r.response_header
214
254
  self.status status
215
255
 
216
256
  uri = URI.parse url_or_path
@@ -219,7 +259,7 @@ module Nyara
219
259
  uri.port = request.port
220
260
  end
221
261
  uri.scheme = r.ssl? ? 'https' : 'http'
222
- r.header['Location'] = uri.to_s
262
+ header['Location'] = uri.to_s
223
263
 
224
264
  # similar to send_header, but without content-type
225
265
  Ext.request_send_data r, HTTP_STATUS_FIRST_LINES[r.status]
@@ -484,6 +524,12 @@ module Nyara
484
524
  Fiber.yield :sleep # see event.c for the handler
485
525
  end
486
526
 
527
+ # Render a template as string
528
+ def partial view_path, locals: nil
529
+ view = View.new self, view_path, nil, nil, {}
530
+ view.partial
531
+ end
532
+
487
533
  # One shot render, and terminate the action.
488
534
  #
489
535
  # #### Call-seq
@@ -545,7 +591,7 @@ module Nyara
545
591
  def handle_error e
546
592
  if l = Nyara.logger
547
593
  l.error "#{e.class}: #{e.message}"
548
- l.error e.backtrace
594
+ l.error e.backtrace.join "\n"
549
595
  end
550
596
  status 500
551
597
  send_header rescue nil
data/lib/nyara/cookie.rb CHANGED
@@ -14,7 +14,7 @@ module Nyara
14
14
  def decode header
15
15
  res = ParamHash.new
16
16
  if data = header['Cookie']
17
- Ext.parse_cookie res, data
17
+ ParamHash.parse_cookie res, data
18
18
  end
19
19
  res
20
20
  end
@@ -35,30 +35,8 @@ module Nyara
35
35
  # config['a', 'very', 'deep', 'key'] = value
36
36
  #
37
37
  # All intermediate level ConfigHashes are created automatically
38
- def []= *keys, last_key, value
39
- h = self
40
- keys.each do |key|
41
- if h.has_key?(key)
42
- if h.is_a?(ConfigHash)
43
- h = h._aref key
44
- else
45
- h = h[key]
46
- end
47
- else
48
- new_h = ConfigHash.new
49
- if h.is_a?(ConfigHash)
50
- h._aset key, new_h
51
- else
52
- h[key] = new_h
53
- end
54
- h = new_h
55
- end
56
- end
57
- if h.is_a?(ConfigHash)
58
- h._aset last_key, value
59
- else
60
- h[last_key] = value
61
- end
38
+ def []= *keys, value
39
+ nested_aset keys.map(&:to_s), value
62
40
  end
63
41
  end
64
42
  end
data/lib/nyara/nyara.rb CHANGED
@@ -28,9 +28,6 @@ require_relative "cpu_counter"
28
28
  require_relative "part"
29
29
  require_relative "command"
30
30
 
31
- # default controllers
32
- require_relative "controllers/public_controller"
33
-
34
31
  module Nyara
35
32
  HTTP_STATUS_FIRST_LINES = Hash[HTTP_STATUS_CODES.map{|k,v|[k, "HTTP/1.1 #{k} #{v}\r\n".freeze]}].freeze
36
33
 
@@ -80,6 +77,24 @@ module Nyara
80
77
  View.init
81
78
  end
82
79
 
80
+ def load_app
81
+ Dir.chdir Config.root do
82
+ if Config.development?
83
+ require_relative "reload"
84
+ Reload.init do
85
+ # NOTE app_files can be an array
86
+ Dir.glob(Config['app_files']).uniq.each do |file|
87
+ Reload.load_file Config.project_path file
88
+ end
89
+ end
90
+ else
91
+ Dir.glob Config['app_files'] do |file|
92
+ require Config.project_path file
93
+ end
94
+ end
95
+ end
96
+ end
97
+
83
98
  def start_server
84
99
  port = Config[:port]
85
100
 
@@ -105,13 +120,6 @@ module Nyara
105
120
  require_relative "patches/tcp_socket"
106
121
  end
107
122
 
108
- def summary_request method_num, path, controller
109
- if l = logger
110
- method = HTTP_METHODS.find{|k, v| method_num == v}.first
111
- l.info "#{method} #{path} => #{controller}"
112
- end
113
- end
114
-
115
123
  def start_development_server port
116
124
  trap :INT do
117
125
  exit!
@@ -210,15 +218,21 @@ module Nyara
210
218
  def spawn_new_master sig
211
219
  fork do
212
220
  @server.close_on_exec = false
213
- Dir.chdir START_CTX[:cwd]
214
- if File.executable?(START_CTX[0])
215
- exec START_CTX[0], *START_CTX[:argv], close_others: false
216
- else
217
- # gemset env should be correct because env is inherited
218
- require "rbconfig"
219
- ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
220
- exec ruby, START_CTX[0], *START_CTX[:argv], close_others: false
221
- end
221
+ reload_all
222
+ end
223
+ end
224
+
225
+ # Reload everything
226
+ def reload_all
227
+ # todo set 1-1024 close_on_exec
228
+ Dir.chdir START_CTX[:cwd]
229
+ if File.executable?(START_CTX[0])
230
+ exec START_CTX[0], *START_CTX[:argv], close_others: false
231
+ else
232
+ # gemset env should be correct because env is inherited
233
+ require "rbconfig"
234
+ ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
235
+ exec ruby, START_CTX[0], *START_CTX[:argv], close_others: false
222
236
  end
223
237
  end
224
238
 
data/lib/nyara/part.rb CHANGED
@@ -100,19 +100,22 @@ module Nyara
100
100
  end
101
101
 
102
102
  # NOTE `[` are `]` are escaped in url-encoded, so should not split before decode
103
- keys = name.sub(/\]$/, '').split(/\]\[|\[/)
103
+ keys = ParamHash.split_name(name)
104
+
104
105
  if self['filename']
105
- Ext.param_hash_nested_aset params, keys, self
106
+ params.nested_aset keys, self
106
107
  elsif self['type']
107
108
  warn "looks like bad part: #{self['header'].inspect}"
108
109
  else
109
- Ext.param_hash_nested_aset params, keys, CGI.unescape(self['data'])
110
+ params.nested_aset keys, CGI.unescape(self['data'])
110
111
  end
111
112
  end
112
113
 
113
114
  # #### Params
114
115
  #
115
116
  # - `raw` in binary encoding
117
+ #
118
+ # NOTE close connection on error
116
119
  def update raw
117
120
  case self['mechanism']
118
121
  when 'base64'
@@ -148,6 +151,7 @@ module Nyara
148
151
  end
149
152
  end
150
153
 
154
+ # NOTE close connection on error
151
155
  def final
152
156
  case self['mechanism']
153
157
  when 'base64'