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

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

included, logger

Class Method Details

.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



74
75
76
# File 'lib/trellis/trellis.rb', line 74

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



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

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



105
106
107
# File 'lib/trellis/trellis.rb', line 105

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

.layoutsObject



101
102
103
# File 'lib/trellis/trellis.rb', line 101

def self.layouts
  @@layouts
end

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

define url paths for static resources



83
84
85
# File 'lib/trellis/trellis.rb', line 83

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



97
98
99
# File 'lib/trellis/trellis.rb', line 97

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

.partialsObject



93
94
95
# File 'lib/trellis/trellis.rb', line 93

def self.partials
  @@partials
end

.persistent(*fields) ⇒ Object

application-wide persistent fields



88
89
90
91
# File 'lib/trellis/trellis.rb', line 88

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

.routersObject



153
154
155
156
157
158
# File 'lib/trellis/trellis.rb', line 153

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



78
79
80
# File 'lib/trellis/trellis.rb', line 78

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

Instance Method Details

#call(env) ⇒ Object

rack call interface.



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

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

#call!(env) ⇒ Object

implements the rack specification



172
173
174
175
176
177
178
179
180
181
182
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
# File 'lib/trellis/trellis.rb', line 172

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

  Application.logger.debug "request received with url_root of #{request.script_name}" if request.script_name

  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
    # -------------------------
    
    if result.kind_of?(Trellis::Redirect)
      # redirect short circuits
      result.apply_to(request, response)
      Application.logger.debug "redirecting to ==> #{request.script_name}/#{result.target}"
    elsif (request.post? || route.event) && result.kind_of?(Trellis::Page)
      # for action events of posts then use redirect after post pattern
      # remove the events path and just return to the page
      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 to ==> #{request.script_name}/#{path}"
    else
      # handle the get method
      if result.kind_of?(Trellis::Page) && result.respond_to?(:get)
        get = result.get
        if get.kind_of?(Trellis::Redirect)
          # redirect short circuits
          get.apply_to(request, response)
          Application.logger.debug "redirecting to ==> #{request.script_name}/#{get.target}"
        elsif (get.class == result.class) || !get.kind_of?(Trellis::Page)
          response.body = get.kind_of?(Trellis::Page) ? get.render : get
          response.status = 200
        else
          path = get.path ? get.path.gsub(/\/events\/.*/, '') : get.class.class_to_sym
          response.status = 302
          response.headers["Location"] = "#{request.script_name}/#{path}"
          Application.logger.debug "redirecting to ==> #{request.script_name}/#{path}"              
        end
      else
        # for render requests simply render the page
        response.body = result.kind_of?(Trellis::Page) ? result.render : result
        response.status = 200
      end
    end
  else
    response.status = 404
  end
  save_persistent_fields_data(session)
  response.finish
end

#configuredObject



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/trellis/trellis.rb', line 128

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



161
162
163
164
# File 'lib/trellis/trellis.rb', line 161

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



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/trellis/trellis.rb', line 110

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