Class: RocketIO::Controller

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/rocketio-views/engine.rb,
lib/rocketio-views/layout.rb,
lib/rocketio-views/layouts.rb,
lib/rocketio-views/templates.rb,
lib/rocketio-views/controller.rb,
lib/rocketio-views/template_vars.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.define_engine_methods(source = self) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rocketio-views/engine.rb', line 59

def self.define_engine_methods source = self
  return unless engine = source.instance_variable_get(:@__engine__)
  if Proc === engine
    selfengine = allocate.engine
    api.delete define_method(:__rocketio_engine__, &engine)
    api.delete define_method(:engine) {
      engine, *engine_options = __rocketio_engine__
      return selfengine unless engine
      [RocketIO.engine_class(engine), engine_options.freeze].freeze
    }
  else
    api.delete define_method(:engine) {engine}
  end
end

.define_layout(name, file: nil, &block) ⇒ Object

Note:

files will be searched relative to controller’s dirname, that’s it, the folder controller was defined in and operates from.

Note:

when searching for file multiple extensions will be tried, that’s it, all extensions controller’s engine actually supports.

Note:

controllers that inherits named layouts will always search for files in own dirname. controllers that inherits :file layouts will search files in the original controller’s dirname.

if only name given it will search for a file with same name in controller’s dirname.

if file name differs from layout name pass it as :file option. file path should be relative to controller’s dirname. also a block accepted for :file option. the block will be executed at controllers’s instance level and should return path to layout file. file name should NOT include extension.

if a block given NO file will be searched and returned value will be used as layout.

Examples:

define :master layout.

./master.erb file will be used

define_layout :master

define :master layout.

../layouts/master.erb file will be used

define_layout :master, file: '../layouts/master'

define :master layout.

./admin file to be used when user logged in and ./user otherwise

define_layout :master, file: -> {user? ? 'admin' : 'user'}

define :master layout using a block that returns the layout string.

no file will be used.

define_layout(:master) do
  layout = Layouts.find_by(id: params[:layout_id]) || halt(400, 'Template not found')
  layout.source
end

Parameters:

  • name
  • file (defaults to: nil)
  • block


50
51
52
53
54
# File 'lib/rocketio-views/layouts.rb', line 50

def self.define_layout name, file: nil, &block
  file && block && raise(::ArgumentError, 'both file and block given, please use either one')
  (@__layouts__ ||= {})[name.to_sym] = {block: block, root: dirname, file: file, name: name}.freeze
  define_layouts_methods
end

.define_layout_methods(source = self) ⇒ Object



17
18
19
20
21
# File 'lib/rocketio-views/layout.rb', line 17

def self.define_layout_methods source = self
  return unless source.instance_variables.include?(:@__layout__)
  layout = source.instance_variable_get(:@__layout__)
  api.delete define_method(:layout) {layout}
end

.define_layouts_methods(source = self) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/rocketio-views/layouts.rb', line 56

def self.define_layouts_methods source = self
  return unless source.instance_variables.include?(:@__layouts__)
  layouts = source.instance_variable_get(:@__layouts__).each_with_object(allocate.layouts.dup) do |(name,setup),o|
    o[name] = :"__#{name}_layout__"
    if setup[:block]
      # block given, do not search for file, use returned value instead
      api.delete define_method(o[name], &setup[:block])
    elsif setup[:file]
      # file given, search the file in original controller dirname
      meth_name = :"__#{name}_layout_file__"
      meth_proc = setup[:file].is_a?(::Proc) ? setup[:file] : -> {setup[:file]}
      api.delete define_method(meth_name, &meth_proc)
      api.delete define_method(o[name]) {
        engine, * = resolve_engine
        read_template(find_template(setup[:root], __send__(meth_name), engine))
      }
    else
      # only name given, search for a file with same name in controller's dirname
      api.delete define_method(o[name]) {
        engine, * = resolve_engine
        read_template(find_template(self.dirname, setup[:name], engine))
      }
    end
  end.freeze
  api.delete define_method(:layouts) {layouts}
end

.define_template(name, file: nil, &block) ⇒ Object

Note:

files will be searched relative to controller’s dirname, that’s it, the folder controller was defined in and operates from.

Note:

when searching for file multiple extensions will be tried, that’s it, all extensions controller’s engine actually supports.

Note:

controllers that inherits named templates will always search for files in own dirname. controllers that inherits :file templates will search files in the original controller’s dirname.

if only name given it will search for a file with same name in controller’s dirname

if file name differs from template name pass it as :file option. file path should be relative to controller’s dirname. also a block accepted for :file option. the block will be executed at controllers’s instance level and should return path to template file. file name should NOT include extension.

if a block given NO file will be searched and returned value will be used as template.

Examples:

define :items template.

./items.erb file will be used
define_template :items

define :items template.

../shared_templates/items.erb file will be used
define_template :items, file: '../shared_templates/items'

define :items template.

./admin-items.erb file to be used when user logged in and ./items.erb otherwise

define_template :items, file: -> {user? ? 'admin-items' : 'items'}

define :items template using a block that returns the template string.

no file will be used.

define_template(:items) do
  template = Templates.find_by(id: params[:template_id]) || halt(400, 'Template not found')
  template.source
end

Parameters:

  • name
  • file (defaults to: nil)
  • block


48
49
50
51
52
# File 'lib/rocketio-views/templates.rb', line 48

def self.define_template name, file: nil, &block
  file && block && raise(ArgumentError, 'both file and block given, please use either one')
  (@__templates__ ||= {})[name.to_sym] = {block: block, root: dirname, file: file, name: name}.freeze
  define_templates_methods
end

.define_template_var(name, value = nil, &block) ⇒ Object

define variables to be used on templates alongside provided locals.

Parameters:

  • name
  • value (defaults to: nil)


9
10
11
12
13
# File 'lib/rocketio-views/template_vars.rb', line 9

def self.define_template_var name, value = nil, &block
  value || block || raise(ArgumentError, 'A value or a block expected')
  (@__template_vars__ ||= {})[name.to_sym] = (block || value).freeze
  define_template_vars_methods
end

.define_template_vars_methods(source = self) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/rocketio-views/template_vars.rb', line 15

def self.define_template_vars_methods source = self
  return unless source.instance_variables.include?(:@__template_vars__)
  vars = source.instance_variable_get(:@__template_vars__).each_with_object(allocate.__template_vars__.dup) do |(name,value),o|
    o[name] = :"__#{name}_template_var__"
    if value.is_a?(Proc)
      api.delete define_method(o[name], &value)
    else
      api.delete define_method(o[name]) {value}
    end
  end.freeze
  api.delete define_method(:__template_vars__) {vars}
end

.define_templates_methods(source = self) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rocketio-views/templates.rb', line 54

def self.define_templates_methods source = self
  return unless source.instance_variables.include?(:@__templates__)
  templates = source.instance_variable_get(:@__templates__).each_with_object(allocate.templates.dup) do |(name,setup),o|
    o[name] = :"__#{name}_template__"
    if setup[:block]
      # block given, do not search for file, use returned value instead
      api.delete define_method(o[name], &setup[:block])
    elsif setup[:file]
      # file given, search the file in original controller dirname
      meth_name = :"__#{name}_template_file__"
      meth_proc = setup[:file].is_a?(Proc) ? setup[:file] : -> {setup[:file]}
      api.delete define_method(meth_name, &meth_proc)
      api.delete define_method(o[name]) {
        engine, * = resolve_engine
        read_template(find_template(setup[:root], __send__(meth_name), engine))
      }
    else
      # only name given, search for a file with same name in controller's dirname
      api.delete define_method(o[name]) {
        engine, * = resolve_engine
        read_template(find_template(self.dirname, setup[:name], engine))
      }
    end
  end.freeze
  api.delete define_method(:templates) {templates}
end

.engine(engine = nil, *engine_options, &block) ⇒ Object

Note:

if a block given it will be executed at instance level and result used for engine. To have any options passed at engine initialization the block should return an array having engine as first element and options as consequent elements.

Note:

if given block returns no engine, inherited engine will be used

if no engine set, templates will be rendered using ERB engine. any engine supported by [Tilt](github.com/rtomayko/tilt) can be used. to set engine use symbolized constant name, e.g. :Slim, :Haml engine name is Case Sensitive and there should be a Tilt::ENGINETemplate class defined e.g. ‘engine :Slim` will look for Tilt::SlimTemplate and `engine RDiscount` will look for Tilt::RDiscountTemplate

Examples:

use static engine

engine :Slim

use static engine with options

engine :Slim, pretty_print: true

use dynamically set engine

engine do
  some_condition ? :SomeEngine : AnotherEngine
end

use dynamically set engine with options

engine do
  some_condition ? [:SomeEngine, :opt1, :opt2] : AnotherEngine
end

Search will use ERB when requested by a bot and Slim otherwise


class BaseController < RocketIO::Controller
  engine :Slim
end

class Search < BaseController
  engine do
    if request.user_agent =~ /i'm a bot/
      # requested by a bot, using ERB
      return :ERB
    end
    # requested by a user, returning no engine for inherited engine to be used
  end
end

Parameters:

  • engine (defaults to: nil)

    engine name.

  • *engine_options

    any arguments to be passed at engine initialization

  • block

    to be executed at instance level



54
55
56
57
# File 'lib/rocketio-views/engine.rb', line 54

def self.engine engine = nil, *engine_options, &block
  @__engine__ = block || [RocketIO.engine_class(engine), engine_options.freeze].freeze
  define_engine_methods
end

.layout(layout) ⇒ Object

Note:

to disable layout set it to false: ‘layout false`

by default templates will be rendered without layout. to make them render inside a layout use ‘layout :layout_name` at class level. to use a layout it should be defined at first (@see define_layout)

Parameters:

  • layout

    name of a defined layout



12
13
14
15
# File 'lib/rocketio-views/layout.rb', line 12

def self.layout layout
  @__layout__ = layout
  define_layout_methods
end

Instance Method Details

#__template_vars__Object



28
# File 'lib/rocketio-views/template_vars.rb', line 28

def __template_vars__; RocketIO::EMPTY_HASH end

#engineObject



74
# File 'lib/rocketio-views/engine.rb', line 74

def engine; RocketIO::DEFAULT_ENGINE end

#layoutObject

by default no layout used, so this method returns nil. controllers that uses a layout will override this method.



25
# File 'lib/rocketio-views/layout.rb', line 25

def layout; end

#layoutsObject



83
# File 'lib/rocketio-views/layouts.rb', line 83

def layouts; RocketIO::EMPTY_HASH end

#render(template = nil, opts = {}) ⇒ Object

if called without arguments render a template with lowercased name of current request method, e.g. get for GET, post for POST etc. at first it will look between defined templates. then it will search a file. it will try each extension the effective engine has registered, e.g. .erb, .rhtml for ERB. it will search in the folder the controller was defined in(NOT in path_to_templates, which is used for defined templates only). so put each controller in a separate folder to avoid templates clash.

if no file found a TemplateError will be raised. if a block given it will use the the string returned by the block as template and wont search for defined nor file templates.

by default ERB engine will be used (@see engine).

for layout it will take one given through options or one defined at class level. if none given, it will render without layout. to use a layout path_to_layouts should be defined at class level.

by default it will use current instance as scope. to render in a isolated scope, set it via :scope option

to pass some local variables use :locals option

Examples:

render ./get.erb without layout

class Pages < RocketIO::Controller

  def get
    render
  end
end

render ./get.erb with :master layout

class Pages < RocketIO::Controller
  layout :master

  def get
    render
  end
end

render ./get.erb with explicit :master layout

class Pages < RocketIO::Controller

  def get
    render(layout: :master)
  end
end

render within isolated scope

class Pages < RocketIO::Controller

  def get
    render(scope: Object.new)
  end
end

render with custom locals

class Pages < RocketIO::Controller

  def get
    render(locals: {x: 'y'})
  end
end


70
71
72
73
74
75
76
77
78
79
# File 'lib/rocketio-views/controller.rb', line 70

def render template = nil, opts = {}
  opts, template = template, nil if template.is_a?(::Hash)
  engine, engine_opts = resolve_engine(opts)
  template = block_given? ? yield : resolve_template(template, engine)
  scope    = opts.fetch(:scope, self)
  locals   = template_vars.merge(opts.fetch(:locals, RocketIO::EMPTY_HASH)).freeze
  layout   = opts.fetch(:layout, self.layout)
  template = compile_template(template, engine, engine_opts).render(scope, locals)
  layout   ? render_layout(layout, opts) {template} : template
end

#render_layout(template = nil, opts = {}, &block) ⇒ Object

render a template that yields the given block. that’s it, a layout is a template that yields given string.

layout can be specified two ways:

- as layout name
- as string

if both given a ArgumentError error raised. if :template option given, no layout lookups will occur.

otherwise… if no layout name given, it will use the one set at class level. if no layout set at class level and no layout given, it will raise a RuntimeError.

then it will search for given layout between defined ones. if none found, it will search a file in ‘path_to_layouts` folder. it will try each extension registered with effective engine. if no file found it will raise a TemplateError.

block is required and should return the string to be yielded.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/rocketio-views/controller.rb', line 102

def render_layout template = nil, opts = {}, &block
  template && opts[:template] && raise(ArgumentError, 'Both layout name and :template option given. Please use either one.')

  opts, template = template, nil if template.is_a?(::Hash)
  engine, engine_opts = resolve_engine(opts)
  template = if template
    resolve_layout(template, engine)
  else
    opts[:template] || begin
      self.layout || raise(RocketIO::LayoutError, 'No default layout set and no explicit layout given')
      resolve_layout(self.layout, engine)
    end
  end

  scope  = opts.fetch(:scope, self)
  locals = template_vars.merge(opts.fetch(:locals, RocketIO::EMPTY_HASH)).freeze
  compile_template(template, engine, engine_opts)
    .render(scope, locals, &(block || RocketIO::EMPTY_STRING_PROC))
end

#template_varsObject



30
31
32
# File 'lib/rocketio-views/template_vars.rb', line 30

def template_vars
  __template_vars__.each_with_object({}) {|(k,v),o| o[k] = __send__(v)}
end

#templatesObject



81
# File 'lib/rocketio-views/templates.rb', line 81

def templates; RocketIO::EMPTY_HASH end