Class: SC::Rack::Builder
- Inherits:
-
Object
- Object
- SC::Rack::Builder
- Defined in:
- lib/sproutcore/rack/builder.rb
Overview
A Rack application for serving dynamically-built SproutCore projects. Most of the time you will use this application as part of the sc-server command to dynamically build your SproutCore project while you develop it.
If you are deploying some Ruby-based infrastructure in your production environment, you could also use this application to dynamically build new versions of your SproutCore apps when you deploy them. This would allow you to potentially bypass the pre-deployment build step using sc-build.
While this model is supported by the Rack adaptor, it is generally recommended that you instead build you app without using this adaptor since the build step will help catch possible errors in your code before you go live with your project. Sometimes, however, dynamically building content is useful, and that is what this adaptor is for.
Using This Application
When you instantiate a builder, you must provide one or more projects that contain the resources you want to load. Each incoming request url will be mapped to an entriy in a project manifest. The entry is then built and the resulting file returned. Once a file has been built, it will not be rebuilt unless the source file it represents has changed.
In addition to dynamically building entries, the Builder can also forwards requests onto an SC::Rack::Proxy app to handle proxies requests.
Config Settings
This app respects several options that you can name in your config file (in addition to proxy configs), that can affect the app performance. Normally reasonable defaults for these settings are built into the SproutCore buildfile, but you may choose to override them if you are deploying into a production environment.
:reload_project:: If set to true, then the builder will reload the
projects to look for changed files before servicing incoming
requests. You will generally want this option while working in
debug mode, but you may want to disable it for production, since it
can slow down performance.
:use_cached_headers:: If set to true, then the builder will return
static assets with an "Expires: <10-years>" header attached. This
will yield excellent performance in production systems but it may
interfere with loading the most recent copies of files when in
development mode.
:combine_javascript:: If set, the generated html will reference a
combined version of the javascript for elgible targets. This will
yield better performance in production, but slows down load time in
development mode.
:combine_stylesheets:: Ditto to combine_javascript
Constant Summary collapse
- ONE_YEAR =
used to set expires header.
365 * 24 * 60 * 60
Instance Attribute Summary collapse
-
#project ⇒ Object
readonly
Returns the value of attribute project.
Instance Method Summary collapse
-
#call(env) ⇒ Object
Main entry point for this Rack application.
-
#initialize(project) ⇒ Builder
constructor
When you create a new builder, pass in one or more projects you want the builder to monitor for changes.
Constructor Details
#initialize(project) ⇒ Builder
When you create a new builder, pass in one or more projects you want the builder to monitor for changes.
76 77 78 79 |
# File 'lib/sproutcore/rack/builder.rb', line 76 def initialize(project) @project = project @last_reload_time = Time.now end |
Instance Attribute Details
#project ⇒ Object (readonly)
Returns the value of attribute project.
151 152 153 |
# File 'lib/sproutcore/rack/builder.rb', line 151 def project @project end |
Instance Method Details
#call(env) ⇒ Object
Main entry point for this Rack application. Returns 404 if no matching entry could be found in the project.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/sproutcore/rack/builder.rb', line 83 def call(env) # define local variables so they will survive the mutext contexts # below... ret = url = target = language = cacheable = manifest = entry = nil build_path = nil project_mutex.synchronize do reload_project! # if needed # collect some standard info url = env['PATH_INFO'] # look for a matching target target = target_for(url) ret = not_found("No matching target") if target.nil? # normalize url to resolve to entry & extract the language if ret.nil? url, language, cacheable = normalize_url(url, target) ret = not_found("Target requires language") if language.nil? end # lookup manifest if ret.nil? language = language.to_s.downcase.to_sym # normalize manifest = target.manifest_for(:language => language).build! # lookup entry by url unless entry = manifest.entries.find { |e| e.url == url } ret = not_found("No matching entry in target") end end if ret.nil? # Clean the entry so it will rebuild if we are serving an html # file entry.clean! if [:html, :test].include?(entry.entry_type) # Now build entry and return a file object build_path = entry.build!.build_path end # Update last reload time. This way if any other requests are # waiting, they won't rebuild their manifest. @last_reload_time = Time.now end return ret unless ret.nil? unless File.file?(build_path) && File.readable?(build_path) return not_found("File could not build (entry: #{entry.filename} - build_path: #{build_path}") end SC.logger.info "Serving #{target.target_name.to_s.sub(/^\//,'')}:#{entry.filename}" # define response headers file_size = File.size(build_path) headers = { "Last-Modified" => File.mtime(build_path).httpdate, "Etag" => File.mtime(build_path).to_i.to_s, "Content-Type" => mime_type(build_path), "Content-Length" => file_size.to_s, "Expires" => (cacheable ? (Time.now + ONE_YEAR) : Time.now).httpdate } [200, headers, File.open(build_path, 'rb')] end |