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