Class: EBuilder

Inherits:
Object
  • Object
show all
Includes:
EConstants, EUtils
Defined in:
lib/e-builder/base.rb,
lib/e-builder/setup.rb,
lib/e-more/view/e-builder.rb

Defined Under Namespace

Classes: ExtendedRack

Constant Summary

Constants included from EConstants

EConstants::CONTENT_TYPE__DEFAULT, EConstants::CONTENT_TYPE__EVENT_STREAM, EConstants::ENV__AUTHORIZATION_KEYS, EConstants::ENV__ESPRESSO_ACTION, EConstants::ENV__ESPRESSO_FORMAT, EConstants::ENV__ESPRESSO_PATH_INFO, EConstants::ENV__HTTP_ACCEPT, EConstants::ENV__HTTP_HOST, EConstants::ENV__HTTP_IF_MODIFIED_SINCE, EConstants::ENV__HTTP_IF_NONE_MATCH, EConstants::ENV__HTTP_IF_UNMODIFIED_SINCE, EConstants::ENV__HTTP_X_FORWARDED_HOST, EConstants::ENV__HTTP_X_REQUESTED_WITH, EConstants::ENV__PATH_INFO, EConstants::ENV__QUERY_STRING, EConstants::ENV__RACK_ENV, EConstants::ENV__REMOTE_USER, EConstants::ENV__REQUEST_METHOD, EConstants::ENV__REQUEST_URI, EConstants::ENV__SCRIPT_NAME, EConstants::ENV__SERVER_NAME, EConstants::ENV__SERVER_PORT, EConstants::ENV__XML_HTTP_REQUEST, EConstants::HEADER__AUTHENTICATE, EConstants::HEADER__CACHE_CONTROL, EConstants::HEADER__CONTENT_DISPOSITION, EConstants::HEADER__CONTENT_TYPE, EConstants::HEADER__EXPIRES, EConstants::HEADER__LAST_MODIFIED, EConstants::HEADER__LOCATION, EConstants::HEADER__TRANSFER_ENCODING, EConstants::HTTP__DEFAULT_PORT, EConstants::HTTP__DEFAULT_REQUEST_METHOD, EConstants::HTTP__DEFAULT_SERVER, EConstants::HTTP__REQUEST_METHODS, EConstants::INDEX_ACTION, EConstants::PATH_MODIFIERS, EConstants::PATH_RULES, EConstants::RACK__WEBSOCKET, EConstants::STATUS__BAD_REQUEST, EConstants::STATUS__NOT_FOUND, EConstants::STATUS__NOT_IMPLEMENTED, EConstants::STATUS__OK, EConstants::STATUS__PERMANENT_REDIRECT, EConstants::STATUS__PROTECTED, EConstants::STATUS__REDIRECT, EConstants::STATUS__SERVER_ERROR, EConstants::VIEW__DEFAULT_ENGINE, EConstants::VIEW__DEFAULT_ENGINE_NAME, EConstants::VIEW__DEFAULT_PATH, EConstants::VIEW__ENGINE_BY_SYM, EConstants::VIEW__ENGINE_MAPPER, EConstants::VIEW__EXTRA_ENGINES, EConstants::VIEW__EXT_BY_ENGINE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from EUtils

action_to_route, build_path, canonical_to_route, class_to_route, deRESTify_action, encode_token_auth_credentials, extract_hosts, indifferent_hash, indifferent_params, is_app?, method_arity, normalize_path, register_extra_engines!, rootify_url, route_to_regexp, underscore

Constructor Details

#initialize(automount = false, &proc) ⇒ EBuilder

creates new Espresso app.

Parameters:

  • automount (defaults to: false)

    if set to any positive value(except Class, Module or Regexp), all found controllers will be mounted, if set to a Class, Module or Regexp, only controllers under given namespace will be mounted.

  • proc (Proc)

    if block given, it will be executed inside newly created app



19
20
21
22
23
24
25
26
# File 'lib/e-builder/base.rb', line 19

def initialize automount = false, &proc
  @controllers, @subcontrollers = {}, []
  @routes, @hosts, @controllers_hosts = {}, {}, {}
  @automount = automount
  proc && self.instance_exec(&proc)
  use ExtendedRack
  compiler_pool(Hash.new)
end

Instance Attribute Details

#controllersObject (readonly)

Returns the value of attribute controllers.



9
10
11
# File 'lib/e-builder/base.rb', line 9

def controllers
  @controllers
end

#mounted_controllersObject (readonly)

Returns the value of attribute mounted_controllers.



9
10
11
# File 'lib/e-builder/base.rb', line 9

def mounted_controllers
  @mounted_controllers
end

Class Method Details

.call(env) ⇒ Object



5
6
7
# File 'lib/e-builder/base.rb', line 5

def self.call env
  new(:automount).call(env)
end

Instance Method Details

#appObject



157
158
159
160
161
162
163
164
165
# File 'lib/e-builder/base.rb', line 157

def app
  @app ||= begin
    on_boot!
    mount_controllers!
    @sorted_routes = sorted_routes.freeze
    @routes.freeze
    middleware.reverse.inject(lambda {|env| call!(env)}) {|a,e| e[a]}
  end
end

#automount!Object

auto-mount auto-discovered controllers. call this only after all controllers defined and app ready to start! leaving it in public zone for better control over mounting.



67
68
69
70
71
72
# File 'lib/e-builder/base.rb', line 67

def automount!
  controllers = [Class, Module, Regexp].include?(@automount.class) ?
    extract_controllers(@automount) :
    discover_controllers
  mount controllers.select {|c| c.accept_automount?}
end

#base_urlObject



10
11
12
# File 'lib/e-builder/setup.rb', line 10

def base_url
  @base_url || ''
end

#basic_auth(opts = {}, &proc) ⇒ Object Also known as: auth

set authorization at app level. any controller/action will be protected.



86
87
88
# File 'lib/e-builder/setup.rb', line 86

def basic_auth opts = {}, &proc
  use Rack::Auth::Basic, opts[:realm] || 'AccessRestricted', &proc
end

#call(env) ⇒ Object



153
154
155
# File 'lib/e-builder/base.rb', line 153

def call env
  app.call env
end

#clear_compiler(*keys) ⇒ Object

same as clear_compiler! except it work only on current process



33
34
35
# File 'lib/e-more/view/e-builder.rb', line 33

def clear_compiler *keys
  compiler_pool && compiler_pool.clear
end

#clear_compiler!Object

clearing compiler cache



27
28
29
30
# File 'lib/e-more/view/e-builder.rb', line 27

def clear_compiler!
  clear_compiler
  ipcm_trigger :clear_compiler
end

#compiler_pool(pool = nil) ⇒ Object

for most apps, most expensive operations are fs operations and template compilation. to avoid these operations, templates are compiled and stored into memory. on consequent requests they are just rendered.

by default, compiler are disabled. to enable it, set compiler pool at app level.

if you want to use a custom pool, make sure your pool behaves just like a Hash, meant it responds to []=, [], and clear methods.

Examples:

class App < E
  # ...
end
app = E.new
app.compiler_pool Hash.new # will store compiler cache into a hash
app.run


20
21
22
23
# File 'lib/e-more/view/e-builder.rb', line 20

def compiler_pool  pool =  nil
  @compiler_pool = pool if pool
  @compiler_pool
end

#digest_auth(opts = {}, &proc) ⇒ Object

set authorization at app level. any controller/action will be protected.



92
93
94
95
96
# File 'lib/e-builder/setup.rb', line 92

def digest_auth opts = {}, &proc
  opts[:realm]  ||= 'AccessRestricted'
  opts[:opaque] ||= opts[:realm]
  use Rack::Auth::Digest::MD5, opts, &proc
end

#environmentObject



109
110
111
# File 'lib/e-builder/base.rb', line 109

def environment
  ENV[ENV__RACK_ENV] || :development
end

#global_setup(&proc) ⇒ Object Also known as: setup_controllers, controllers_setup, setup

proc given here will be executed inside all controllers. used to setup multiple controllers at once.



76
77
78
79
# File 'lib/e-builder/base.rb', line 76

def global_setup &proc
  @global_setup = proc
  self
end

#map(url, opts = {}) ⇒ Object

set base URL to be prepended to all controllers



4
5
6
7
8
# File 'lib/e-builder/setup.rb', line 4

def map url, opts = {}
  url.is_a?(Hash) && (opts = url) && (url = '')
  @base_url = rootify_url(url).freeze
  @hosts    = extract_hosts(opts)
end

#middlewareObject



144
145
146
# File 'lib/e-builder/setup.rb', line 144

def middleware
  @middleware ||= []
end

#mount(*args, &setup) ⇒ Object

mount given/discovered controllers into current app. any number of arguments accepted. String arguments are treated as roots/canonicals. any other arguments are used to discover controllers. controllers can be passed directly or as a Module that contain controllers or as a Regexp matching controller’s name.

proc given here will be executed inside given/discovered controllers



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/e-builder/base.rb', line 37

def mount *args, &setup
  root, controllers, applications = nil, [], []
  opts = args.last.is_a?(Hash) ? args.pop : {}
  args.flatten.each do |a|
    if a.is_a?(String)
      root = rootify_url(a)
    elsif is_app?(a)
      controllers << a
    elsif a.respond_to?(:call)
      applications << a
    else
      controllers.concat extract_controllers(a)
    end
  end
  controllers.each do |c|
    @controllers[c] = [root, opts, setup]
    c.subcontrollers.each do |sc|
      mount(sc, root.to_s + c.base_url, opts)
      @subcontrollers << sc
    end
  end
  
  mount_applications applications, root, opts

  self
end

#on_boot(&proc) ⇒ Object

block(s) to run just before application starts



234
235
236
# File 'lib/e-builder/setup.rb', line 234

def on_boot &proc
  (@on_boot ||= []).push(proc)
end

#rewrite(rule, &proc) ⇒ Object Also known as: rewrite_rule

Note:

any method available to controller instance are also available inside rule proc. so you can fine tune the behavior of any rule. ex. redirect on GET requests and pass control on POST requests. or do permanent redirect for robots and simple redirect for browsers etc.

declaring rewrite rules.

first argument should be a regex and a proc should be provided.

the regex(actual rule) will be compared against Request-URI, i.e. current URL without query string. if some rule depend on query string, use params inside proc to determine either some param was or not set.

the proc will decide how to operate when rule matched. you can do: ‘redirect(’location’)‘

redirect to new location using 302 status code

‘permanent_redirect(’location’)‘

redirect to new location using 301 status code

‘pass(controller, action, any, params, with => opts)`

pass control to given controller and action without redirect.
consequent params are used to build URL to be sent to given controller.

‘halt(status|body|headers|response)`

send response to browser without redirect.
accepts an arbitrary number of arguments.
if arg is an Integer, it will be used as status code.
if arg is a Hash, it is treated as headers.
if it is an array, it is treated as Rack response and are sent immediately, ignoring other args.
any other args are treated as body.

Examples:

app = E.new

# redirect to new address
app.rewrite /\A\/(.*)\.php$/ do |title|
  redirect Controller.route(:index, title)
end

# permanent redirect
app.rewrite /\A\/news\/([\w|\d]+)\-(\d+)\.html/ do |title, id|
  permanent_redirect Forum, :posts, :title => title, :id => id
end

# no redirect, just pass control to News controller
app.rewrite /\A\/latest\/(.*)\.html/ do |title|
  pass News, :index, :scope => :latest, :title => title
end

# Return arbitrary body, status-code, headers, without redirect:
# If argument is a hash, it is added to headers.
# If argument is a Integer, it is treated as Status-Code.
# Any other arguments are treated as body.
app.rewrite /\A\/archived\/(.*)\.html/ do |title|
  if page = Model::Page.first(:url => title)
    halt page.content, 'Last-Modified' => page.last_modified.to_rfc2822
  else
    halt 404, 'page not found'
  end
end

app.run


211
212
213
214
# File 'lib/e-builder/setup.rb', line 211

def rewrite rule, &proc
  proc || raise(ArgumentError, "Rewrite rules requires a block to run")
  @routes[rule] = {'GET' => {:rewriter => proc}}
end

#root(path = nil) ⇒ Object Also known as: app_root

set/get app root



15
16
17
18
# File 'lib/e-builder/setup.rb', line 15

def root path = nil
  @root = ('%s/' % path).sub(/\/+\Z/, '/').freeze if path
  @root ||= (::Dir.pwd << '/').freeze
end

#run(opts = {}) ⇒ Object

by default, Espresso will use WEBrick server. pass :server option and any option accepted by selected(or default) server:

Examples:

use Thin server on its default port

app.run :server => :Thin

use EventedMongrel server with custom options

app.run :server => :EventedMongrel, :port => 9090, :num_processors => 1000

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :server (Symbol) — default: :WEBrick

    web server

  • :port (Integer) — default: 5252
  • :host (String) — default: 0.0.0.0


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/e-builder/base.rb', line 126

def run opts = {}
  boot!

  handler = opts.delete(:server)
  (handler && Rack::Handler.const_defined?(handler)) || (handler = HTTP__DEFAULT_SERVER)

  port = opts.delete(:port)
  opts[:Port] ||= port || HTTP__DEFAULT_PORT

  host = opts.delete(:host) || opts.delete(:bind)
  opts[:Host] = host if host

  $stderr.puts "\n--- Starting Espresso for %s on %s port backed by %s server ---\n\n" % [
    environment, opts[:Port], handler
  ]
  Rack::Handler.const_get(handler).run app, opts do |server|
    %w[INT TERM].each do |sig|
      Signal.trap(sig) do
        $stderr.puts "\n--- Stopping Espresso... ---\n\n"
        server.respond_to?(:stop!) ? server.stop! : server.stop
      end
    end
    server.threaded = opts[:threaded] if server.respond_to? :threaded=
    yield server if block_given?
  end
end

#session(use, *args) ⇒ Object

allow app to use sessions.

Examples:

keep sessions in memory

class App < E
  # ...
end
app = E.new
app.session :memory
app.run

keep sessions in memory using custom options

class App < E
  # ...
end
app = E.new
app.session :memory, :domain => 'foo.com', :expire_after => 2592000
app.run

keep sessions in cookies

class App < E
  # ...
end
app = E.new
app.session :cookies
app.run

keep sessions in memcache

class App < E
  # ...
end
app = E.new
app.session :memcache
app.run

use a custom pool, i.e. github.com/migrs/rack-session-mongo

#> gem install rack-session-mongo

class App < E
  # ...
end

require 'rack/session/mongo'

app = E.new
app.session Rack::Session::Mongo
app.run

Parameters:

  • use (Symbol, Class)
  • args (Array)


70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/e-builder/setup.rb', line 70

def session use, *args
  args.unshift case use
                 when :memory
                   Rack::Session::Pool
                 when :cookies
                   Rack::Session::Cookie
                 when :memcache
                   Rack::Session::Memcache
                 else
                   use
               end
  use(*args)
end

#streaming_backendObject

by default Espresso will use EventMachine for streaming, but it also supports Celluloid, when Reel web-server used. use this method to set Celluloid as streaming backend.

Examples:

app = E.new do
  streaming_backend :Celluloid
end


226
227
228
229
230
231
# File 'lib/e-builder/setup.rb', line 226

def streaming_backend backend = nil
  @streaming_backend = backend
  def streaming_backend
    @streaming_backend
  end
end

#to_appObject Also known as: to_app!, boot!



167
168
169
170
# File 'lib/e-builder/base.rb', line 167

def to_app
  app
  self
end

#url_map(opts = {}) ⇒ Object Also known as: urlmap

displays URLs the app will respond to, with controller and action that serving each URL.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/e-builder/base.rb', line 86

def url_map opts = {}
  mount_controllers!
  map = sorted_routes.inject({}) do |m,r|
    @routes[r].each_pair {|rm,rs| (m[r] ||= {})[rm] = rs.dup}; m
  end

  def map.to_s
    out = ''
    self.each_pair do |route, request_methods|
      next if route.source.size == 0
      out << "%s\n" % route.source
      request_methods.each_pair do |rm,rs|
        out << "  %s%s" % [rm, ' ' * (10 - rm.to_s.size)]
        out << "%s\n" % (rs[:app] || [rs[:controller], rs[:action]]*'#')
      end
      out << "\n"
    end
    out
  end
  map
end

#use(ware, *args, &block) ⇒ Object

Note:

middleware defined inside some controller will run only for that controller. to have global middleware, define it at app level.

middleware declared here will be used on all controllers.

especially, here should go middleware that changes app state, which wont work if defined inside controller.

you can of course define any type of middleware at app level, it is even recommended to do so to avoid redundant middleware declaration at controllers level.

Any middleware that does not change app state, i.e. non-upfront middleware, can be defined inside controllers.

Examples:


class App < E
  # ...
end
app = E.new
app.use SomeMiddleware, :with, :some => :opts
app.run

defining middleware at app level

module App
  class Forum < E
    map '/forum'
    # ...
  end

  class Blog < E
    map '/blog'
    # ...
  end
end

app = E.new
app.use Rack::CommonLogger
app.use Rack::ShowExceptions
app.run


140
141
142
# File 'lib/e-builder/setup.rb', line 140

def use ware, *args, &block
  middleware << proc { |app| ware.new(app, *args, &block) }
end