Class Maveric
In: maveric.rb
maveric/sessions.rb
Parent: Object

The Maveric: Yeargh.

The Maveric may be used alone or may be used in a cadre of loosely aligned Maveric instances. The Maveric stands tall and proud, relying on it‘s fine family and it‘s inventory of goods to get things done with little magic or trickery.

Methods

Classes and Modules

Module Maveric::Models
Module Maveric::Views
Class Maveric::Controller
Class Maveric::FCGI
Class Maveric::MongrelHandler
Class Maveric::Route
Class Maveric::Router
Class Maveric::Sessions
Class Maveric::WEBrickServlet

Constants

VERSION = '0.3.0'   Implementation details.
EOL = "\r\n"   Standard end of line for HTTP
MP_BOUND_REGEX = /\Amultipart\/form-data.*boundary=\"?([^\";, ]+)\"?/n   Group 1 wil contain a boundary for multipart/form-data bodies.

Attributes

options  [R] 
router  [R] 

Public Class methods

If no argument is given, the array of actions is returned.

If a Symbol or String is given with no block, in prepare_env, the corresponding method is called with the environment hash as an argument. If a block is given then, in prepare_env, that block will be called with the environment hash as the single argument.

If you are specifying other options you must explicitly state :name => <chosen label> as an argument. Additional arguments include :test, which should be a Proc that accepts a single argument. The argument will be the environment hash and the boolean result of the block will determine if the block will run.

[Source]

     # File maveric.rb, line 303
303:     def adjust_env act={}, &block
304:       ::Maveric.type_check :act, act, Hash, Symbol, String
305:       return @adj_env if act.is_a? Hash and act.empty?
306:       act = {:name => act} unless act.is_a? Hash
307:       act[:do] = block
308:       @adj_env << act
309:     end

Performs URI escaping.

[Source]

     # File maveric.rb, line 176
176:     def escape(s)
177:       s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
178:         '%'+$1.unpack('H2'*$1.size).join('%').upcase
179:       }.tr(' ', '+')
180:     end

Sets up a new Maveric with everything it needs for normal operation. Many things are either copied or included from it‘s parent The logger is copied and Models and Views are included by their respective modules.

[Source]

     # File maveric.rb, line 276
276:     def inherited klass
277:       ::Maveric.log.info "#{klass} inherits from #{self}."
278:       super klass
279:       parent = self
280:       klass.class_eval do
281:         const_set(:Models, Module.new).
282:           module_eval { include parent::Models }
283:         const_set(:Views, Module.new).
284:           module_eval { include parent::Views }
285:         @adj_env = parent.adjust_env.dup
286:       end
287:     end

Builds a logging object if there‘s not one yet, then returns it.

[Source]

     # File maveric.rb, line 164
164:     def log
165:       unless defined? @@maveric_logger
166:         @@maveric_logger = Log4r::Logger.new 'mvc'
167:         @@maveric_logger.outputters = Log4r::Outputter['stderr']
168:         @@maveric_logger.level = Log4r::INFO
169:         @@maveric_logger.info "#{self} #{::Maveric::VERSION}"+
170:           " integrated at #{Time.now}"
171:       end
172:       @@maveric_logger
173:     end

A recursive method to hunt out subclasses of Controller nested within a Maveric subclass. Used in the setting up of autoroutes.

[Source]

     # File maveric.rb, line 261
261:     def nested_controllers realm=self, stk=[]
262:       stk << realm #We don't need to visit the same thing twice.
263:       realm.constants.map do |c|
264:         next if stk.include?(c = realm.const_get(c)) or not c.is_a? Module
265:         a = []
266:         a << c if c < ::Maveric::Controller
267:         a += nested_controllers c, stk
268:       end.compact.flatten
269:     end

When instantiated, the Maveric decends through its constants for nested Controllers and adds them by their routes.

[Source]

     # File maveric.rb, line 316
316:   def initialize opts={}
317:     ::Maveric.log.info "#{self.class} instantiated at #{Time.now}"
318:     ::Maveric.type_check :opts, opts, Hash
319:     @options = {:path_prefix => '/'}.merge opts
320:     @router = ::Maveric::Router.new
321:     self.class.nested_controllers.each {|c| router.add c.routes, c }
322:   end

Parse a multipart/form-data entity. Adapated from cgi.rb. The body argument may either be a StringIO or a IO of subclass thereof.

Might need to be rehauled to match query_parse‘s behaviour.

[Source]

     # File maveric.rb, line 210
210:     def parse_multipart boundary, body
211:       ::Maveric.type_check :body, body, IO, StringIO
212:       values = {}
213:       bound = /(?:\r?\n|\A)#{Regexp::quote('--'+boundary)}(?:--)?\r$/
214:       until body.eof?
215:         fv = {}
216:         until body.eof? or /^#{EOL}$/=~l
217:           case l = body.readline
218:           when /^Content-Type: (.+?)(\r$|\Z)/m
219:             fv[:type] = $1
220:           when /^Content-Disposition: form-data;/
221:             $'.scan(/(?:\s(\w+)="([^"]+)")/) {|w| fv[w[0].intern] = w[1] }
222:           end
223:         end
224: 
225:         o = unless fv[:filename] then ''
226:           else fv[:tempfile] = Tempfile.new('MVC').binmode end
227:         body.inject do |buf,line|
228:           o << buf.chomp and break if bound =~ line
229:           o << buf
230:           line
231:         end
232: 
233:         fv[:tempfile].rewind if fv.key? :tempfile
234:         values[fv[:name]] = fv.key?(:filename) ? fv : o
235:       end
236:       body.rewind
237:       values
238:     end

Parses a query string by breaking it up around the delimiting characters. You can also use this to parse cookies by changing the characters used in the second parameter (which defaults to ’;,’).

This will return a hash of parameters, values being contained in an array. As a warning, the default value is an empty array, not nil.

[Source]

     # File maveric.rb, line 197
197:     def query_parse(qs, delim = '&;')
198:       (qs||'').split(/[#{delim}] */n).inject(Hash.new([])) { |h,p|
199:         k, v = unescape(p).split('=',2)
200:         (h[k]||=[]) << v
201:         h
202:       }
203:     end

Return a session via Session#session

[Source]

    # File maveric/sessions.rb, line 58
58:   def self.session id; @sessions.session id; end

Return the standard session set.

[Source]

    # File maveric/sessions.rb, line 56
56:   def self.sessions; @sessions; end

I have a penchant for static typing, so as a general assertion and insurance that the correct types of data are being passed I created this little checking method. It will test value to be an instance of any of the trailing class, or if the block provided evalutates as true.

[Source]

     # File maveric.rb, line 245
245:     def type_check name, value, *klasses, &test
246:       return unless $DEBUG
247:       # order of speed: #include?, #index, #any?
248:       if i = klasses.any?{|k|value.is_a? k} then return i
249:       elsif test and r = test[value] then return r
250:       else
251:         raise TypeError, "Expected #{klasses*' or '} for #{name},"+
252:           " got #{value.class}:#{value.inspect}."
253:       end
254:     end

Unescapes a URI escaped string.

[Source]

     # File maveric.rb, line 183
183:     def unescape(s)
184:       s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
185:         [$1.delete('%')].pack('H*')
186:       }
187:     end

Public Instance methods

Uses env[:cookie] to find the session_id, and retrives the data to set the valuse or env[:session].

[Source]

    # File maveric/sessions.rb, line 62
62:   def env_1_session env
63:     session_id = env[:cookies][::Maveric::Sessions::COOKIE_NAME].first
64:     env[:session] = ::Maveric.session session_id 
65:   end

The env argument should be a normal environment hash derived from HTTP.

[Source]

     # File maveric.rb, line 367
367:   def prepare_environment env
368:     ::Maveric.type_check :env, env, Hash
369:     env.update :maveric => self
370:     self.class.adjust_env.select do |act|
371:       act[:test].nil? or act[:test][env]
372:     end.each do |act|
373:       ::Maveric.log.debug "#{self.class} prep_env #{act[:name]}"
374:       if act[:do]
375:         act[:do].call env
376:       else
377:         __send__ act[:name], env
378:       end
379:     end
380:   end

Maveric‘s cue to start doing the heavy lifting. The env should be a hash with the typical assignments from an HTTP environment or crying and whining will ensue. The req_body argument should be a StringIO.

[Source]

     # File maveric.rb, line 330
330:   def process req_body=$stdin, env=ENV, opts={}
331:     ::Maveric.log.info "#{self.class}#process\n  "+
332:       "[#{Time.now}] #{env['REMOTE_ADDR']} => #{env['REQUEST_URI']}"
333:     ::Maveric.type_check :req_body, req_body, StringIO
334:     ::Maveric.type_check :env, env, Hash
335:     begin
336:       prepare_environment env unless env.key? :maveric
337:       raise RuntimeError, [404, "Page not found."] unless env[:route]
338: 
339:       # If this is a POST request we need to load data from the body
340:       if env['REQUEST_METHOD'] =~ /^post$/i
341:         env[:params].update env.key?(:multipart_boundary) ?
342:           parse_multipart(env[:multipart_boundary], req_body) :
343:           ::Maveric.query_parse(req_body.read)
344:       end
345: 
346:       unless env[:route][:controller] < ::Maveric::Controller
347:         raise TypeError, "Route Controller of "+
348:           "#{env[:route][:controller].class}. Expecting ::Maveric::Controller."
349:       end
350: 
351:       env[:route][:controller].new req_body, env, opts
352:     rescue # we catch exception.is_a? StandardError
353:       ::Maveric.log.error "#{Time.now}:\n#{$!.inspect}\n#{$!.backtrace*"\n"}"
354:       begin
355:         raise $! # this makes sense in a certain context...
356:         # Here is where we should have transformational stuffs for accessorizing
357:         # or customizing error pages. As long as someone doesn't get stupid
358:         # about it.
359:       rescue
360:         return $!
361:       end
362:     end
363:   end

[Validate]