Class: Hatetepe::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/hatetepe/builder.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ Builder

Returns a new instance of Builder.



21
22
23
24
25
26
27
28
# File 'lib/hatetepe/builder.rb', line 21

def initialize(&block)
  reset
  @on_write, @on_complete, @on_error = [], [], []
  
  if block
    block.arity == 0 ? instance_eval(&block) : block.call(self)
  end
end

Instance Attribute Details

#stateObject (readonly)

Returns the value of attribute state.



19
20
21
# File 'lib/hatetepe/builder.rb', line 19

def state
  @state
end

Class Method Details

.build(&block) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
# File 'lib/hatetepe/builder.rb', line 7

def self.build(&block)
  message = ""
  builder = new do |b|
    b.on_write {|data| message << data }
  end
  
  block.arity == 0 ? builder.instance_eval(&block) : block.call(builder)
  
  builder.complete
  return message.empty? ? nil : message
end

Instance Method Details

#body(body) ⇒ Object



120
121
122
123
# File 'lib/hatetepe/builder.rb', line 120

def body(body)
  body.each {|c| body_chunk c }
  # XXX complete here?
end

#body_chunk(chunk) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/hatetepe/builder.rb', line 125

def body_chunk(chunk)
  if ready?
    error "A request or response line and headers are required before writing body"
  elsif writing_trailing_headers?
    error "Cannot write body after trailing headers"
  elsif writing_headers?
    if @chunked.nil?
      header "Transfer-Encoding", "chunked"
    end
    write "\r\n"
    @state = :writing_body
  end
  
  chunk = chunk.to_s
  if chunked?
    write "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n"
  else
    write chunk unless chunk.empty?
  end
end

#chunked?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/hatetepe/builder.rb', line 59

def chunked?
  @chunked
end

#completeObject



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/hatetepe/builder.rb', line 146

def complete
  if ready?
    return
  elsif writing_headers? && @chunked.nil?
    header "Content-Length", "0"
  end
  body_chunk ""

  on_complete.each {|blk| blk.call }
  reset
end

#error(message) ⇒ Object



162
163
164
165
166
167
168
169
# File 'lib/hatetepe/builder.rb', line 162

def error(message)
  exception = BuilderError.new(message)
  unless on_error.empty?
    on_error.each {|blk| blk.call exception }
  else
    raise exception
  end
end

#header(name, value) ⇒ Object



93
94
95
96
# File 'lib/hatetepe/builder.rb', line 93

def header(name, value)
  value = String(value)
  raw_header "#{name}: #{value}" unless value.empty?
end

#headers(hash) ⇒ Object



98
99
100
# File 'lib/hatetepe/builder.rb', line 98

def headers(hash)
  hash.each {|k, v| header k, v }
end

#raw_header(header) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/hatetepe/builder.rb', line 102

def raw_header(header)
  if ready?
    error "A request or response line is required before writing headers"
  elsif writing_body?
    error "Trailing headers require chunked transfer encoding" unless chunked?
    write "0\r\n"
    @state = :writing_trailing_headers
  end
  
  if @chunked.nil? && header[0..13] == "Content-Length"
    @chunked = false
  elsif @chunked.nil? && header[0..16] == "Transfer-Encoding"
    @chunked = true
  end
  
  write "#{header}\r\n"
end

#ready?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/hatetepe/builder.rb', line 43

def ready?
  state == :ready
end

#request(req) ⇒ Object



63
64
65
66
67
68
# File 'lib/hatetepe/builder.rb', line 63

def request(req)
  request_line req[0], req[1], (req[4] || "1.1")
  headers req[2]
  body req[3] if req[3]
  complete
end

#request_line(verb, uri, version = "1.1") ⇒ Object



70
71
72
73
74
# File 'lib/hatetepe/builder.rb', line 70

def request_line(verb, uri, version = "1.1")
  complete unless ready?
  write "#{verb.upcase} #{uri} HTTP/#{version}\r\n"
  @state = :writing_headers
end

#resetObject



30
31
32
33
# File 'lib/hatetepe/builder.rb', line 30

def reset
  @state = :ready
  @chunked = nil
end

#response(res) ⇒ Object



76
77
78
79
80
81
# File 'lib/hatetepe/builder.rb', line 76

def response(res)
  response_line res[0], (res[3] || "1.1")
  headers res[1]
  body res[2] if res[2]
  complete
end

#response_line(code, version = "1.1") ⇒ Object



83
84
85
86
87
88
89
90
91
# File 'lib/hatetepe/builder.rb', line 83

def response_line(code, version = "1.1")
  complete unless ready?
  unless status = Rack::Utils::HTTP_STATUS_CODES[code]
    error "Unknown status code: #{code}"
  end
  
  write "HTTP/#{version} #{code} #{status}\r\n"
  @state = :writing_headers
end

#write(data) ⇒ Object



158
159
160
# File 'lib/hatetepe/builder.rb', line 158

def write(data)
  on_write.each {|blk| blk.call data }
end

#writing_body?Boolean

Returns:

  • (Boolean)


51
52
53
# File 'lib/hatetepe/builder.rb', line 51

def writing_body?
  state == :writing_body
end

#writing_headers?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'lib/hatetepe/builder.rb', line 47

def writing_headers?
  state == :writing_headers
end

#writing_trailing_headers?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/hatetepe/builder.rb', line 55

def writing_trailing_headers?
  state == :writing_trailing_headers
end