Class: Bijou::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/bijou/context.rb

Overview

When a Bijou file is loaded by the processor, each component that is encountered causes a Bijou::Component class to be instantiated in the context. This context can then be executed with a set of input arguments, causing each component to be evaluated and the output to be rendered. A context may be used to invoke the same arrangement of components any number of times with different arguments.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, environment = nil) ⇒ Context

Returns a new instance of Context.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/bijou/context.rb', line 173

def initialize(config, environment=nil)
  if !environment 
    environment = Environment.new
    environment.trace_level = config.trace_level
    environment.trace_buffer = config.trace_buffer
  end
  
  @args = {}
  @stack = Stack.new
  @config = config
  @environment = environment
  @container_callback = nil
  @component_callback = nil

  @request = nil
  @response = nil

  @cgi = nil
end

Instance Attribute Details

#cgiObject

Returns the value of attribute cgi.



169
170
171
# File 'lib/bijou/context.rb', line 169

def cgi
  @cgi
end

#component_callbackObject

Returns the value of attribute component_callback.



163
164
165
# File 'lib/bijou/context.rb', line 163

def component_callback
  @component_callback
end

#configObject (readonly)

Returns the value of attribute config.



166
167
168
# File 'lib/bijou/context.rb', line 166

def config
  @config
end

#container_callbackObject

Returns the value of attribute container_callback.



164
165
166
# File 'lib/bijou/context.rb', line 164

def container_callback
  @container_callback
end

#environmentObject (readonly)

Returns the value of attribute environment.



167
168
169
# File 'lib/bijou/context.rb', line 167

def environment
  @environment
end

#requestObject

Returns the value of attribute request.



170
171
172
# File 'lib/bijou/context.rb', line 170

def request
  @request
end

#responseObject

Returns the value of attribute response.



171
172
173
# File 'lib/bijou/context.rb', line 171

def response
  @response
end

#stackObject (readonly)

Returns the value of attribute stack.



165
166
167
# File 'lib/bijou/context.rb', line 165

def stack
  @stack
end

Instance Method Details

#add_component(component) ⇒ Object



233
234
235
236
237
238
239
240
241
# File 'lib/bijou/context.rb', line 233

def add_component(component)
  @stack.push_frame(component)

  # If the owner registered a container load handler, invoke after the
  # component has been added.
  if component.class.container && @container_callback
    @container_callback.call(self, component.class.container)
  end
end

#argument_exception(method, name) ⇒ Object



398
399
400
401
# File 'lib/bijou/context.rb', line 398

def argument_exception(method, name)
  # BUGBUG: We need to print the page (component) name.
  raise "Expected argument '#{name}' to method '#{method}'"
end

#cache_filenameObject

Used to retrieve the last active frame in the event of an exception.



225
226
227
228
229
230
231
# File 'lib/bijou/context.rb', line 225

def cache_filename
  if @environment.component
    @environment.component.class.cache_filename
  else
    nil
  end
end

#call_next(extra = {}) ⇒ Object

Container modules render their callers by calling the content method once at the location where the caller’s output should be rendered.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/bijou/context.rb', line 297

def call_next(extra={})
  if @stack.index == 0
    raise 'next called too many times'
  end

  # Flush the first part of the component's output to the stack's buffer.
  @stack.flush_frame

  prev_component = @environment.component

  # REVIEW: Argument merge and override is experimental.
  # Render the child content.
  component = @stack.next_frame.component
  @environment.component = component
  component.render(@args.merge(extra))
  @environment.component = prev_component

  # The remainder of the content will be flushed at the end of render.
  # @stack.flush_frame
end

#clearObject



211
212
213
# File 'lib/bijou/context.rb', line 211

def clear
  @stack.flush
end

#fetch_nextObject



326
327
328
# File 'lib/bijou/context.rb', line 326

def fetch_next
  try_fetch_next || raise('fetch_next cannot find next component')
end

#fetch_remainderObject



338
339
340
# File 'lib/bijou/context.rb', line 338

def fetch_remainder
  try_fetch_remainder || raise('fetch_remainder cannot find next component')
end

#get_logObject



413
414
415
# File 'lib/bijou/context.rb', line 413

def get_log()
  @environment.get_log()
end

#get_traceObject



427
428
429
# File 'lib/bijou/context.rb', line 427

def get_trace()
  @environment.get_trace()
end

#invoke(name, args) ⇒ Object

Used to invoke a method or a component. The output is rendered to the buffer. Returns true if a match was found.



383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/bijou/context.rb', line 383

def invoke(name, args)
  if name == 'content'
    # REVIEW: This is an experimental feature. An alternative (without 
    # argument support) is <%content>, but it is an unclosed tag.
    return call_next(args)
  end

  if text = sinvoke(name, args)
    write text
    return true
  end

  false
end

#log(level, str) ⇒ Object

Logs with carriage return.



404
405
406
# File 'lib/bijou/context.rb', line 404

def log(level, str)
  @environment.log(level, str)
end

#log_(level, str) ⇒ Object

Logs without carriage return.



409
410
411
# File 'lib/bijou/context.rb', line 409

def log_(level, str)
  @environment.log_(level, str)
end

#outputObject



207
208
209
# File 'lib/bijou/context.rb', line 207

def output
  @stack.buffer
end

#render(args) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/bijou/context.rb', line 243

def render(args)
  trace Bijou::Log::Info, "render init"

  prev_component = @environment.component

  # Init may render, after the headers but before the page text.
  @stack.frames.each { |frame|
    @environment.component = frame.component
    frame.component.init(args)
  }

  @environment.component = prev_component

  # Render the component chain to the response target.
  render_component(args)

  trace Bijou::Log::Info, "render fini"

  # Fini may still render to the stream, at the end after the page text.
  @stack.frames.each { |frame|
    @environment.component = frame.component
    frame.component.fini()
  }

  @environment.component = prev_component

  return @output
end

#render_component(args) ⇒ Object

The render method renders the component chain.



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/bijou/context.rb', line 273

def render_component(args)
  if @stack.frames.length == 0
    return
  end

  # Set top-level args so they are available to all components.
  @args = args

  # Start with the outer-most container at the end of the list.
  @stack.start

  prev_component = @environment.component

  component = @stack.top_frame.component
  @environment.component = component
  component.render(@args)
  @environment.component = prev_component

  # This flushes the component's output to the stack's buffer.
  @stack.flush_frame
end

#resetObject

Called after clone



194
195
196
197
# File 'lib/bijou/context.rb', line 194

def reset()
  @args = {}
  @stack = Stack.new
end

#sinvoke(name, args) ⇒ Object

Used to invoke a method or a component. The output is rendered to a string, which is returned as the result. If no match is found returns nil.



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/bijou/context.rb', line 344

def sinvoke(name, args)
  # Does this look like a method? We don't include setters, etc. (?!=).
  if name =~ /[_a-z][_a-z0-9]*/
    prev_component = @environment.component

    # Do any of the components support this method?
    @stack.frames.each { |f|
      if f.component.respond_to?(name)
        @stack.flush_frame
        @environment.component = f.component
        m = f.component.method(name)
        m.call(args)
        @environment.component = prev_component
        return @stack.top_frame.flush
      end
    }

    @environment.component = prev_component
  end
  
  # Delegate to context owner
  if @component_callback
    # The subcontext wraps a new stack for the component, but shares 
    # common data. This allows us to encapsulate the rendered output
    # into a separate buffer chain.
    subcontext = self.clone
    subcontext.reset

    # TODO: Handle new context and buffer merging.
    @component_callback.call(subcontext, name, args)

    return subcontext.stack.flush
  end

  nil
end

#source_filenameObject

Used to retrieve the last active frame in the event of an exception.



216
217
218
219
220
221
222
# File 'lib/bijou/context.rb', line 216

def source_filename
  if @environment.component
    @environment.component.class.source_filename
  else
    nil
  end
end

#trace(level, str) ⇒ Object

Trace with carriage return.



418
419
420
# File 'lib/bijou/context.rb', line 418

def trace(level, str)
  @environment.trace(level, str)
end

#trace_(level, str) ⇒ Object

Trace without carriage return.



423
424
425
# File 'lib/bijou/context.rb', line 423

def trace_(level, str)
  @environment.trace_(level, str)
end

#try_fetch_nextObject



318
319
320
321
322
323
324
# File 'lib/bijou/context.rb', line 318

def try_fetch_next
  if @stack.index == 0
    nil
  else
    @stack.peek_frame
  end
end

#try_fetch_remainderObject



330
331
332
333
334
335
336
# File 'lib/bijou/context.rb', line 330

def try_fetch_remainder
  if @stack.index == 0
    nil
  else
    @stack.frames[0, @stack.index].reverse
  end
end

#write(str) ⇒ Object



199
200
201
# File 'lib/bijou/context.rb', line 199

def write(str)
  @stack.output << str
end

#writeline(str) ⇒ Object



203
204
205
# File 'lib/bijou/context.rb', line 203

def writeline(str)
  @stack.output << str + "\n"
end