Class: Maveric
- Inherits:
-
Object
- Object
- Maveric
- Defined in:
- lib/maveric.rb,
lib/maveric/sessions.rb
Overview
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.
Defined Under Namespace
Modules: Models, Views Classes: Controller, FCGI, MongrelHandler, Route, Router, Sessions, WEBrickServlet
Constant Summary collapse
- VERSION =
Implementation details.
'0.3.0'
- EOL =
Standard end of line for HTTP
"\r\n"
- MP_BOUND_REGEX =
Group 1 wil contain a boundary for multipart/form-data bodies.
/\Amultipart\/form-data.*boundary=\"?([^\";, ]+)\"?/n
Instance Attribute Summary collapse
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#router ⇒ Object
readonly
Returns the value of attribute router.
Class Method Summary collapse
-
.adjust_env(act = {}, &block) ⇒ Object
If no argument is given, the array of actions is returned.
-
.escape(s) ⇒ Object
Performs URI escaping.
-
.inherited(klass) ⇒ Object
Sets up a new Maveric with everything it needs for normal operation.
-
.log ⇒ Object
Builds a logging object if there’s not one yet, then returns it.
-
.nested_controllers(realm = self, stk = []) ⇒ Object
A recursive method to hunt out subclasses of Controller nested within a Maveric subclass.
-
.parse_multipart(boundary, body) ⇒ Object
Parse a multipart/form-data entity.
-
.query_parse(qs, delim = '&;') ⇒ Object
Parses a query string by breaking it up around the delimiting characters.
-
.session(id) ⇒ Object
Return a session via Session#session.
-
.sessions ⇒ Object
Return the standard session set.
-
.type_check(name, value, *klasses, &test) ⇒ Object
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.
-
.unescape(s) ⇒ Object
Unescapes a URI escaped string.
Instance Method Summary collapse
- #env_1_session(env) ⇒ Object
-
#initialize(opts = {}) ⇒ Maveric
constructor
When instantiated, the Maveric decends through its constants for nested Controllers and adds them by their routes.
-
#prepare_environment(env) ⇒ Object
The env argument should be a normal environment hash derived from HTTP.
-
#process(req_body = $stdin, env = ENV, opts = {}) ⇒ Object
Maveric’s cue to start doing the heavy lifting.
Constructor Details
#initialize(opts = {}) ⇒ Maveric
When instantiated, the Maveric decends through its constants for nested Controllers and adds them by their routes.
316 317 318 319 320 321 322 |
# File 'lib/maveric.rb', line 316 def initialize opts={} ::Maveric.log.info "#{self.class} instantiated at #{Time.now}" ::Maveric.type_check :opts, opts, Hash @options = {:path_prefix => '/'}.merge opts @router = ::Maveric::Router.new self.class.nested_controllers.each {|c| router.add c.routes, c } end |
Instance Attribute Details
#options ⇒ Object (readonly)
Returns the value of attribute options.
324 325 326 |
# File 'lib/maveric.rb', line 324 def @options end |
#router ⇒ Object (readonly)
Returns the value of attribute router.
324 325 326 |
# File 'lib/maveric.rb', line 324 def router @router end |
Class Method Details
.adjust_env(act = {}, &block) ⇒ Object
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.
303 304 305 306 307 308 309 |
# File 'lib/maveric.rb', line 303 def adjust_env act={}, &block ::Maveric.type_check :act, act, Hash, Symbol, String return @adj_env if act.is_a? Hash and act.empty? act = {:name => act} unless act.is_a? Hash act[:do] = block @adj_env << act end |
.escape(s) ⇒ Object
Performs URI escaping.
176 177 178 179 180 |
# File 'lib/maveric.rb', line 176 def escape(s) s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { '%'+$1.unpack('H2'*$1.size).join('%').upcase }.tr(' ', '+') end |
.inherited(klass) ⇒ Object
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.
276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/maveric.rb', line 276 def inherited klass ::Maveric.log.info "#{klass} inherits from #{self}." super klass parent = self klass.class_eval do const_set(:Models, Module.new). module_eval { include parent::Models } const_set(:Views, Module.new). module_eval { include parent::Views } @adj_env = parent.adjust_env.dup end end |
.log ⇒ Object
Builds a logging object if there’s not one yet, then returns it.
164 165 166 167 168 169 170 171 172 173 |
# File 'lib/maveric.rb', line 164 def log unless defined? @@maveric_logger @@maveric_logger = Log4r::Logger.new 'mvc' @@maveric_logger.outputters = Log4r::Outputter['stderr'] @@maveric_logger.level = Log4r::INFO @@maveric_logger.info "#{self} #{::Maveric::VERSION}"+ " integrated at #{Time.now}" end @@maveric_logger end |
.nested_controllers(realm = self, stk = []) ⇒ Object
A recursive method to hunt out subclasses of Controller nested within a Maveric subclass. Used in the setting up of autoroutes.
261 262 263 264 265 266 267 268 269 |
# File 'lib/maveric.rb', line 261 def nested_controllers realm=self, stk=[] stk << realm #We don't need to visit the same thing twice. realm.constants.map do |c| next if stk.include?(c = realm.const_get(c)) or not c.is_a? Module a = [] a << c if c < ::Maveric::Controller a += nested_controllers c, stk end.compact.flatten end |
.parse_multipart(boundary, body) ⇒ Object
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.
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 |
# File 'lib/maveric.rb', line 210 def parse_multipart boundary, body ::Maveric.type_check :body, body, IO, StringIO values = {} bound = /(?:\r?\n|\A)#{Regexp::quote('--'+boundary)}(?:--)?\r$/ until body.eof? fv = {} until body.eof? or /^#{EOL}$/=~l case l = body.readline when /^Content-Type: (.+?)(\r$|\Z)/m fv[:type] = $1 when /^Content-Disposition: form-data;/ $'.scan(/(?:\s(\w+)="([^"]+)")/) {|w| fv[w[0].intern] = w[1] } end end o = unless fv[:filename] then '' else fv[:tempfile] = Tempfile.new('MVC').binmode end body.inject do |buf,line| o << buf.chomp and break if bound =~ line o << buf line end fv[:tempfile].rewind if fv.key? :tempfile values[fv[:name]] = fv.key?(:filename) ? fv : o end body.rewind values end |
.query_parse(qs, delim = '&;') ⇒ Object
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.
197 198 199 200 201 202 203 |
# File 'lib/maveric.rb', line 197 def query_parse(qs, delim = '&;') (qs||'').split(/[#{delim}] */n).inject(Hash.new([])) { |h,p| k, v = unescape(p).split('=',2) (h[k]||=[]) << v h } end |
.session(id) ⇒ Object
Return a session via Session#session
58 |
# File 'lib/maveric/sessions.rb', line 58 def self.session id; @sessions.session id; end |
.sessions ⇒ Object
Return the standard session set.
56 |
# File 'lib/maveric/sessions.rb', line 56 def self.sessions; @sessions; end |
.type_check(name, value, *klasses, &test) ⇒ Object
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.
245 246 247 248 249 250 251 252 253 254 |
# File 'lib/maveric.rb', line 245 def type_check name, value, *klasses, &test return unless $DEBUG # order of speed: #include?, #index, #any? if i = klasses.any?{|k|value.is_a? k} then return i elsif test and r = test[value] then return r else raise TypeError, "Expected #{klasses*' or '} for #{name},"+ " got #{value.class}:#{value.inspect}." end end |
.unescape(s) ⇒ Object
Unescapes a URI escaped string.
183 184 185 186 187 |
# File 'lib/maveric.rb', line 183 def unescape(s) s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ [$1.delete('%')].pack('H*') } end |
Instance Method Details
#env_1_session(env) ⇒ Object
62 63 64 65 |
# File 'lib/maveric/sessions.rb', line 62 def env_1_session env session_id = env[:cookies][::Maveric::Sessions::COOKIE_NAME].first env[:session] = ::Maveric.session session_id end |
#prepare_environment(env) ⇒ Object
The env argument should be a normal environment hash derived from HTTP.
367 368 369 370 371 372 373 |
# File 'lib/maveric.rb', line 367 def prepare_environment env ::Maveric.type_check :env, env, Hash env.update :maveric => self self.class.adjust_env. select {|act| act[:test].nil? or act[:test][env] }. each {|act| act[:do] ? act[:do][env] : __send__(act[:name], env) } end |
#process(req_body = $stdin, env = ENV, opts = {}) ⇒ Object
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.
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/maveric.rb', line 330 def process req_body=$stdin, env=ENV, opts={} ::Maveric.log.info "#{self.class}#process\n "+ "[#{Time.now}] #{env['REMOTE_ADDR']} => #{env['REQUEST_URI']}" ::Maveric.type_check :req_body, req_body, StringIO ::Maveric.type_check :env, env, Hash begin prepare_environment env unless env.key? :maveric raise RuntimeError, [404, "Page not found."] unless env[:route] # If this is a POST request we need to load data from the body if env['REQUEST_METHOD'] =~ /^post$/i env[:params].update env.key?(:multipart_boundary) ? parse_multipart(env[:multipart_boundary], req_body) : ::Maveric.query_parse(req_body.read) end unless env[:route][:controller] < ::Maveric::Controller raise TypeError, "Route Controller of "+ "#{env[:route][:controller].class}. Expecting ::Maveric::Controller." end env[:route][:controller].new req_body, env, opts rescue # we catch exception.is_a? StandardError ::Maveric.log.error "#{Time.now}:\n#{$!.inspect}\n#{$!.backtrace*"\n"}" begin raise $! # this makes sense in a certain context... # Here is where we should have transformational stuffs for accessorizing # or customizing error pages. As long as someone doesn't get stupid # about it. rescue return $! end end end |