Class: Trellis::Application

Inherits:
Object show all
Includes:
Logging, Nokogiri::XML, Rack::Utils
Defined in:
lib/trellis/trellis.rb

Overview

– Application – Represents a Trellis Web Application. An application can define one or more pages and it must define a home page or entry point into the application

Constant Summary collapse

@@partials =
Hash.new
@@layouts =
Hash.new
@@filters =
Hash.new

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

included, logger

Class Method Details

.filter(name, kind = :before, &block) ⇒ Object



115
116
117
118
# File 'lib/trellis/trellis.rb', line 115

def self.filter(name, kind = :before, &block)
  name = name.to_sym unless name.class == Symbol
  @@filters[name] = OpenStruct.new({:name => name, :kind => kind, :block => block}) 
end

.filtersObject



111
112
113
# File 'lib/trellis/trellis.rb', line 111

def self.filters
  @@filters
end

.home(sym) ⇒ Object

class method that defines the homepage or entry point of the application the entry point is the URL pattern / where the application is mounted



76
77
78
# File 'lib/trellis/trellis.rb', line 76

def self.home(sym)
  @homepage = sym   
end

.inherited(child) ⇒ Object

descendant application classes get a singleton class level instances for holding homepage, dependent pages, static resource routing paths



63
64
65
66
67
68
69
70
71
72
# File 'lib/trellis/trellis.rb', line 63

def self.inherited(child) #:nodoc:
  child.class_attr_reader(:homepage)
  child.attr_array(:persistents)
  child.class_attr_reader(:session_config)
  child.attr_array(:static_routes)
  child.attr_array(:routers)
  child.meta_def(:logger) { Application.logger }
  child.instance_variable_set(:@session_config, OpenStruct.new({:impl => :cookie}))
  super
end

.layout(name, body = nil, options = nil, &block) ⇒ Object



107
108
109
# File 'lib/trellis/trellis.rb', line 107

def self.layout(name, body = nil, options = nil, &block)
  store_template(name, :layout, body, options, &block)
end

.layoutsObject



103
104
105
# File 'lib/trellis/trellis.rb', line 103

def self.layouts
  @@layouts
end

.map_static(urls = [], root = File.expand_path("#{File.dirname($0)}/../html/")) ⇒ Object

define url paths for static resources



85
86
87
# File 'lib/trellis/trellis.rb', line 85

def self.map_static(urls = [], root = File.expand_path("#{File.dirname($0)}/../html/"))
  @static_routes << {:urls => urls, :root => root}
end

.partial(name, body = nil, options = nil, &block) ⇒ Object



99
100
101
# File 'lib/trellis/trellis.rb', line 99

def self.partial(name, body = nil, options = nil, &block)
  store_template(name, :partial, body, options, &block)
end

.partialsObject



95
96
97
# File 'lib/trellis/trellis.rb', line 95

def self.partials
  @@partials
end

.persistent(*fields) ⇒ Object

application-wide persistent fields



90
91
92
93
# File 'lib/trellis/trellis.rb', line 90

def self.persistent(*fields)
  instance_attr_accessor fields
  @persistents = @persistents | fields    
end

.routersObject



164
165
166
167
168
169
# File 'lib/trellis/trellis.rb', line 164

def self.routers 
  unless @routers
    @routers = Page.subclasses.values.collect { |page| page.router }.compact.sort {|a,b| b.score <=> a.score }
  end
  @routers
end

.session(sym, options = {}) ⇒ Object



80
81
82
# File 'lib/trellis/trellis.rb', line 80

def self.session(sym, options={})
  @session_config = OpenStruct.new({:impl => sym, :options => options}) 
end

Instance Method Details

#call(env) ⇒ Object

rack call interface.



178
179
180
# File 'lib/trellis/trellis.rb', line 178

def call(env)
  dup.call!(env)
end

#call!(env) ⇒ Object

implements the rack specification



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
# File 'lib/trellis/trellis.rb', line 183

def call!(env)
  response = Rack::Response.new
  request = Rack::Request.new(env)

  Application.logger.debug "request received with url_root of #{request.script_name}" unless request.script_name.blank?

  session = env['rack.session'] ||= {}

  router = find_router_for(request)
  route = router.route(request)
  
  page = route.destination.new if route.destination
  if page
    load_persistent_fields_data(session)
    page.application = self
    page.class.url_root = request.script_name
    page.path = request.path_info.sub(/^\//, '')
    page.inject_dependent_pages
    page.call_if_provided(:before_load)
    page.load_page_session_information(session)
    page.call_if_provided(:after_load)
    page.params = request.params.keys_to_symbols
    router.inject_parameters_into_page_instance(page, request)

    result = route.event ? page.process_event(route.event, route.value, route.source, session) : page
    
    Application.logger.debug "response is #{result} an instance of #{result.class}"
    
    # -------------------------
    # prepare the http response
    # -------------------------
    
    # -------------------------------------
    # process the 'get' method if available
    # -------------------------------------
    same_class = true
    if result.kind_of?(Trellis::Page) && result.respond_to?(:get)
      result_cls = result.class
      result = result.get
      same_class = result.class == result_cls
      Application.logger.debug "processed get method, result.class is now => #{result.class}"
    end
    
    case result
    # -----------------
    # explicit redirect
    # -----------------
    when Trellis::Redirect
      result.process(request, response)
      Application.logger.debug "redirecting (explicit) to ==> #{request.script_name}/#{result.target}"      
    # -----------------
    # implicit redirect
    # -----------------
    when Trellis::Page
      # redirect after POST or 'get' method returns a different page
      if (request.post? || route.event) || !same_class
        path = result.path ? result.path.gsub(/\/events\/.*/, '') : result.class.class_to_sym
        response.status = 302
        response.headers["Location"] = "#{request.script_name}/#{path}"
        Application.logger.debug "redirecting (implicit) to ==> #{request.script_name}/#{path}"
      # simply render page
      else
        response.body = result.render
        response.status = 200
        Application.logger.debug "rendering page #{result}"
      end
    # -------------------------------
    # stringify any other result type
    # -------------------------------
    else
      response.body = result.to_s
      response.status = 200
      Application.logger.debug "rendering #{result}"
    end
  else
    response.status = 404
  end
  save_persistent_fields_data(session)
  response.finish
end

#configuredObject



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/trellis/trellis.rb', line 139

def configured
  # configure rack middleware
  application = Rack::ShowStatus.new(self)
  application = Rack::ShowExceptions.new(application)
  application = Rack::Reloader.new(application) # only in development mode
  application = Rack::CommonLogger.new(application, Application.logger)
  
  # configure rack session
  session_config = self.class.session_config
  case session_config.impl
  when :pool
    application = Rack::Session::Pool.new(application, session_config.options)
  when :memcached
    application = Rack::Session::Memcache.new(application, session_config.options)
  else
    application = Rack::Session::Cookie.new(application)
  end

  # set all static resource paths
  self.class.static_routes.each do |path|
    application = Rack::Static.new(application, path)
  end
  application
end

#find_router_for(request) ⇒ Object

find the first page with a suitable router, if none is found use the default router



172
173
174
175
# File 'lib/trellis/trellis.rb', line 172

def find_router_for(request)
  match = Application.routers.find { |router| router.matches?(request) }
  match || DefaultRouter.new(:application => self)
end

#start(port = 3000) ⇒ Object

bootstrap the application



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/trellis/trellis.rb', line 121

def start(port = 3000)
  Application.logger.info "Starting Trellis Application #{self.class} on port #{port}"

  # only in development mode
  directory_watcher = configure_directory_watcher
  directory_watcher.start

  Rack::Handler::Mongrel.run configured, :Port => port do |server|
    trap(:INT) do
      Application.logger.info "Exiting Trellis Application #{self.class}"
      directory_watcher.stop
      server.stop
    end
  end
rescue Exception => e
  Application.logger.warn "#{ e } (#{ e.class })!"
end