Class: Exocora::Sheet

Inherits:
Object
  • Object
show all
Defined in:
lib/exocora/sheet.rb

Overview

A single CGI script. Requests are processed through a chain of actions

validate - extract and sanitize data from the CGI request process - do stuff render - render to the specified template.

Constant Summary collapse

@@validations =

Parameters with validations

Hash.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSheet

Returns a new instance of Sheet.



13
14
15
16
17
18
19
20
21
22
23
# File 'lib/exocora/sheet.rb', line 13

def initialize
  @cgi = CGI.new
  @headers = {
  }
  
  @params = {}
  @errors = {}
  
  @template = self.class.to_s.module_path
  @log_file = self.class.to_s.underscore + '.log'
end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



11
12
13
# File 'lib/exocora/sheet.rb', line 11

def errors
  @errors
end

#paramsObject (readonly)

Returns the value of attribute params.



11
12
13
# File 'lib/exocora/sheet.rb', line 11

def params
  @params
end

Class Method Details

.add_validation(param, validation) ⇒ Object

Add a validation to the validation stack



26
27
28
29
30
# File 'lib/exocora/sheet.rb', line 26

def self.add_validation(param, validation)
  @@validations[param.to_sym] ||= []
  @@validations[param.to_sym] << validation
  validation
end

.runObject

Creates an instance of this sheet, and runs it.



50
51
52
# File 'lib/exocora/sheet.rb', line 50

def self.run
  new.run
end

.validate(param, message = 'must be valid', &block) ⇒ Object

Adds a validation condition for a parameter. validate :query { |q| q.size > 0 } validate :query, “is invalid” { |q|



35
36
37
38
39
40
41
42
# File 'lib/exocora/sheet.rb', line 35

def self.validate(param, message='must be valid', &block)
  validation = Validation.new(nil, message)
  if block_given?
    validation.when block
  end

  add_validation param, validation
end

.validate_presence_of(param, *params) ⇒ Object

Ensure that a parameter is non-nil



45
46
47
# File 'lib/exocora/sheet.rb', line 45

def self.validate_presence_of(param, *params)
  add_validation param, ValidationOfPresence.new(*params)
end

Instance Method Details

#add_error(param, error) ⇒ Object

Adds an error on a parameter



55
56
57
58
# File 'lib/exocora/sheet.rb', line 55

def add_error(param, error)
  @errors[param] ||= []
  @errors[param] << error
end

#cast_params(params) ⇒ Object

Casts incoming CGI parameters to nil, numerics, collapses single-element arrays, and so forth.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/exocora/sheet.rb', line 62

def cast_params(params)
  cast = {}
  params.each do |key, values|
    cast_values = values.map do |value|
      if value =~ /^\d+$/
        value.to_i
      elsif value =~ /^[\d.]+$/
        value.to_f
      else
        value
      end
    end

    # I prefer indexing by symbols
    key = key.to_sym

    # Store cast values
    if values.empty?
      cast[key] = nil
    elsif values.size == 1
      cast[key] = cast_values.first
    else
      cast[key] = cast_values
    end
  end

  cast
end

#log(message) ⇒ Object

Logs a message to a file.



92
93
94
95
96
# File 'lib/exocora/sheet.rb', line 92

def log(message)
  File.open(@log_file, 'a') do |file|
    file.puts message
  end
end

#log_to(file) ⇒ Object

Chooses the file for logging.



99
100
101
# File 'lib/exocora/sheet.rb', line 99

def log_to(file)
  @log_file = file
end

#output(string) ⇒ Object

Sends output to the client. The first time output is called, it sends an HTTP header first.



105
106
107
108
109
110
111
112
# File 'lib/exocora/sheet.rb', line 105

def output(string)
  unless @headers_sent
    puts @cgi.header(@headers)
    @headers_sent = true
  end

  puts string
end

#processObject

Process is the meat of the script. It returns a hash like => 2, which become instance variables @a = 2 in the template.



117
118
# File 'lib/exocora/sheet.rb', line 117

def process
end

#redirect_to(uri_fragment = @cgi.uri, params = {}) ⇒ Object

Breaks the normal rendering flow, and outputs an HTTP redirect header.



121
122
123
124
125
126
# File 'lib/exocora/sheet.rb', line 121

def redirect_to(uri_fragment = @cgi.uri, params = {})
  @headers['Status'] = '302 Moved'
  @headers['Location'] = url_for(@cgi.full_uri_for(uri_fragment), params)
  output
  exit
end

#render(context = Erubis::Context.new) ⇒ Object

Renders the erubis template for this action. Takes a hash of variables to render. The default template is determined by underscoring this sheet’s class name, but another template can be specified using #template.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/exocora/sheet.rb', line 131

def render(context = Erubis::Context.new)
  # Read template data
  template_filename = "#{@template}.rhtml"
  begin
    template = File.read(template_filename)
  rescue Errno::ENOENT
    raise ScriptError.new("Template #{template_filename} does not exist!")
  end

  # Prepare template and variables
  eruby = Erubis::Eruby.new template
  
  # Perform templating
  begin
    result = eruby.evaluate context
  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
  output result 
end

#requestObject

Accessor for the CGI object



160
161
162
# File 'lib/exocora/sheet.rb', line 160

def request
  @cgi
end

#runObject

This is the action which initiates processing of the script.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/exocora/sheet.rb', line 165

def run
  begin
    # Cast parameters
    @params = cast_params @cgi.params

    # Check parameters
    validate

    # Run process
    context = process

    # Render output
    render context
  rescue
    # Handle errors
    output "<html><head><title>Exocora Error</title></head><body>"
    output "<h1>#{$!.class}</h1>"

    output $!.to_html

    output "</body></html>"
  end
end

#url_for(url = @cgi.uri, params = {}) ⇒ Object

Returns a URL for the given url and parameters



190
191
192
193
194
# File 'lib/exocora/sheet.rb', line 190

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

#use_template(template) ⇒ Object

Sets the name of the template to render.



197
198
199
# File 'lib/exocora/sheet.rb', line 197

def use_template(template)
  @template = template
end

#valid?(param = nil) ⇒ Boolean

Returns true if the specified (or all) parameters have no errors.

Returns:

  • (Boolean)


202
203
204
205
206
207
208
# File 'lib/exocora/sheet.rb', line 202

def valid?(param = nil)
  if param.nil?
    @errors.empty?
  else
    @errors[param].nil? or @errors[param].empty?
  end
end

#validate(params = @params) ⇒ Object

Validates CGI parameters according to @@validations.

Parameters which fail validation have entries recorderd in @errors.



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
244
245
# File 'lib/exocora/sheet.rb', line 213

def validate(params = @params)
  @@validations.each do |param, validations|
    validations.each do |validation|
      values = params[param]

      # If there are multiple values, check each one.
      unless values.kind_of? Array
        values = [values]
      end

#          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
          add_error param, e
        end
      end
    end
  end

  params
end