exocora 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +27 -0
- data/examples/hello_world.cgi +12 -0
- data/examples/hello_world.rhtml +8 -0
- data/examples/request.cgi +12 -0
- data/examples/request.rhtml +35 -0
- data/examples/validation.cgi +20 -0
- data/examples/validation.rhtml +20 -0
- data/lib/array.rb +18 -0
- data/lib/cgi.rb +46 -0
- data/lib/exocora/errors.rb +47 -0
- data/lib/exocora/sheet.rb +205 -0
- data/lib/exocora/support.rb +4 -0
- data/lib/exocora/validation.rb +73 -0
- data/lib/exocora/version.rb +8 -0
- data/lib/exocora.rb +26 -0
- data/lib/string.rb +62 -0
- metadata +70 -0
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2008 Aphyr <aphyr@aphyr.com>.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of the <ORGANIZATION> nor the names of its contributors
|
15
|
+
may be used to endorse or promote products derived from this software
|
16
|
+
without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Request Test</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>Inferred URI information</h1>
|
7
|
+
<ul>
|
8
|
+
<li>Host URI: <%== @r.host_uri %></li>
|
9
|
+
<li>Relative URI: <%==@r.relative_uri %></li>
|
10
|
+
<li>Full URI: <%== @r.uri %></li>
|
11
|
+
</ul>
|
12
|
+
|
13
|
+
<h1>Request Parameters</h1>
|
14
|
+
<% names = [
|
15
|
+
'AUTH_TYPE', 'HTTP_HOST', 'REMOTE_IDENT',
|
16
|
+
'CONTENT_LENGTH', 'HTTP_NEGOTIATE', 'REMOTE_USER',
|
17
|
+
'CONTENT_TYPE', 'HTTP_PRAGMA', 'REQUEST_METHOD',
|
18
|
+
'GATEWAY_INTERFACE', 'HTTP_REFERER', 'SCRIPT_NAME',
|
19
|
+
'HTTP_ACCEPT', 'HTTP_USER_AGENT', 'SERVER_NAME',
|
20
|
+
'HTTP_ACCEPT_CHARSET', 'PATH_INFO', 'SERVER_PORT',
|
21
|
+
'HTTP_ACCEPT_ENCODING', 'PATH_TRANSLATED', 'SERVER_PROTOCOL',
|
22
|
+
'HTTP_ACCEPT_LANGUAGE', 'QUERY_STRING', 'SERVER_SOFTWARE',
|
23
|
+
'HTTP_CACHE_CONTROL', 'REMOTE_ADDR',
|
24
|
+
'HTTP_FROM', 'REMOTE_HOST'
|
25
|
+
] %>
|
26
|
+
<table>
|
27
|
+
<% names.each do |name| %>
|
28
|
+
<tr>
|
29
|
+
<td><%== name %></td>
|
30
|
+
<td><%== @r.send(name.downcase.sub(/^http_/, '')).inspect %></td>
|
31
|
+
</tr>
|
32
|
+
<% end %>
|
33
|
+
</table>
|
34
|
+
</body>
|
35
|
+
</html>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/exocora'
|
4
|
+
#require '/home/aphyr/exocora/lib/exocora'
|
5
|
+
|
6
|
+
class Validation < Exocora::Sheet
|
7
|
+
|
8
|
+
validate(:short) do |value|
|
9
|
+
value.length < 4
|
10
|
+
end
|
11
|
+
|
12
|
+
validate(:num, "must be a number").when Numeric
|
13
|
+
validate(:empty).unless(lambda { |s| s.size > 0 }).because("must be empty")
|
14
|
+
|
15
|
+
def process
|
16
|
+
{:params => params, :errors => errors}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Validation.run
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Validation Test</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>Errors</h1>
|
7
|
+
<ul>
|
8
|
+
<% @errors.each do |name, errors| %>
|
9
|
+
<li><%= name %> <%= errors.to_sentence %>.</li>
|
10
|
+
<% end %>
|
11
|
+
</ul>
|
12
|
+
|
13
|
+
<h1>All Parameters</h1>
|
14
|
+
<ul>
|
15
|
+
<% @params.each do |name, value| %>
|
16
|
+
<li><%= name %>: <%= value.inspect %></li>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
</body>
|
20
|
+
</html>
|
data/lib/array.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Array
|
2
|
+
# Convert an array to a nice sentence. Method is called on each element of the array, and should return a string.
|
3
|
+
def to_sentence(method = :to_s)
|
4
|
+
case size
|
5
|
+
when 0
|
6
|
+
''
|
7
|
+
when 1
|
8
|
+
first.send method
|
9
|
+
when 2
|
10
|
+
first.send(method) + ' and ' + last.send(method)
|
11
|
+
else
|
12
|
+
sentence = self[1..-2].inject(first.send(method)) do |sentence, element|
|
13
|
+
sentence += ', ' + element.send(method)
|
14
|
+
end
|
15
|
+
sentence + ', and ' + last.send(method)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/cgi.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
class CGI
|
2
|
+
HTTP_PORT = 80
|
3
|
+
HTTPS_PORT = 443
|
4
|
+
|
5
|
+
# Returns a full uri for a given uri fragment.
|
6
|
+
# full_uri_for 'foo.cgi' => 'http://localhost/dir/foo.cgi'
|
7
|
+
# full_uri_for '/images/' => 'http://localhost/images/'
|
8
|
+
def full_uri_for(fragment)
|
9
|
+
case fragment
|
10
|
+
when /^http:\/\//
|
11
|
+
# The fragment is a complete URI
|
12
|
+
fragment
|
13
|
+
when /^\//
|
14
|
+
# The fragment is relative to the root of this host
|
15
|
+
host_uri + fragment
|
16
|
+
else
|
17
|
+
# The fragment is relative to this directory
|
18
|
+
relative_uri + fragment
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Try to guess the uri for the host alone (http://host/)
|
23
|
+
def host_uri
|
24
|
+
uri = ''
|
25
|
+
case server_port
|
26
|
+
when HTTP_PORT
|
27
|
+
uri << 'http://' + server_name
|
28
|
+
when HTTPS_PORT
|
29
|
+
uri << 'https://' + server_name
|
30
|
+
else
|
31
|
+
uri << 'https://' + server_name + ':' + server_port
|
32
|
+
end
|
33
|
+
uri
|
34
|
+
end
|
35
|
+
|
36
|
+
# Try to guess the relative path for this request. (http://host/directory/)
|
37
|
+
def relative_uri
|
38
|
+
uri.sub(/[^\/]*$/, '')
|
39
|
+
end
|
40
|
+
|
41
|
+
# Try to guess the full uri for this script (http://host/directory/script.cgi)
|
42
|
+
def uri
|
43
|
+
host_uri.chop + script_name
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Exception
|
2
|
+
def to_html
|
3
|
+
"<p><b>#{self.class}</b>: #{Erubis::XmlHelper::escape_xml(to_s)}</p>" +
|
4
|
+
"<p>Backtrace:" +
|
5
|
+
'<ul>' + backtrace.map { |line|
|
6
|
+
'<li><code>' + Erubis::XmlHelper::escape_xml(line) + '</code></li>'
|
7
|
+
}.join + '</ul></p>'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Exocora
|
12
|
+
# An error encountered when validating data
|
13
|
+
class ValidationError < RuntimeError
|
14
|
+
attr_accessor :value, :message
|
15
|
+
def initialize(value, message = 'is invalid')
|
16
|
+
@value = value
|
17
|
+
@message = message
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Represents an error encountered in an Exocora sheet.
|
26
|
+
class SheetError < RuntimeError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Represents an error encountered when processing a template.
|
30
|
+
class TemplateError < RuntimeError
|
31
|
+
attr_accessor :message, :exception
|
32
|
+
def initialize(message, exception)
|
33
|
+
@message = message
|
34
|
+
@exception = exception
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_html
|
38
|
+
"<p>" + Erubis::XmlHelper::escape_xml(@message) + "</p>" +
|
39
|
+
"<p>The original exception was:</p>" +
|
40
|
+
if @exception.respond_to? :to_html
|
41
|
+
@exception.to_html
|
42
|
+
else
|
43
|
+
Erubis::XmlHelper::escape_xml(@exception.to_s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module Exocora
|
2
|
+
# A single CGI script. Requests are processed through a chain of actions
|
3
|
+
#
|
4
|
+
# validate - extract and sanitize data from the CGI request
|
5
|
+
# process - do stuff
|
6
|
+
# render - render to the specified template.
|
7
|
+
class Sheet
|
8
|
+
# Parameters with validations
|
9
|
+
@@validations = Hash.new
|
10
|
+
|
11
|
+
attr_reader :params, :errors
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@cgi = CGI.new
|
15
|
+
@headers = {
|
16
|
+
}
|
17
|
+
|
18
|
+
@params = {}
|
19
|
+
@errors = {}
|
20
|
+
|
21
|
+
@template = self.class.to_s.module_path
|
22
|
+
@log_file = self.class.to_s.underscore + '.log'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds a validation condition for a parameter.
|
26
|
+
# validate :query { |q| q.size > 0 }
|
27
|
+
# validate :query, "is invalid" { |q|
|
28
|
+
def self.validate(param, message='must be valid', &block)
|
29
|
+
validation = Validation.new(nil, message)
|
30
|
+
if block_given?
|
31
|
+
validation.when block
|
32
|
+
end
|
33
|
+
|
34
|
+
@@validations[param.to_s] ||= []
|
35
|
+
@@validations[param.to_s] << validation
|
36
|
+
|
37
|
+
validation
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates an instance of this sheet, and runs it.
|
41
|
+
def self.run
|
42
|
+
new.run
|
43
|
+
end
|
44
|
+
|
45
|
+
# Casts incoming CGI parameters to nil, numerics, collapses single-element
|
46
|
+
# arrays, and so forth.
|
47
|
+
def cast_params(params)
|
48
|
+
cast = {}
|
49
|
+
params.each do |key, values|
|
50
|
+
cast_values = values.map do |value|
|
51
|
+
if value =~ /^\d+$/
|
52
|
+
value.to_i
|
53
|
+
elsif value =~ /^[\d.]+$/
|
54
|
+
value.to_f
|
55
|
+
else
|
56
|
+
value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if values.empty?
|
61
|
+
cast[key] = nil
|
62
|
+
elsif values.size == 1
|
63
|
+
cast[key] = cast_values.first
|
64
|
+
else
|
65
|
+
cast[key] = cast_values
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
cast
|
70
|
+
end
|
71
|
+
|
72
|
+
# Logs a message to a file.
|
73
|
+
def log(message)
|
74
|
+
File.open(@log_file, 'a') do |file|
|
75
|
+
file.puts message
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Chooses the file for logging.
|
80
|
+
def log_to(file)
|
81
|
+
@log_file = file
|
82
|
+
end
|
83
|
+
|
84
|
+
# Sends output to the client. The first time output is called, it sends an
|
85
|
+
# HTTP header first.
|
86
|
+
def output(string)
|
87
|
+
unless @headers_sent
|
88
|
+
puts @cgi.header(@headers)
|
89
|
+
@headers_sent = true
|
90
|
+
end
|
91
|
+
|
92
|
+
puts string
|
93
|
+
end
|
94
|
+
|
95
|
+
# Process is the meat of the script.
|
96
|
+
# It returns a hash like {:a => 2}, which become instance
|
97
|
+
# variables @a = 2 in the template.
|
98
|
+
def process
|
99
|
+
end
|
100
|
+
|
101
|
+
# Breaks the normal rendering flow, and outputs an HTTP redirect header.
|
102
|
+
def redirect_to(uri_fragment)
|
103
|
+
@headers['Status'] = '302 Moved'
|
104
|
+
@headers['Location'] = @cgi.full_uri_for uri_fragment
|
105
|
+
output
|
106
|
+
end
|
107
|
+
|
108
|
+
# Renders the erubis template for this action. Takes a hash of variables to
|
109
|
+
# render. The default template is determined by underscoring this sheet's
|
110
|
+
# class name, but another template can be specified using #template.
|
111
|
+
def render(context = Erubis::Context.new)
|
112
|
+
# Read template data
|
113
|
+
template_filename = "#{@template}.rhtml"
|
114
|
+
begin
|
115
|
+
template = File.read(template_filename)
|
116
|
+
rescue Errno::ENOENT
|
117
|
+
raise ScriptError.new("Template #{template_filename} does not exist!")
|
118
|
+
end
|
119
|
+
|
120
|
+
# Prepare template and variables
|
121
|
+
eruby = Erubis::Eruby.new template
|
122
|
+
|
123
|
+
# Perform templating
|
124
|
+
begin
|
125
|
+
result = eruby.evaluate context
|
126
|
+
rescue
|
127
|
+
raise TemplateError.new("Encountered error processing template #{template_filename}.", $!)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Output result
|
131
|
+
output result
|
132
|
+
end
|
133
|
+
|
134
|
+
# Accessor for the CGI object
|
135
|
+
def request
|
136
|
+
@cgi
|
137
|
+
end
|
138
|
+
|
139
|
+
# This is the action which initiates processing of the script.
|
140
|
+
def run
|
141
|
+
begin
|
142
|
+
# Cast parameters
|
143
|
+
@params = cast_params @cgi.params
|
144
|
+
|
145
|
+
# Check parameters
|
146
|
+
validate
|
147
|
+
|
148
|
+
# Run process
|
149
|
+
context = process
|
150
|
+
|
151
|
+
# Render output
|
152
|
+
render context
|
153
|
+
rescue
|
154
|
+
# Handle errors
|
155
|
+
output "<html><head><title>Exocora Error</title></head><body>"
|
156
|
+
output "<h1>#{$!.class}</h1>"
|
157
|
+
|
158
|
+
output $!.to_html
|
159
|
+
|
160
|
+
output "</body></html>"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Sets the name of the template to render.
|
165
|
+
def use_template(template)
|
166
|
+
@template = template
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns true if the specified (or all) parameters have no errors.
|
170
|
+
def valid?(param = nil)
|
171
|
+
if param.nil?
|
172
|
+
@errors.empty?
|
173
|
+
else
|
174
|
+
@errors[param].nil? or @errors[param].empty?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Validates CGI parameters according to @@validations.
|
179
|
+
#
|
180
|
+
# Parameters which fail validation have entries recorderd in @errors.
|
181
|
+
def validate(params = @params)
|
182
|
+
@@validations.each do |param, validations|
|
183
|
+
validations.each do |validation|
|
184
|
+
values = params[param]
|
185
|
+
|
186
|
+
# If there are multiple values, check each one.
|
187
|
+
unless values.kind_of? Array
|
188
|
+
values = [values]
|
189
|
+
end
|
190
|
+
|
191
|
+
values.each do |value|
|
192
|
+
begin
|
193
|
+
validation.validate(value)
|
194
|
+
rescue ValidationError => e
|
195
|
+
@errors[param] ||= []
|
196
|
+
@errors[param] << e
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
params
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Exocora
|
2
|
+
# Validates a parameter. If a block is provided, the block is applied to
|
3
|
+
# values passed to apply; the return value determines if the value is valid.
|
4
|
+
# Otherwise, compares filter === value.
|
5
|
+
|
6
|
+
class Validation
|
7
|
+
attr_accessor :message, :filter, :negate
|
8
|
+
|
9
|
+
def initialize(filter = Object, message = 'must be valid')
|
10
|
+
@filter = filter
|
11
|
+
@message = message
|
12
|
+
@negate = false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Change the message given when this validation fails.
|
16
|
+
def because(message)
|
17
|
+
@message = message
|
18
|
+
self
|
19
|
+
end
|
20
|
+
alias :explain :because
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#{@message} #{@negate ? 'unless' : 'when'} #{@filter.inspect}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds the opposite of this filter
|
27
|
+
def unless(filter, &block)
|
28
|
+
@filter = block_given? ? block : filter
|
29
|
+
@negate = true
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Applies this validation to a valie. Returns true, or raises a
|
34
|
+
# ValidationError.
|
35
|
+
def validate(value)
|
36
|
+
# Treat the filter as a function or a rubric
|
37
|
+
if @filter.respond_to? :call
|
38
|
+
method = :call
|
39
|
+
else
|
40
|
+
method = :===
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check the value
|
44
|
+
begin
|
45
|
+
result = @filter.send method, value
|
46
|
+
rescue
|
47
|
+
result = false
|
48
|
+
end
|
49
|
+
|
50
|
+
# If we're a negative filter, negate the result
|
51
|
+
if negate
|
52
|
+
result = ! result
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return
|
56
|
+
if result
|
57
|
+
value
|
58
|
+
else
|
59
|
+
raise ValidationError.new(value, @message)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Sets the filter used
|
64
|
+
def if(filter, &block)
|
65
|
+
@filter = block_given? ? block : filter
|
66
|
+
@negate = false
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
alias :with :if
|
71
|
+
alias :when :if
|
72
|
+
end
|
73
|
+
end
|
data/lib/exocora.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Exocora is an obscure species of spider, in the sheet weaver family.
|
2
|
+
#
|
3
|
+
# It is also a small web framework for writing CGI scripts, or "sheets". Each
|
4
|
+
# request has a three-stage lifecycle, in which it extracts and checks incoming
|
5
|
+
# data for safety, performs some operations, and renders a templated result.
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'cgi'
|
9
|
+
require 'erubis'
|
10
|
+
|
11
|
+
APPDIR = File.dirname(File.expand_path($0))
|
12
|
+
BASEDIR = File.dirname(File.expand_path(__FILE__))
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(BASEDIR)
|
15
|
+
$LOAD_PATH.uniq!
|
16
|
+
|
17
|
+
require 'string'
|
18
|
+
require 'array'
|
19
|
+
require 'cgi'
|
20
|
+
require 'exocora/support'
|
21
|
+
require 'exocora/errors'
|
22
|
+
require 'exocora/validation'
|
23
|
+
require 'exocora/sheet'
|
24
|
+
|
25
|
+
module Exocora
|
26
|
+
end
|
data/lib/string.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Provides support methods for string manipulation
|
2
|
+
|
3
|
+
class String
|
4
|
+
# Converts dashes, underscores, and spaces to camelcase
|
5
|
+
#
|
6
|
+
# "foo_bar_baz".camelcase => "FooBarBaz"
|
7
|
+
# "FooBarBaz".camelcase(false) => "fooBarBaz"
|
8
|
+
def camelcase(first_letter_capitalized = true)
|
9
|
+
# Convert separators
|
10
|
+
camelcase = gsub(/[ _-]([^ _-])/) do |match|
|
11
|
+
$1.upcase
|
12
|
+
end
|
13
|
+
|
14
|
+
# Convert first letter
|
15
|
+
if first_letter_capitalized
|
16
|
+
camelcase[0..0].upcase + camelcase[1..-1]
|
17
|
+
else
|
18
|
+
camelcase[0..0].downcase + camelcase[1..-1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Removes module and class prefixes
|
23
|
+
#
|
24
|
+
# "Foo::Bar::Baz".demodulize => "Baz"
|
25
|
+
def demodulize
|
26
|
+
self[/([^:]+)$/]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Splits the string into module and class components
|
30
|
+
#
|
31
|
+
# "Foo::Bar::Baz".module_split => ["Foo", "Bar", "Baz"]
|
32
|
+
def module_split
|
33
|
+
self.split /::/
|
34
|
+
end
|
35
|
+
|
36
|
+
# Converts the string into a conventional path.
|
37
|
+
def module_path
|
38
|
+
File.join(module_split.map {|e| e.underscore })
|
39
|
+
end
|
40
|
+
|
41
|
+
# Converts dashes, spaces, and capitals to underscore separators.
|
42
|
+
#
|
43
|
+
# "FooBar-Baz Whee".underscore => 'foo_bar_baz_whee'
|
44
|
+
def underscore(force_lower_case = true)
|
45
|
+
# Convert separators
|
46
|
+
underscore = gsub(/[ -]/, '_')
|
47
|
+
|
48
|
+
# Convert capitals
|
49
|
+
underscore.gsub!(/(.)([A-Z])/) do |match|
|
50
|
+
$1 + '_' + $2
|
51
|
+
end
|
52
|
+
|
53
|
+
# Drop double underscores
|
54
|
+
underscore.gsub!(/_+/, '_')
|
55
|
+
|
56
|
+
if force_lower_case
|
57
|
+
underscore.downcase
|
58
|
+
else
|
59
|
+
underscore
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: exocora
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2008-03-27 00:00:00 -05:00
|
8
|
+
summary: A small framework for cgi scripts
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: aphyr@aphyr.com
|
12
|
+
homepage: http://aphyr.com/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.8.6
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- aphyr
|
31
|
+
files:
|
32
|
+
- examples/validation.rhtml
|
33
|
+
- examples/hello_world.rhtml
|
34
|
+
- examples/validation.cgi
|
35
|
+
- examples/hello_world.cgi
|
36
|
+
- examples/request.rhtml
|
37
|
+
- examples/request.cgi
|
38
|
+
- lib/cgi.rb
|
39
|
+
- lib/array.rb
|
40
|
+
- lib/exocora.rb
|
41
|
+
- lib/string.rb
|
42
|
+
- lib/exocora
|
43
|
+
- lib/exocora/support.rb
|
44
|
+
- lib/exocora/validation.rb
|
45
|
+
- lib/exocora/sheet.rb
|
46
|
+
- lib/exocora/errors.rb
|
47
|
+
- lib/exocora/version.rb
|
48
|
+
- LICENSE
|
49
|
+
test_files: []
|
50
|
+
|
51
|
+
rdoc_options: []
|
52
|
+
|
53
|
+
extra_rdoc_files: []
|
54
|
+
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
dependencies:
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: erubis
|
64
|
+
version_requirement:
|
65
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.5.0
|
70
|
+
version:
|