lib/orange/packet.rb in orange-0.0.2 vs lib/orange/packet.rb in orange-0.0.3

- old
+ new

@@ -1,66 +1,140 @@ module Orange + # Orange::Packet is a wrapper for Rack's basic env variable. + # It acts somewhat like Rack::Request, except with more functionality. + # For each request a unique Packet is generated, and this packet is + # used to by middleware to create the response. + # + # All orange enhanced middleware has a packet_call method that + # automatically turns the generic rack call(env) into a call + # that has a packet instead, so all functions for the packet should + # be available for the request. + # + # Pulps are modules that are mixed into the Packet, allowing + # additional functionality to be used by the packet. + # # By default, haml files are parsed in the context of their # packet. This means all of instance variables and functions should # be available to the haml parser. class Packet + # By default, header will be content-type DEFAULT_HEADERS = {"Content-Type" => 'text/html'} unless defined?(DEFAULT_HEADERS) + # We override the instantiation to only create one packet per env + # @param [Orange::Core] orange a pointer to the orange core + # @param [Hash] env a standard Rack hash def self.new(orange, env) return env['orange.packet'] if env['orange.packet'] super(orange, env) end + # Allows tying in to the method_missing method without redefining it + # elsewhere. This lets dynamic methods be defined on the packet. + # Regexes are defined to match method names. #method_missing will + # loop through and try to find a match, executing the proc defined in + # the block. + # @param [Regexp] regex the regex to match + # @yield [Orange::Packet, MatchData, args] the block to execute if matched + # (passed instance, match data and args) + def self.meta_methods(regex, &block) + return unless block_given? + proc = block + @@matchers ||= {} + @@matchers[regex] = proc + end + + # Allows access to the matchers added via the #meta_methods method + # @return [Hash] the matchers hash + def matchers + @@matchers || {} + end + + # Initialize is only called if a packet hasn't already been called for + # this env. Sets up the basic env, creating a pointer back to self + # and a Rack::Request object. + # @param [Orange::Core] orange a pointer to the orange core + # @param [Hash] env a standard Rack hash def initialize(orange, env) @orange = orange @env = env @env['orange.packet'] = self @env['orange.env'] = {} unless @env['orange.env'] @env['orange.env'][:request] = Rack::Request.new(env) @env['orange.env'][:headers] = {} end + # Gives access to the orange.env key in the env, optionally + # including a default if key isn't involved. + # @param [Symbol, String] key the key to be found + # @param [optional, Object] default the return value if key doesn't exist (default is false) + # @return [Object] any value stored in the env def [](key, default = false) @env['orange.env'].has_key?(key) ? @env['orange.env'][key] : default end + # Lets user set a value in the orange.env + # @param [Symbol, String] key the key to be set + # @param [Object] val the value to be set def []=(key, val) @env['orange.env'][key] = val end + # Access to the main env (orange env is stored within the main env) + # @return [Hash] the request's env hash def env @env end + # Access to the rack session + # @return [Hash] the session information made available by Rack def session env['rack.session'] end + # Generate headers for finalization + # @return [Hash] the header information stored in the orange.env, combined with the defaults + # set as DEFAULT_HEADERS def headers packet[:headers, {}].with_defaults(DEFAULT_HEADERS) end + + # Set a header + # @param [String] key the key to be set + # @param [Object] val the value to be set def header(key, val) @env['orange.env'][:headers][key] = val end + # Set a header (same as #header) + # @param [String] key the key to be set + # @param [Object] val the value to be set def add_header(key, val) header key, val end + # Returns the content ready to be used by Rack (wrapped in an array) + # @return [Array] array of strings to be rendered def content return [packet[:content]] if packet[:content] - return [] + return [''] end + # Returns the request object generated by Rack::Request(packet.env) + # @return [Rack::Request] the request object def request packet[:request] end + # A pointer to the Orange::Core instance + # @return [Orange::Core] the orange core run by the application def orange @orange end + # Returns the array of [status, headers, content] Rack expects + # @return [Array] the triple array expected by Rack at the end + # of a call def finish headers = packet.headers status = packet[:status, 200] content = packet.content if content.respond_to?(:to_ary) @@ -68,21 +142,41 @@ inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s end [status, headers, content] end + # Returns self + # @return [Orange::Packet] self def packet self end + # Includes the module passed + # @param [Module] inc the module to be mixed into the class def self.mixin(inc) include inc end + # Route calls the router object set in the packet + # @return [void] route doesn't return anything directly, the + # main application calls packet.route then returns packet.finish. + # Routers set content, headers and status if necessary. + # They can also raise redirect errors to circumvent the process. def route router = packet['route.router'] raise 'Router not found' unless router router.route(self) end + + # Method Missing allows defining custom methods + def method_missing(id, *args) + matched = false + id = id.to_s + @@matchers.each_key do |k| + matched = k if id =~ k + break if matched + end + return @@matchers[matched].call(packet, matched.match(id), args) if matched + raise NoMethodError + end end - end \ No newline at end of file