Skip to content

Commit

Permalink
Version 0.1.1 (2008-03-30)
Browse files Browse the repository at this point in the history
  Validations
    ValidationOfPresence lets you ensure a parameter is present.
  CGI
    url_for helper lets you construct escaped parameters programmatically.
      url_for('index.cgi', :q => 'bar') => 'index.cgi?test=bar'
    Made URI completion more consistent
    Fixed CGI header duplication
  Templating
    Better error handling
  • Loading branch information
Aphyr committed Mar 30, 2008
1 parent 2e3aba8 commit a92910b
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 27 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/doc/
/pkg/
doc/
pkg/
~*
*.swp
11 changes: 11 additions & 0 deletions HISTORY
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
Version 0.1.1 (2008-03-30)
Validations
ValidationOfPresence lets you ensure a parameter is present.
CGI
url_for helper lets you construct escaped parameters programmatically.
url_for('index.cgi', :q => 'bar') => 'index.cgi?test=bar'
Made URI completion more consistent
Fixed CGI header duplication
Templating
Better error handling

Version 0.1.0 (2008-03-27)
Initial release
Validations
Expand Down
6 changes: 3 additions & 3 deletions lib/exocora.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
$LOAD_PATH.unshift(BASEDIR)
$LOAD_PATH.uniq!

require 'string'
require 'array'
require 'cgi'
require 'support/string'
require 'support/array'
require 'support/cgi'
require 'exocora/support'
require 'exocora/errors'
require 'exocora/validation'
Expand Down
12 changes: 4 additions & 8 deletions lib/exocora/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,16 @@ class SheetError < RuntimeError

# Represents an error encountered when processing a template.
class TemplateError < RuntimeError
attr_accessor :message, :exception
def initialize(message, exception)
attr_accessor :message, :original
def initialize(message, original)
@message = message
@exception = exception
@original = original
end

def to_html
"<p>" + Erubis::XmlHelper::escape_xml(@message) + "</p>" +
"<p>The original exception was:</p>" +
if @exception.respond_to? :to_html
@exception.to_html
else
Erubis::XmlHelper::escape_xml(@exception.to_s)
end
@original.to_html
end
end
end
66 changes: 54 additions & 12 deletions lib/exocora/sheet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ def initialize
@log_file = self.class.to_s.underscore + '.log'
end

# Add a validation to the validation stack
def self.add_validation(param, validation)
@@validations[param.to_sym] ||= []
@@validations[param.to_sym] << validation
validation
end

# Adds a validation condition for a parameter.
# validate :query { |q| q.size > 0 }
# validate :query, "is invalid" { |q|
Expand All @@ -31,17 +38,25 @@ def self.validate(param, message='must be valid', &block)
validation.when block
end

@@validations[param.to_s] ||= []
@@validations[param.to_s] << validation

validation
add_validation param, validation
end

# Ensure that a parameter is non-nil
def self.validate_presence_of(param, *params)
add_validation param, ValidationOfPresence.new(*params)
end

# Creates an instance of this sheet, and runs it.
def self.run
new.run
end

# Adds an error on a parameter
def add_error(param, error)
@errors[param] ||= []
@errors[param] << error
end

# Casts incoming CGI parameters to nil, numerics, collapses single-element
# arrays, and so forth.
def cast_params(params)
Expand All @@ -57,6 +72,10 @@ def cast_params(params)
end
end

# I prefer indexing by symbols
key = key.to_sym

# Store cast values
if values.empty?
cast[key] = nil
elsif values.size == 1
Expand Down Expand Up @@ -99,10 +118,11 @@ def process
end

# Breaks the normal rendering flow, and outputs an HTTP redirect header.
def redirect_to(uri_fragment)
def redirect_to(uri_fragment = @cgi.uri, params = {})
@headers['Status'] = '302 Moved'
@headers['Location'] = @cgi.full_uri_for uri_fragment
@headers['Location'] = url_for(@cgi.full_uri_for(uri_fragment), params)
output
exit
end

# Renders the erubis template for this action. Takes a hash of variables to
Expand All @@ -123,8 +143,13 @@ def render(context = Erubis::Context.new)
# Perform templating
begin
result = eruby.evaluate context
rescue
raise TemplateError.new("Encountered error processing template #{template_filename}.", $!)
rescue Exception => e
if e.backtrace.first =~ /\(erubis\):(\d+)/
message = "encountered error processing template #{template_filename} at line #{$1}"
else
message = "encountered error processing template #{template_filename}"
end
raise TemplateError.new(message, e)
end

# Output result
Expand Down Expand Up @@ -161,6 +186,13 @@ def run
end
end

# Returns a URL for the given url and parameters
def url_for(url = @cgi.uri, params = {})
url + params.inject('?') do |query_string, pair|
query_string << CGI::escape(pair[0].to_s) + '=' + CGI::escape(pair[1].to_s)
end
end

# Sets the name of the template to render.
def use_template(template)
@template = template
Expand All @@ -187,13 +219,23 @@ def validate(params = @params)
unless values.kind_of? Array
values = [values]
end

values.each do |value|

# if validation.kind_of? ValidationOfPresence
# # Make sure we perform a validation even if no parameters are
# # present.
# begin
# validation.validate values.first
# rescue ValidationError => e
# @errors[param] ||= []
# @errors[param] << e
# end
# end

values.each do |value|
begin
validation.validate(value)
rescue ValidationError => e
@errors[param] ||= []
@errors[param] << e
add_error param, e
end
end
end
Expand Down
22 changes: 21 additions & 1 deletion lib/exocora/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ module Exocora
# Otherwise, compares filter === value.

class Validation
DEFAULT_MESSAGE = 'must be present'

attr_accessor :message, :filter, :negate

def initialize(filter = Object, message = 'must be valid')
def initialize(filter=Object, message=DEFAULT_MESSAGE)
@filter = filter
@message = message
@negate = false
Expand Down Expand Up @@ -70,4 +72,22 @@ def if(filter, &block)
alias :with :if
alias :when :if
end

# ValidationOfPresence is a special validation which ensures its value
# is both present and non-empty.
class ValidationOfPresence < Validation
DEFAULT_MESSAGE = 'must be present'

def initialize(message=DEFAULT_MESSAGE)
@message = message
end

def validate(value = nil)
if value.nil? or (value.respond_to? :empty? and value.empty?)
raise ValidationError.new(value, @message)
else
value
end
end
end
end
2 changes: 1 addition & 1 deletion lib/exocora/version.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Exocora
APP_NAME = 'exocora'
APP_VERSION = '0.1.0'
APP_VERSION = '0.1.1'
APP_AUTHOR = 'aphyr'
APP_EMAIL = 'aphyr@aphyr.com'
APP_URL = 'http://aphyr.com/'
Expand Down
18 changes: 18 additions & 0 deletions lib/support/array.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Array
# Convert an array to a nice sentence. Method is called on each element of the array, and should return a string.
def to_sentence(method = :to_s)
case size
when 0
''
when 1
first.send method
when 2
first.send(method) + ' and ' + last.send(method)
else
sentence = self[1..-2].inject(first.send(method)) do |sentence, element|
sentence += ', ' + element.send(method)
end
sentence + ', and ' + last.send(method)
end
end
end
46 changes: 46 additions & 0 deletions lib/support/cgi.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
class CGI
HTTP_PORT = 80
HTTPS_PORT = 443

# Returns a full uri for a given uri fragment.
# full_uri_for 'foo.cgi' => 'http://localhost/dir/foo.cgi'
# full_uri_for '/images/' => 'http://localhost/images/'
def full_uri_for(fragment)
case fragment
when /^http:\/\//
# The fragment is a complete URI
fragment
when /^\//
# The fragment is relative to the root of this host
host_uri + fragment
else
# The fragment is relative to this directory
relative_uri + fragment
end
end

# Try to guess the uri for the host alone (http://host/)
def host_uri
uri = ''
case server_port
when HTTP_PORT
uri << 'http://' + server_name
when HTTPS_PORT
uri << 'https://' + server_name
else
uri << 'https://' + server_name + ':' + server_port
end
uri << '/'
end

# Try to guess the relative path for this request. (http://host/directory/)
def relative_uri
uri.sub(/[^\/]*$/, '')
end

# Try to guess the full uri for this script (http://host/directory/script.cgi)
def uri
host_uri.chop + script_name
end

end
62 changes: 62 additions & 0 deletions lib/support/string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Provides support methods for string manipulation

class String
# Converts dashes, underscores, and spaces to camelcase
#
# "foo_bar_baz".camelcase => "FooBarBaz"
# "FooBarBaz".camelcase(false) => "fooBarBaz"
def camelcase(first_letter_capitalized = true)
# Convert separators
camelcase = gsub(/[ _-]([^ _-])/) do |match|
$1.upcase
end

# Convert first letter
if first_letter_capitalized
camelcase[0..0].upcase + camelcase[1..-1]
else
camelcase[0..0].downcase + camelcase[1..-1]
end
end

# Removes module and class prefixes
#
# "Foo::Bar::Baz".demodulize => "Baz"
def demodulize
self[/([^:]+)$/]
end

# Splits the string into module and class components
#
# "Foo::Bar::Baz".module_split => ["Foo", "Bar", "Baz"]
def module_split
self.split /::/
end

# Converts the string into a conventional path.
def module_path
File.join(module_split.map {|e| e.underscore })
end

# Converts dashes, spaces, and capitals to underscore separators.
#
# "FooBar-Baz Whee".underscore => 'foo_bar_baz_whee'
def underscore(force_lower_case = true)
# Convert separators
underscore = gsub(/[ -]/, '_')

# Convert capitals
underscore.gsub!(/(.)([A-Z])/) do |match|
$1 + '_' + $2
end

# Drop double underscores
underscore.gsub!(/_+/, '_')

if force_lower_case
underscore.downcase
else
underscore
end
end
end
Binary file modified pkg/exocora-0.1.0.gem
Binary file not shown.

0 comments on commit a92910b

Please sign in to comment.