lib/nyara/controller.rb in nyara-0.0.1.pre.5 vs lib/nyara/controller.rb in nyara-0.0.1.pre.6
- old
+ new
@@ -13,11 +13,11 @@
action = RouteEntry.new
action.http_method = HTTP_METHODS[method]
action.path = path
action.set_accept_exts @formats
- action.id = @curr_id.to_sym if @curr_id
+ action.id = @curr_id if @curr_id
action.blk = blk
@route_entries << action
if @curr_id
raise ArgumentError, "action id #{@curr_id} already in use" if @used_ids[@curr_id]
@@ -43,11 +43,11 @@
end
if tag
# todo scan class
id = tag[/\#\w++(\-\w++)*/]
- @curr_id = id
+ @curr_id = id.to_sym
end
if opts
# todo add opts: strong param, etag, cache-control
@formats = opts[:formats]
@@ -79,12 +79,12 @@
# HTTP PATCH
def patch path, &blk
http 'PATCH', path, &blk
end
- # HTTP OPTIONS
- # todo generate options response for a url
+ # HTTP OPTIONS<br>
+ # todo generate options response for a url<br>
# see http://tools.ietf.org/html/rfc5789
def options path, &blk
http 'OPTIONS', path, &blk
end
@@ -102,12 +102,11 @@
def set_name n
@controller_name = n
end
attr_reader :controller_name
- # :nodoc:
- def preprocess_actions
+ def compile_route_entries scope # :nodoc:
raise "#{self}: no action defined" unless @route_entries
curr_id = :'#0'
next_id = proc{
while @used_ids[curr_id]
@@ -116,72 +115,131 @@
@used_ids[curr_id] = true
curr_id
}
next_id[]
+ @path_templates = {}
@route_entries.each do |e|
- e.id ||= next_id[]
+ e.id = next_id[] if e.id.empty?
define_method e.id, &e.blk
+ e.compile self, scope
+ e.validate
+ @path_templates[e.id] = e.path_template
end
@route_entries
end
+
+ attr_accessor :path_templates
end
include Renderable
- # :nodoc:
def self.inherited klass
# klass will also have this inherited method
# todo check class name
klass.extend ClassMethods
- [:@route_entries, :@usred_ids, :@default_layout].each do |iv|
+ [:@used_ids, :@default_layout].each do |iv|
klass.instance_variable_set iv, klass.superclass.instance_variable_get(iv)
end
+
+ route_entries = klass.superclass.instance_variable_get :@route_entries
+ if route_entries
+ route_entries.map! {|e| e.dup }
+ klass.instance_variable_set :@route_entries, route_entries
+ end
end
# Path helper
- def path_for id, *args
+ def path_to id, *args
if args.last.is_a?(Hash)
opts = args.pop
end
- r = Route.path_template(self.class, id) % args
+ r = self.class.path_templates[id.to_s] % args
if opts
- r << ".#{opts[:format]}" if opts[:format]
- query = opts.map do |k, v|
- next if k == :format
- "#{CGI.escape k.to_s}=#{CGI.escape v}"
- end
- query.compact!
- r << '?' << query.join('&') unless query.empty?
+ format = opts.delete :format
+ r << ".#{format}" if format
+ r << '?' << opts.to_query unless opts.empty?
end
r
end
- # Url helper
- # NOTE: host can include port
- def url_for id, *args, scheme: nil, host: Config['host'], **opts
+ # Url helper<br>
+ # NOTE: host string can include port number<br>
+ # TODO: user and password?
+ def url_to id, *args, scheme: nil, host: nil, **opts
scheme = scheme ? scheme.sub(/\:?$/, '://') : '//'
- host ||= 'localhost'
- path = path_for id, *args, opts
+ host ||= request.host_with_port
+ path = path_to id, *args, opts
scheme << host << path
end
+ # Redirect to a url or path, terminates action<br>
+ # +status+ can be one of:
+ #
+ # - 300 multiple choices (e.g. offer different languages)
+ # - 301 moved permanently
+ # - 302 found (default)
+ # - 303 see other (e.g. for results of cgi-scripts)
+ # - 307 temporary redirect
+ #
+ # Caveats: there's no content in a redirect response yet, if you want one, you can configure nginx to add it
+ def redirect url_or_path, status=302
+ status = status.to_i
+ raise "unsupported redirect status: #{status}" unless HTTP_REDIRECT_STATUS.include?(status)
+
+ r = request
+ header = r.header
+ self.status status
+
+ uri = URI.parse url_or_path
+ if uri.host.nil?
+ uri.host = request.domain
+ uri.port = request.port
+ end
+ uri.scheme = r.ssl? ? 'https' : 'http'
+ r.header['Location'] = uri.to_s
+
+ # similar to send_header, but without content-type
+ Ext.request_send_data r, HTTP_STATUS_FIRST_LINES[r.status]
+ data = header.serialize
+ data.concat r.response_header_extra_lines
+ data << "\r\n"
+ Ext.request_send_data r, data.join
+
+ Fiber.yield :term_close
+ end
+
+ # Shortcut for +redirect url_to *xs+
+ def redirect_to *xs
+ redirect url_to(*xs)
+ end
+
+ # Request extension or generated by `Accept`
def format
request.format
end
+ # Request header<br>
+ # NOTE to change response header, use +set_header+
def header
request.header
end
alias headers header
- def set_header k, v
- request.response_header[k] = v
+ # Set response header
+ def set_header field, value
+ request.response_header[field] = value
end
+ # Append an extra line in reponse header
+ #
+ # :call-seq:
+ #
+ # add_header_line "X-Myheader: here we are"
+ #
def add_header_line h
raise 'can not modify sent header' if request.response_header.frozen?
h = h.sub /(?<![\r\n])\z/, "\r\n"
request.response_header_extra_lines << h
end
@@ -237,10 +295,14 @@
def session
request.session
end
+ def flash
+ request.flash
+ end
+
# Set response status
def status n
raise ArgumentError, "unsupported status: #{n}" unless HTTP_STATUS_FIRST_LINES[n]
Ext.request_set_status request, n
end
@@ -266,18 +328,19 @@
template_deduced_content_type ||
'text/html'
header.reverse_merge! OK_RESP_HEADER
- data = header.map do |k, v|
- "#{k}: #{v}\r\n"
- end
+ data = header.serialize
data.concat r.response_header_extra_lines
+ data << Session.encode_set_cookie(r.session, r.ssl?)
data << "\r\n"
Ext.request_send_data r, data.join
# forbid further modification
header.freeze
+ session.freeze
+ flash.next.freeze
end
# Send raw data, that is, not wrapped in chunked encoding<br>
# NOTE: often you should call send_header before doing this.
def send_data data