mirror of
				https://gitea.invidious.io/iv-org/shard-kemal.git
				synced 2024-08-15 00:53:36 +00:00 
			
		
		
		
	Refactor helpers into module namespaces
This commit is contained in:
		
							parent
							
								
									aaa2109837
								
							
						
					
					
						commit
						1cd329b92f
					
				
					 11 changed files with 357 additions and 177 deletions
				
			
		|  | @ -4,6 +4,7 @@ private def handle(request, fallthrough = true) | ||||||
|   io = IO::Memory.new |   io = IO::Memory.new | ||||||
|   response = HTTP::Server::Response.new(io) |   response = HTTP::Server::Response.new(io) | ||||||
|   context = HTTP::Server::Context.new(request, response) |   context = HTTP::Server::Context.new(request, response) | ||||||
|  |   context.app = Kemal.application | ||||||
|   handler = Kemal::StaticFileHandler.new "#{__DIR__}/static", fallthrough |   handler = Kemal::StaticFileHandler.new "#{__DIR__}/static", fallthrough | ||||||
|   handler.call context |   handler.call context | ||||||
|   response.close |   response.close | ||||||
|  |  | ||||||
|  | @ -1,2 +1,2 @@ | ||||||
| require "./kemal/base" | require "./kemal/base" | ||||||
| require "./kemal/dsl" | require "./kemal/dsl" | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | require "./helpers/*" | ||||||
|  | 
 | ||||||
| # Kemal Base | # Kemal Base | ||||||
| # The DSL currently consists of | # The DSL currently consists of | ||||||
| # - get post put patch delete options | # - get post put patch delete options | ||||||
|  | @ -5,6 +7,10 @@ | ||||||
| # - before_* | # - before_* | ||||||
| # - error | # - error | ||||||
| class Kemal::Base | class Kemal::Base | ||||||
|  |   include FileHelpers | ||||||
|  |   include Templates | ||||||
|  |   include Macros | ||||||
|  | 
 | ||||||
|   HTTP_METHODS   = %w(get post put patch delete options) |   HTTP_METHODS   = %w(get post put patch delete options) | ||||||
|   FILTER_METHODS = %w(get post put patch delete options all) |   FILTER_METHODS = %w(get post put patch delete options all) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -51,7 +51,11 @@ module Kemal | ||||||
| 
 | 
 | ||||||
|     def serve_static?(key) |     def serve_static?(key) | ||||||
|       config = @serve_static |       config = @serve_static | ||||||
|       config.try(&.[key]?) || config == true |       (config.is_a?(Hash) && config[key]?) || false | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def extra_options(&@extra_options : OptionParser ->) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| # - WebSocket(ws) | # - WebSocket(ws) | ||||||
| # - before_* | # - before_* | ||||||
| # - error | # - error | ||||||
|  | require "./dsl/*" | ||||||
| 
 | 
 | ||||||
| {% for method in Kemal::Base::HTTP_METHODS %} | {% for method in Kemal::Base::HTTP_METHODS %} | ||||||
|   def {{method.id}}(path, &block : HTTP::Server::Context -> _) |   def {{method.id}}(path, &block : HTTP::Server::Context -> _) | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ end | ||||||
| # Logs the output via `logger`. | # Logs the output via `logger`. | ||||||
| # This is the built-in `Kemal::LogHandler` by default which uses STDOUT. | # This is the built-in `Kemal::LogHandler` by default which uses STDOUT. | ||||||
| def log(message : String) | def log(message : String) | ||||||
|   Kemal.application.logger.write "#{message}\n" |   Kemal.application.log(message) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| # Enables / Disables logging. | # Enables / Disables logging. | ||||||
|  | @ -65,7 +65,6 @@ end | ||||||
| # ``` | # ``` | ||||||
| def logger(logger : Kemal::BaseLogHandler) | def logger(logger : Kemal::BaseLogHandler) | ||||||
|   Kemal.application.logger = logger |   Kemal.application.logger = logger | ||||||
|   Kemal.application.add_handler logger |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| # Enables / Disables static file serving. | # Enables / Disables static file serving. | ||||||
|  | @ -94,7 +93,7 @@ end | ||||||
| # end | # end | ||||||
| # ``` | # ``` | ||||||
| def headers(env : HTTP::Server::Context, additional_headers : Hash(String, String)) | def headers(env : HTTP::Server::Context, additional_headers : Hash(String, String)) | ||||||
|   env.response.headers.merge!(additional_headers) |   Kemal.application.headers(env, additional_headers) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| # Send a file with given path and base the mime-type on the file extension | # Send a file with given path and base the mime-type on the file extension | ||||||
|  | @ -110,82 +109,7 @@ end | ||||||
| # send_file env, "./path/to/file", "image/jpeg" | # send_file env, "./path/to/file", "image/jpeg" | ||||||
| # ``` | # ``` | ||||||
| def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil) | def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil) | ||||||
|   config = Kemal.config.serve_static |   Kemal.application.send_file(env, path, mime_type) | ||||||
|   file_path = File.expand_path(path, Dir.current) |  | ||||||
|   mime_type ||= Kemal::Utils.mime_type(file_path) |  | ||||||
|   env.response.content_type = mime_type |  | ||||||
|   env.response.headers["Accept-Ranges"] = "bytes" |  | ||||||
|   env.response.headers["X-Content-Type-Options"] = "nosniff" |  | ||||||
|   minsize = 860 # http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits ?? |  | ||||||
|   request_headers = env.request.headers |  | ||||||
|   filesize = File.size(file_path) |  | ||||||
|   filestat = File.info(file_path) |  | ||||||
| 
 |  | ||||||
|   Kemal.config.static_headers.try(&.call(env.response, file_path, filestat)) |  | ||||||
| 
 |  | ||||||
|   File.open(file_path) do |file| |  | ||||||
|     if env.request.method == "GET" && env.request.headers.has_key?("Range") |  | ||||||
|       next multipart(file, env) |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     condition = config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Kemal::Utils.zip_types(file_path) |  | ||||||
|     if condition && request_headers.includes_word?("Accept-Encoding", "gzip") |  | ||||||
|       env.response.headers["Content-Encoding"] = "gzip" |  | ||||||
|       Gzip::Writer.open(env.response) do |deflate| |  | ||||||
|         IO.copy(file, deflate) |  | ||||||
|       end |  | ||||||
|     elsif condition && request_headers.includes_word?("Accept-Encoding", "deflate") |  | ||||||
|       env.response.headers["Content-Encoding"] = "deflate" |  | ||||||
|       Flate::Writer.open(env.response) do |deflate| |  | ||||||
|         IO.copy(file, deflate) |  | ||||||
|       end |  | ||||||
|     else |  | ||||||
|       env.response.content_length = filesize |  | ||||||
|       IO.copy(file, env.response) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
|   return |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| private def multipart(file, env : HTTP::Server::Context) |  | ||||||
|   # See http://httpwg.org/specs/rfc7233.html |  | ||||||
|   fileb = file.size |  | ||||||
|   startb = endb = 0 |  | ||||||
| 
 |  | ||||||
|   if match = env.request.headers["Range"].match /bytes=(\d{1,})-(\d{0,})/ |  | ||||||
|     startb = match[1].to_i { 0 } if match.size >= 2 |  | ||||||
|     endb = match[2].to_i { 0 } if match.size >= 3 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   endb = fileb - 1 if endb == 0 |  | ||||||
| 
 |  | ||||||
|   if startb < endb < fileb |  | ||||||
|     content_length = 1 + endb - startb |  | ||||||
|     env.response.status_code = 206 |  | ||||||
|     env.response.content_length = content_length |  | ||||||
|     env.response.headers["Accept-Ranges"] = "bytes" |  | ||||||
|     env.response.headers["Content-Range"] = "bytes #{startb}-#{endb}/#{fileb}" # MUST |  | ||||||
| 
 |  | ||||||
|     if startb > 1024 |  | ||||||
|       skipped = 0 |  | ||||||
|       # file.skip only accepts values less or equal to 1024 (buffer size, undocumented) |  | ||||||
|       until (increase_skipped = skipped + 1024) > startb |  | ||||||
|         file.skip(1024) |  | ||||||
|         skipped = increase_skipped |  | ||||||
|       end |  | ||||||
|       if (skipped_minus_startb = skipped - startb) > 0 |  | ||||||
|         file.skip skipped_minus_startb |  | ||||||
|       end |  | ||||||
|     else |  | ||||||
|       file.skip(startb) |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     IO.copy(file, env.response, content_length) |  | ||||||
|   else |  | ||||||
|     env.response.content_length = fileb |  | ||||||
|     env.response.status_code = 200 # Range not satisfable, see 4.4 Note |  | ||||||
|     IO.copy(file, env.response) |  | ||||||
|   end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| # Send a file with given data and default `application/octet-stream` mime_type. | # Send a file with given data and default `application/octet-stream` mime_type. | ||||||
|  | @ -200,10 +124,7 @@ end | ||||||
| # send_file env, data_slice, "image/jpeg" | # send_file env, data_slice, "image/jpeg" | ||||||
| # ``` | # ``` | ||||||
| def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil) | def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil) | ||||||
|   mime_type ||= "application/octet-stream" |   Kemal.application.send_file(env, data, mime_type) | ||||||
|   env.response.content_type = mime_type |  | ||||||
|   env.response.content_length = data.bytesize |  | ||||||
|   env.response.write data |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| # Configures an `HTTP::Server::Response` to compress the response | # Configures an `HTTP::Server::Response` to compress the response | ||||||
|  | @ -211,7 +132,7 @@ end | ||||||
| # | # | ||||||
| # Disabled by default. | # Disabled by default. | ||||||
| def gzip(status : Bool = false) | def gzip(status : Bool = false) | ||||||
|   add_handler HTTP::CompressHandler.new if status |   Kemal.application.gzip(status) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| # Adds headers to `Kemal::StaticFileHandler`. This is especially useful for `CORS`. | # Adds headers to `Kemal::StaticFileHandler`. This is especially useful for `CORS`. | ||||||
							
								
								
									
										47
									
								
								src/kemal/dsl/macros.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/kemal/dsl/macros.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | def content_for_blocks | ||||||
|  |   Kemal.application.content_for_blocks | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | macro content_for(key, file = __FILE__) | ||||||
|  |   Kemal::Macros.content_for({{key}}, {{file}}) do | ||||||
|  |     {{yield}} | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | # Yields content for the given key if a `content_for` block exists for that key. | ||||||
|  | macro yield_content(key) | ||||||
|  |   Kemal::Macros.yield_content({{key}}) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | # Render view with a layout as the superview. | ||||||
|  | # | ||||||
|  | #   render "src/views/index.ecr", "src/views/layout.ecr" | ||||||
|  | # | ||||||
|  | macro render(filename, layout) | ||||||
|  |   Kemal::Macros.render({{filename}}, {{layout}}) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | # Render view with the given filename. | ||||||
|  | macro render(filename) | ||||||
|  |   Kemal::Macros.render({{filename}}) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | # Halt execution with the current context. | ||||||
|  | # Returns 200 and an empty response by default. | ||||||
|  | # | ||||||
|  | #   halt env, status_code: 403, response: "Forbidden" | ||||||
|  | macro halt(env, status_code = 200, response = "") | ||||||
|  |   Kemal::Macros.halt({{env}}, {{status_code}}, {{response}}) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | # Extends context storage with user defined types. | ||||||
|  | # | ||||||
|  | # class User | ||||||
|  | #   property name | ||||||
|  | # end | ||||||
|  | # | ||||||
|  | # add_context_storage_type(User) | ||||||
|  | # | ||||||
|  | macro add_context_storage_type(type) | ||||||
|  |   Kemal::Macros.add_context_storage_type({{type}}) | ||||||
|  | end | ||||||
							
								
								
									
										7
									
								
								src/kemal/dsl/templates.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/kemal/dsl/templates.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | def render_404 | ||||||
|  |   Kemal.application.render_404 | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | def render_500(context, backtrace, verbosity) | ||||||
|  |   Kemal.application.render_500(context, backtrace, verbosity) | ||||||
|  | end | ||||||
							
								
								
									
										136
									
								
								src/kemal/helpers/file_helpers.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/kemal/helpers/file_helpers.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | ||||||
|  | module Kemal::FileHelpers | ||||||
|  |   def log(message) | ||||||
|  |     logger.write "#{message}\n" | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Send a file with given path and base the mime-type on the file extension | ||||||
|  |   # or default `application/octet-stream` mime_type. | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # send_file env, "./path/to/file" | ||||||
|  |   # ``` | ||||||
|  |   # | ||||||
|  |   # Optionally you can override the mime_type | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # send_file env, "./path/to/file", "image/jpeg" | ||||||
|  |   # ``` | ||||||
|  |   def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil) | ||||||
|  |     config = env.app.config | ||||||
|  |     file_path = File.expand_path(path, Dir.current) | ||||||
|  |     mime_type ||= Kemal::Utils.mime_type(file_path) | ||||||
|  |     env.response.content_type = mime_type | ||||||
|  |     env.response.headers["Accept-Ranges"] = "bytes" | ||||||
|  |     env.response.headers["X-Content-Type-Options"] = "nosniff" | ||||||
|  |     minsize = 860 # http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits ?? | ||||||
|  |     request_headers = env.request.headers | ||||||
|  |     filesize = File.size(file_path) | ||||||
|  |     filestat = File.stat(file_path) | ||||||
|  | 
 | ||||||
|  |     config.static_headers.try(&.call(env.response, file_path, filestat)) | ||||||
|  | 
 | ||||||
|  |     File.open(file_path) do |file| | ||||||
|  |       if env.request.method == "GET" && env.request.headers.has_key?("Range") | ||||||
|  |         next multipart(file, env) | ||||||
|  |       end | ||||||
|  |       if request_headers.includes_word?("Accept-Encoding", "gzip") && config.serve_static?("gzip") && filesize > minsize && Kemal::Utils.zip_types(file_path) | ||||||
|  |         env.response.headers["Content-Encoding"] = "gzip" | ||||||
|  |         Gzip::Writer.open(env.response) do |deflate| | ||||||
|  |           IO.copy(file, deflate) | ||||||
|  |         end | ||||||
|  |       elsif request_headers.includes_word?("Accept-Encoding", "deflate") && config.serve_static?("gzip") && filesize > minsize && Kemal::Utils.zip_types(file_path) | ||||||
|  |         env.response.headers["Content-Encoding"] = "deflate" | ||||||
|  |         Flate::Writer.open(env.response) do |deflate| | ||||||
|  |           IO.copy(file, deflate) | ||||||
|  |         end | ||||||
|  |       else | ||||||
|  |         env.response.content_length = filesize | ||||||
|  |         IO.copy(file, env.response) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |     return | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   private def multipart(file, env : HTTP::Server::Context) | ||||||
|  |     # See http://httpwg.org/specs/rfc7233.html | ||||||
|  |     fileb = file.size | ||||||
|  | 
 | ||||||
|  |     range = env.request.headers["Range"] | ||||||
|  |     match = range.match(/bytes=(\d{1,})-(\d{0,})/) | ||||||
|  | 
 | ||||||
|  |     startb = 0 | ||||||
|  |     endb = 0 | ||||||
|  | 
 | ||||||
|  |     if match | ||||||
|  |       if match.size >= 2 | ||||||
|  |         startb = match[1].to_i { 0 } | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       if match.size >= 3 | ||||||
|  |         endb = match[2].to_i { 0 } | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     if endb == 0 | ||||||
|  |       endb = fileb - 1 | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     if startb < endb && endb < fileb | ||||||
|  |       content_length = 1 + endb - startb | ||||||
|  |       env.response.status_code = 206 | ||||||
|  |       env.response.content_length = content_length | ||||||
|  |       env.response.headers["Accept-Ranges"] = "bytes" | ||||||
|  |       env.response.headers["Content-Range"] = "bytes #{startb}-#{endb}/#{fileb}" # MUST | ||||||
|  | 
 | ||||||
|  |       if startb > 1024 | ||||||
|  |         skipped = 0 | ||||||
|  |         # file.skip only accepts values less or equal to 1024 (buffer size, undocumented) | ||||||
|  |         until skipped + 1024 > startb | ||||||
|  |           file.skip(1024) | ||||||
|  |           skipped += 1024 | ||||||
|  |         end | ||||||
|  |         if skipped - startb > 0 | ||||||
|  |           file.skip(skipped - startb) | ||||||
|  |         end | ||||||
|  |       else | ||||||
|  |         file.skip(startb) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       IO.copy(file, env.response, content_length) | ||||||
|  |     else | ||||||
|  |       env.response.content_length = fileb | ||||||
|  |       env.response.status_code = 200 # Range not satisfable, see 4.4 Note | ||||||
|  |       IO.copy(file, env.response) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def headers(env, additional_headers) | ||||||
|  |     env.response.headers.merge!(additional_headers) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Send a file with given data and default `application/octet-stream` mime_type. | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # send_file env, data_slice | ||||||
|  |   # ``` | ||||||
|  |   # | ||||||
|  |   # Optionally you can override the mime_type | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # send_file env, data_slice, "image/jpeg" | ||||||
|  |   # ``` | ||||||
|  |   def send_file(env : HTTP::Server::Context, data : Slice(UInt8), mime_type : String? = nil) | ||||||
|  |     mime_type ||= "application/octet-stream" | ||||||
|  |     env.response.content_type = mime_type | ||||||
|  |     env.response.content_length = data.bytesize | ||||||
|  |     env.response.write data | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Configures an `HTTP::Server::Response` to compress the response | ||||||
|  |   # output, either using gzip or deflate, depending on the `Accept-Encoding` request header. | ||||||
|  |   # | ||||||
|  |   # Disabled by default. | ||||||
|  |   def gzip(status : Bool = false) | ||||||
|  |     add_handler HTTP::CompressHandler.new if status | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -1,98 +1,101 @@ | ||||||
| require "kilt" | require "kilt" | ||||||
| 
 | 
 | ||||||
| CONTENT_FOR_BLOCKS = Hash(String, Tuple(String, Proc(String))).new | module Kemal::Macros | ||||||
|  |   def content_for_blocks | ||||||
|  |     @content_for_blocks ||= Hash(String, Tuple(String, Proc(String))).new | ||||||
|  |   end | ||||||
| 
 | 
 | ||||||
| # `content_for` is a set of helpers that allows you to capture |   # `content_for` is a set of helpers that allows you to capture | ||||||
| # blocks inside views to be rendered later during the request. The most |   # blocks inside views to be rendered later during the request. The most | ||||||
| # common use is to populate different parts of your layout from your view. |   # common use is to populate different parts of your layout from your view. | ||||||
| # |   # | ||||||
| # The currently supported engines are: ecr and slang. |   # The currently supported engines are: ecr and slang. | ||||||
| # |   # | ||||||
| # ## Usage |   # ## Usage | ||||||
| # |   # | ||||||
| # You call `content_for`, generally from a view, to capture a block of markup |   # You call `content_for`, generally from a view, to capture a block of markup | ||||||
| # giving it an identifier: |   # giving it an identifier: | ||||||
| # |   # | ||||||
| # ``` |   # ``` | ||||||
| # # index.ecr |   # # index.ecr | ||||||
| # <% content_for "some_key" do %> |   # <% content_for "some_key" do %> | ||||||
| #   <chunk of="html">...</chunk> |   #   <chunk of="html">...</chunk> | ||||||
| # <% end %> |   # <% end %> | ||||||
| # ``` |   # ``` | ||||||
| # |   # | ||||||
| # Then, you call `yield_content` with that identifier, generally from a |   # Then, you call `yield_content` with that identifier, generally from a | ||||||
| # layout, to render the captured block: |   # layout, to render the captured block: | ||||||
| # |   # | ||||||
| # ``` |   # ``` | ||||||
| # # layout.ecr |   # # layout.ecr | ||||||
| # <%= yield_content "some_key" %> |   # <%= yield_content "some_key" %> | ||||||
| # ``` |   # ``` | ||||||
| # |   # | ||||||
| # ## And How Is This Useful? |   # ## And How Is This Useful? | ||||||
| # |   # | ||||||
| # For example, some of your views might need a few javascript tags and |   # For example, some of your views might need a few javascript tags and | ||||||
| # stylesheets, but you don't want to force this files in all your pages. |   # stylesheets, but you don't want to force this files in all your pages. | ||||||
| # Then you can put `<%= yield_content :scripts_and_styles %>` on your |   # Then you can put `<%= yield_content :scripts_and_styles %>` on your | ||||||
| # layout, inside the <head> tag, and each view can call `content_for` |   # layout, inside the <head> tag, and each view can call `content_for` | ||||||
| # setting the appropriate set of tags that should be added to the layout. |   # setting the appropriate set of tags that should be added to the layout. | ||||||
| macro content_for(key, file = __FILE__) |   macro content_for(key, file = __FILE__) | ||||||
|   %proc = ->() { |     %proc = ->() { | ||||||
|     __kilt_io__ = IO::Memory.new |       __kilt_io__ = IO::Memory.new | ||||||
|     {{ yield }} |       {{ yield }} | ||||||
|     __kilt_io__.to_s |       __kilt_io__.to_s | ||||||
|   } |     } | ||||||
| 
 | 
 | ||||||
|   CONTENT_FOR_BLOCKS[{{key}}] = Tuple.new {{file}}, %proc |     content_for_blocks[{{key}}] = Tuple.new {{file}}, %proc | ||||||
|   nil |     nil | ||||||
| end |   end | ||||||
| 
 | 
 | ||||||
| # Yields content for the given key if a `content_for` block exists for that key. |   # Yields content for the given key if a `content_for` block exists for that key. | ||||||
| macro yield_content(key) |   macro yield_content(key) | ||||||
|   if CONTENT_FOR_BLOCKS.has_key?({{key}}) |     if content_for_blocks.has_key?({{key}}) | ||||||
|     __caller_filename__ = CONTENT_FOR_BLOCKS[{{key}}][0] |       __caller_filename__ = content_for_blocks[{{key}}][0] | ||||||
|     %proc = CONTENT_FOR_BLOCKS[{{key}}][1] |       %proc = content_for_blocks[{{key}}][1] | ||||||
|     %proc.call if __content_filename__ == __caller_filename__ |       %proc.call if __content_filename__ == __caller_filename__ | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Render view with a layout as the superview. | ||||||
|  |   # ``` | ||||||
|  |   # render "src/views/index.ecr", "src/views/layout.ecr" | ||||||
|  |   # ``` | ||||||
|  |   macro render(filename, layout) | ||||||
|  |     __content_filename__ = {{filename}} | ||||||
|  |     content = render {{filename}} | ||||||
|  |     render {{layout}} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Render view with the given filename. | ||||||
|  |   macro render(filename) | ||||||
|  |     Kilt.render({{filename}}) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Halt execution with the current context. | ||||||
|  |   # Returns 200 and an empty response by default. | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # halt env, status_code: 403, response: "Forbidden" | ||||||
|  |   # ``` | ||||||
|  |   macro halt(env, status_code = 200, response = "") | ||||||
|  |     {{env}}.response.status_code = {{status_code}} | ||||||
|  |     {{env}}.response.print {{response}} | ||||||
|  |     {{env}}.response.close | ||||||
|  |     next | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   # Extends context storage with user defined types. | ||||||
|  |   # | ||||||
|  |   # ``` | ||||||
|  |   # class User | ||||||
|  |   #   property name | ||||||
|  |   # end | ||||||
|  |   # | ||||||
|  |   # add_context_storage_type(User) | ||||||
|  |   # ``` | ||||||
|  |   macro add_context_storage_type(type) | ||||||
|  |     {{ HTTP::Server::Context::STORE_MAPPINGS.push(type) }} | ||||||
|   end |   end | ||||||
| end | end | ||||||
| 
 |  | ||||||
| # Render view with a layout as the superview. |  | ||||||
| # |  | ||||||
| # ``` |  | ||||||
| # render "src/views/index.ecr", "src/views/layout.ecr" |  | ||||||
| # ``` |  | ||||||
| macro render(filename, layout) |  | ||||||
|   __content_filename__ = {{filename}} |  | ||||||
|   content = render {{filename}} |  | ||||||
|   render {{layout}} |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| # Render view with the given filename. |  | ||||||
| macro render(filename) |  | ||||||
|   Kilt.render({{filename}}) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| # Halt execution with the current context. |  | ||||||
| # Returns 200 and an empty response by default. |  | ||||||
| # |  | ||||||
| # ``` |  | ||||||
| # halt env, status_code: 403, response: "Forbidden" |  | ||||||
| # ``` |  | ||||||
| macro halt(env, status_code = 200, response = "") |  | ||||||
|   {{env}}.response.status_code = {{status_code}} |  | ||||||
|   {{env}}.response.print {{response}} |  | ||||||
|   {{env}}.response.close |  | ||||||
|   next |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| # Extends context storage with user defined types. |  | ||||||
| # |  | ||||||
| # ``` |  | ||||||
| # class User |  | ||||||
| #   property name |  | ||||||
| # end |  | ||||||
| # |  | ||||||
| # add_context_storage_type(User) |  | ||||||
| # ``` |  | ||||||
| macro add_context_storage_type(type) |  | ||||||
|   {{ HTTP::Server::Context::STORE_MAPPINGS.push(type) }} |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| # This file contains the built-in view templates that Kemal uses. | # This file contains the built-in view templates that Kemal uses. | ||||||
| # Currently it contains templates for 404 and 500 error codes. | # Currently it contains templates for 404 and 500 error codes. | ||||||
| 
 | 
 | ||||||
|  | <<<<<<< HEAD | ||||||
| def render_404 | def render_404 | ||||||
|   <<-HTML |   <<-HTML | ||||||
|     <!DOCTYPE html> |     <!DOCTYPE html> | ||||||
|  | @ -32,4 +33,57 @@ def render_500(context, exception, verbosity) | ||||||
| 
 | 
 | ||||||
|   context.response.print template |   context.response.print template | ||||||
|   context |   context | ||||||
|  | ======= | ||||||
|  | module Kemal::Templates | ||||||
|  |   def render_404 | ||||||
|  |     template = <<-HTML | ||||||
|  |         <!DOCTYPE html> | ||||||
|  |         <html> | ||||||
|  |         <head> | ||||||
|  |           <style type="text/css"> | ||||||
|  |           body { text-align:center;font-family:helvetica,arial;font-size:22px; | ||||||
|  |             color:#888;margin:20px} | ||||||
|  |           img { max-width: 579px; width: 100%; } | ||||||
|  |           #c {margin:0 auto;width:500px;text-align:left} | ||||||
|  |           </style> | ||||||
|  |         </head> | ||||||
|  |         <body> | ||||||
|  |           <h2>Kemal doesn't know this way.</h2> | ||||||
|  |           <img src="/__kemal__/404.png"> | ||||||
|  |         </body> | ||||||
|  |         </html> | ||||||
|  |     HTML | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def render_500(context, backtrace, verbosity) | ||||||
|  |     message = if verbosity | ||||||
|  |                 "<pre>#{HTML.escape(backtrace)}</pre>" | ||||||
|  |               else | ||||||
|  |                 "<p>Something wrong with the server :(</p>" | ||||||
|  |               end | ||||||
|  | 
 | ||||||
|  |     template = <<-HTML | ||||||
|  |         <!DOCTYPE html> | ||||||
|  |         <html> | ||||||
|  |         <head> | ||||||
|  |           <style type="text/css"> | ||||||
|  |           body { text-align:center;font-family:helvetica,arial;font-size:22px; | ||||||
|  |             color:#888;margin:20px} | ||||||
|  |           #c {margin:0 auto;width:500px;text-align:left} | ||||||
|  |           pre {text-align:left;font-size:14px;color:#fff;background-color:#222; | ||||||
|  |             font-family:Operator,"Source Code Pro",Menlo,Monaco,Inconsolata,monospace; | ||||||
|  |             line-height:1.5;padding:10px;border-radius:2px;overflow:scroll} | ||||||
|  |           </style> | ||||||
|  |         </head> | ||||||
|  |         <body> | ||||||
|  |           <h2>Kemal has encountered an error. (500)</h2> | ||||||
|  |           #{message} | ||||||
|  |         </body> | ||||||
|  |         </html> | ||||||
|  |     HTML | ||||||
|  |     context.response.status_code = 500 | ||||||
|  |     context.response.print template | ||||||
|  |     context | ||||||
|  |   end | ||||||
|  | >>>>>>> Refactor helpers into module namespaces | ||||||
| end | end | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue