Class: Harbor::Response

Inherits:
Object
  • Object
show all
Defined in:
lib/harbor/response.rb

Direct Known Subclasses

Test::Response

Constant Summary collapse

NOT_MODIFIED_OMIT_HEADERS =

Headers that MUST NOT be included with 304 Not Modified responses.

tools.ietf.org/html/rfc2616#section-10.3.5

%w[
  Allow
  Content-Encoding
  Content-Language
  Content-Length
  Content-MD5
  Content-Type
  Last-Modified
].to_set

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request) ⇒ Response

Returns a new instance of Response.



9
10
11
12
13
14
15
# File 'lib/harbor/response.rb', line 9

def initialize(request)
  @request = request
  @headers = {}
  @headers["Content-Type"] = "text/html"
  @status = 200
  @errors = Harbor::Errors.new
end

Instance Attribute Details

#errorsObject

Returns the value of attribute errors.



7
8
9
# File 'lib/harbor/response.rb', line 7

def errors
  @errors
end

#headersObject

Returns the value of attribute headers.



7
8
9
# File 'lib/harbor/response.rb', line 7

def headers
  @headers
end

#statusObject

Returns the value of attribute status.



7
8
9
# File 'lib/harbor/response.rb', line 7

def status
  @status
end

Instance Method Details

#[](key) ⇒ Object



219
220
221
# File 'lib/harbor/response.rb', line 219

def [](key)
  headers[key]
end

#[]=(key, value) ⇒ Object



223
224
225
# File 'lib/harbor/response.rb', line 223

def []=(key, value)
  headers[key] = value
end

#abort!(code) ⇒ Object



160
161
162
163
164
165
166
167
# File 'lib/harbor/response.rb', line 160

def abort!(code)
  if Harbor::View.exists?("exceptions/#{code}.html.erb")
    render "exceptions/#{code}.html.erb"
  end

  self.status = code
  throw(:abort_request)
end

#bufferObject



51
52
53
54
55
56
57
# File 'lib/harbor/response.rb', line 51

def buffer
  if @io.is_a?(StringIO)
    @io.string
  else
    @io || ""
  end
end

#cache(key, last_modified, ttl = nil, max_age = nil) {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:

Raises:

  • (ArgumentError)


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
# File 'lib/harbor/response.rb', line 90

def cache(key, last_modified, ttl = nil, max_age = nil)
  raise ArgumentError.new("You must provide a block of code to cache.") unless block_given?

  store = nil
  if key && (ttl || max_age)
    store = Harbor::View.cache

    unless store
      raise ArgumentError.new("Cache Store Not Defined. Please set Harbor::View.cache to your desired cache store.")
    end

    key = "page-#{key}"
  end

  last_modified = last_modified.httpdate
  @headers["Last-Modified"] = last_modified
  @headers["Cache-Control"] = "max-age=#{ttl}, must-revalidate" if ttl

  modified_since = @request.env["HTTP_IF_MODIFIED_SINCE"]

  if modified_since == last_modified && (!store || store.get(key))
    not_modified!
  elsif store && item = store.get(key)
    return puts(item.content)
  end

  yield self
  store.put(key, buffer, ttl, max_age) if store
end

#content_typeObject



37
38
39
# File 'lib/harbor/response.rb', line 37

def content_type
  @headers["Content-Type"]
end

#content_type=(content_type) ⇒ Object



33
34
35
# File 'lib/harbor/response.rb', line 33

def content_type=(content_type)
  @headers["Content-Type"] = content_type
end

#flushObject



21
22
23
# File 'lib/harbor/response.rb', line 21

def flush
  @io = nil
end

#inspectObject



196
197
198
# File 'lib/harbor/response.rb', line 196

def inspect
  "<#{self.class} headers=#{headers.inspect} content_type=#{content_type.inspect} status=#{status.inspect} body=#{buffer.inspect}>"
end

#message(key, message) ⇒ Object



204
205
206
# File 'lib/harbor/response.rb', line 204

def message(key, message)
  messages[key] = message
end

#messagesObject



200
201
202
# File 'lib/harbor/response.rb', line 200

def messages
  @messages ||= @request.messages
end

#not_modified!Object



190
191
192
193
194
# File 'lib/harbor/response.rb', line 190

def not_modified!
  NOT_MODIFIED_OMIT_HEADERS.each { |name| headers.delete(name) }
  self.status = 304
  throw(:abort_request)
end


46
47
48
49
# File 'lib/harbor/response.rb', line 46

def print(value)
  string.print(value)
  self.size = string.length
end

#puts(value) ⇒ Object



41
42
43
44
# File 'lib/harbor/response.rb', line 41

def puts(value)
  string.puts(value)
  self.size = string.length
end

#redirect(url, params = {}) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/harbor/response.rb', line 142

def redirect(url, params = {})
  if @request && !@request.session? && !messages.empty? && !messages.expired?
    messages.each { |key, value| params["messages[#{key}]"] = value }
  end

  self.status = 303
  self.headers = {
    "Location" => (params && params.any? ? "#{url}?#{Rack::Utils::build_query(params)}" : url),
    "Content-Type" => "text/html"
  }
  self.flush
  self
end

#redirect!(url, params = nil) ⇒ Object



156
157
158
# File 'lib/harbor/response.rb', line 156

def redirect!(url, params = nil)
  redirect(url, params) and throw(:abort_request)
end

#render(view, context = {}) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/harbor/response.rb', line 120

def render(view, context = {})
  if context[:layout].is_a?(Array)
    warn "Passing multiple layouts to response.render has been deprecated. See Harbor::Layouts."
    context[:layout] = context[:layout].first
  end

  case view
  when View
    view.context.merge(context)
  else
    view = View.new(view, context.merge({ :request => @request, :response => self }))
  end

  self.content_type = view.content_type

  if context.has_key?(:layout) || @request.xhr?
    puts view.to_s(context[:layout])
  else
    puts view.to_s(:search)
  end
end

#send_file(name, path_or_io, content_type = nil) ⇒ Object



74
75
76
77
78
79
# File 'lib/harbor/response.rb', line 74

def send_file(name, path_or_io, content_type = nil)
  stream_file(path_or_io, content_type)

  @headers["Content-Disposition"] = "attachment; filename=\"#{escape_filename_for_http_header(name)}\""
  nil
end

#send_files(name, files) ⇒ Object

Zip up the files (with no compression) and send it the client files should be an enumerable collection of Harbor::File instances



83
84
85
86
87
88
# File 'lib/harbor/response.rb', line 83

def send_files(name, files)
  @io = ZippedIO.new(files)
  self.size = @io.size
  self.content_type = "application/zip"
  @headers["Content-Disposition"] = "attachment; filename=\"#{escape_filename_for_http_header(name)}\""
end

Raises:

  • (ArgumentError)


227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/harbor/response.rb', line 227

def set_cookie(key, value)
  raise ArgumentError.new("+key+ must not be blank") if key.nil? || key.size == 0

  case value
  when Hash
    domain    = "; domain="  + value[:domain]    if value[:domain]
    path      = "; path="    + value[:path]      if value[:path]
    http_only = value[:http_only] ? "; HTTPOnly=" : nil
    # According to RFC 2109, we need dashes here.
    # N.B.: cgi.rb uses spaces...
    expires_on = if (defined?(DateTime) && value[:expires].is_a?(DateTime))
      value[:expires].clone.new_offset(0)
    elsif value[:expires].is_a?(Time)
      value[:expires].clone.gmtime
    elsif value[:expires].nil?
      nil
    else
      raise ArgumentError.new("The value hash for set_cookie contains an invalid +expires+ attribute, Time or DateTime expected, but got: #{value[:expires].inspect}")
    end
    expires = "; expires=" + expires_on.strftime("%a, %d-%b-%Y %H:%M:%S GMT") if expires_on

    value = value[:value]
  end
  value = [value]  unless Array === value
  cookie = Rack::Utils.escape(key) + "=" +
    value.map { |v| Rack::Utils.escape v }.join("&") +
    "#{domain}#{path}#{expires}#{http_only}"

  case self["Set-Cookie"]
  when Array
    self["Set-Cookie"] << cookie
  when String
    self["Set-Cookie"] = [self["Set-Cookie"], cookie]
  when nil
    self["Set-Cookie"] = cookie
  end
end

#sizeObject



29
30
31
# File 'lib/harbor/response.rb', line 29

def size
  (@headers["Content-Length"] || buffer.size).to_i
end

#size=(size) ⇒ Object



25
26
27
# File 'lib/harbor/response.rb', line 25

def size=(size)
  @headers["Content-Length"] = size.to_s
end

#stream_file(path_or_io, content_type = nil) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/harbor/response.rb', line 59

def stream_file(path_or_io, content_type = nil)
  io = BlockIO.new(path_or_io)
  content_type ||= Harbor::Mime.mime_type(::File.extname(io.path.to_s))

  if io.path && @request.env.has_key?("HTTP_X_SENDFILE_TYPE")
    @headers["X-Sendfile"] = io.path
  else
    @io = io
  end
 
  self.size = io.size
  self.content_type = content_type
  nil
end

#to_aObject



208
209
210
211
212
213
214
215
216
217
# File 'lib/harbor/response.rb', line 208

def to_a
  messages.clear if messages.expired?

  if @request.session?
    session = @request.session
    set_cookie(session.key, session.save)
  end

  [self.status, self.headers, self.buffer]
end

#unauthorizedObject



169
170
171
# File 'lib/harbor/response.rb', line 169

def unauthorized
  self.status = 401
end

#unauthorized!Object



173
174
175
# File 'lib/harbor/response.rb', line 173

def unauthorized!
  unauthorized and throw(:abort_request)
end