Module: Slinky::Server

Includes:
EM::HttpServer
Defined in:
lib/slinky/server.rb

Constant Summary collapse

INJECT_CSS =
File.read("#{File.dirname(__FILE__)}/templates/inject.css")
ERROR_CSS =
File.read("#{File.dirname(__FILE__)}/templates/error.css")
ERROR_HAML =
File.read("#{File.dirname(__FILE__)}/templates/error.haml")
ERROR_JS =
File.read("#{File.dirname(__FILE__)}/templates/error.js")

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.configObject



16
# File 'lib/slinky/server.rb', line 16

def self.config; @config || ConfigReader.empty; end

.config=(_config) ⇒ Object



15
# File 'lib/slinky/server.rb', line 15

def self.config= _config; @config = _config; end

.dirObject

Gets the root directory from which files should be served



13
# File 'lib/slinky/server.rb', line 13

def self.dir; @dir || "."; end

.dir=(_dir) ⇒ Object

Sets the root directory from which files should be served



11
# File 'lib/slinky/server.rb', line 11

def self.dir= _dir; @dir = _dir; end

.format_error_output(resp, mf, error) ⇒ Object

Produces nice error output for various kinds of formats



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/slinky/server.rb', line 68

def self.format_error_output resp, mf, error
  resp.content =
    case Pathname.new(mf.output_path).extname
    when ".html"
      Haml::Engine.new(ERROR_HAML).
        render(Object.new, errors: error.messages, css: ERROR_CSS)
    when ".css"
      resp.status = 200 # browsers ignore 500'd css
      INJECT_CSS.gsub("{REPLACE_ERRORS}",
                      error.messages.join("\n").gsub("'", "\""))
    when ".js"
      resp.status = 200 # browsers ignore 500'd js
      ERROR_JS
        .gsub("{REPLACE_CSS}",
              JSON.dump({css: ERROR_CSS.gsub("\n", "")}))
        .gsub("{REPLACE_ERRORS}", JSON.dump(error.messages))
    else
      error.message
    end
end

.handle_file(resp, mf, compile = true) ⇒ Object

Takes a manifest file and produces a response for it



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/slinky/server.rb', line 90

def self.handle_file resp, mf, compile = true
  begin
    path = mf.process(nil, compile)
    serve_file resp, path.to_s
  rescue SlinkyError => e
    resp.status = 500
    if self.config.enable_browser_errors
      format_error_output(resp, mf, e)
    else
      resp.content = e.message
    end
    e.messages.each{|m|
      $stderr.puts(m.foreground(:red))
    }
  rescue => e
    resp.status = 500
    format_error_output(resp, mf, SlinkyError.new(
                          "Unknown error handling #{mf.source}: #{$!}\n"))
    $stderr.puts("Unknown error handling #{mf.source}: #{$!}".foreground(:red))
  end
  resp
end

.manifestObject



19
# File 'lib/slinky/server.rb', line 19

def self.manifest; @manifest; end

.manifest=(_manifest) ⇒ Object



18
# File 'lib/slinky/server.rb', line 18

def self.manifest= _manifest; @manifest = _manifest; end

.not_found(resp) ⇒ Object

Returns the proper response for files that do not exist



134
135
136
137
# File 'lib/slinky/server.rb', line 134

def self.not_found resp
  resp.status = 404
  resp.content = "File not found\n"
end

.path_for_uri(uri) ⇒ Object

Splits a uri into its components, returning only the path sans initial forward slash.



23
24
25
26
# File 'lib/slinky/server.rb', line 23

def self.path_for_uri uri
  _, _, _, _, _, path, _, _  = URI.split uri
  path[1..-1] #get rid of the leading /
end

.process_path(resp, path, pushstate = false) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/slinky/server.rb', line 43

def self.process_path resp, path, pushstate = false
  file = manifest.find_by_path(path).first

  if file.is_a? ManifestDir
    file = manifest.find_by_path(path + "/index.html").first
    path += "/index.html"
  end

  resp.content_type MIME::Types.type_for(path).first.to_s

  if file
    # They're requesting the source file and we should not
    # compile it (but still process directives)
    compile = !(file.source.end_with?(path) && file.source)
    handle_file(resp, file, compile)
  elsif !pushstate && p = config.pushstate_for_path("/" + path)
    path = p[0] == "/" ? p[1..-1] : p
    self.process_path(resp, path, true)
  else
    not_found resp
  end
  resp
end

.serve_file(resp, path) ⇒ Object

Serves a file from the file system



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/slinky/server.rb', line 114

def self.serve_file resp, path
  if File.exists?(path) && !File.directory?(path)
    size = File.size(path)
    _, _, extension = path.match(EXTENSION_REGEX).to_a
    # File reading code from rack/file.rb
    File.open path do |file|
      resp.content = ""
      while size > 0
        part = file.read([8192, size].min)
        break unless part
        size -= part.length
        resp.content << part
      end
    end
  else
    not_found resp
  end
end

Instance Method Details

#process_http_requestObject

Method called for every HTTP request made



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/slinky/server.rb', line 29

def process_http_request
  resp = EventMachine::DelegatedHttpResponse.new(self)

  begin
    path = Server.path_for_uri(@http_request_uri)
  rescue
    resp.status = 500
    resp.content = "Invalid request"
    return
  end

  Server.process_path(resp, path).send_response
end