cgi-spa 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2009 Sam Ruby <rubys@intertwingly.net>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README CHANGED
@@ -1,23 +1,55 @@
1
- Provides the following globals:
2
- $cgi - Common Gateway Interface
3
- $params - Access to parameters
4
- $x - XmlBuilder instance
1
+ = cgi-spa: Common Gateway Interface for Single Page Applications
5
2
 
6
- $USER - user
7
- $HOME - user's home directory
8
- $HOST - server host
3
+ CGI-SPA provides a number of globals, helper methods, and monkey patches which
4
+ simplify the development of single page applications in the form of CGI
5
+ scripts.
9
6
 
10
- $HTTP_GET - request is an HTTP GET
11
- $HTTP_POST - request is an HTTP POST
12
- $XHR_JSON - request is XmlHttpRequest for JSON
7
+ == Globals provided
8
+ * $cgi - Common Gateway Interface
9
+ * $param - Access to parameters (read-only OpenStruct like interface)
10
+ * $x - XmlBuilder instance
11
+ * $USER - Host user id
12
+ * $HOME - Home directory
13
+ * $SERVER- Server name
14
+ * SELF - Request URI
15
+ * SELF? - Request URI with '?' appended (avoids spoiling the cache)
13
16
 
14
- Helper methods:
15
- submit: runs command (or block) as a deamon process
17
+ * $USER - user
18
+ * $HOME - user's home directory
19
+ * $HOST - server host
16
20
 
17
- MonkeyPatches to Builder:
18
- indented_text: matches text indentation to markup
19
- indented_data: useful for script and styles in HTML syntax
20
- traceback! : formats an exception traceback
21
+ * $HTTP_GET - request is an HTTP GET
22
+ * $HTTP_POST - request is an HTTP POST
23
+ * $XHR_JSON - request is XmlHttpRequest for JSON
24
+ * $XHTML - user agent accepts XHTML responses
21
25
 
22
- method_missing: patched to ensure open tags are closed
26
+ == HTML methods
27
+ * style! - argument is indented text/data
28
+ * script! - argument is indented text/data
23
29
 
30
+ == CGI methods
31
+ * json - produce JSON output using the block specified
32
+ * json! - produce JSON output using the block specified and exit
33
+ * html - produce HTML output using the block specified
34
+ * html! - produce HTML output using the block specified and exit
35
+ * post - execute block only if method is POST
36
+ * post! - if POST, produce HTML output using the block specified and exit
37
+
38
+ == Helper methods
39
+ * submit: runs command (or block) as a deamon process
40
+
41
+ == Builder extensions
42
+ * indented_text: matches text indentation to markup
43
+ * indented_data: useful for script and styles in HTML syntax
44
+ * traceback!: formats an exception traceback
45
+ * method_missing: patched to ensure open tags are closed
46
+
47
+ == Command line options
48
+ When run from the command line, CGI name=value pairs can be specified.
49
+ Additionally, the following options are supported:
50
+ * --html: HTML (HTTP GET) output is expected
51
+ * --post: HTML (HTTP POST) output is expected
52
+ * --json: JSON (XML HTTP Request) output is expected
53
+ * --xhtml: XHTML output is expected
54
+ * --prompt: prompt for key/value pairs using stdin
55
+ * --install=path: produce an suexec-callable wrapper script
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- require File.expand_path(File.dirname(__FILE__) + "/lib/version")
5
+ require File.expand_path(File.dirname(__FILE__) + "/lib/cgi-spa/version")
6
6
 
7
7
  Echoe.new('cgi-spa', CgiSpa::VERSION::STRING) do |p|
8
8
  p.summary = "CGI Single Page Applications"
data/cgi-spa.gemspec CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{cgi-spa}
5
- s.version = "0.2.1"
5
+ s.version = "0.3.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Sam Ruby"]
9
- s.date = %q{2009-12-18}
9
+ s.date = %q{2009-12-19}
10
10
  s.description = %q{ Provides a number of globals, helper methods, and monkey patches which
11
11
  simplify the development of single page applications in the form of
12
12
  CGI scripts.
13
13
  }
14
14
  s.email = %q{rubys@intertwingly.net}
15
- s.extra_rdoc_files = ["README", "lib/cgi-spa.rb", "lib/version.rb"]
16
- s.files = ["Manifest", "README", "Rakefile", "cgi-spa.gemspec", "lib/cgi-spa.rb", "lib/version.rb"]
15
+ s.extra_rdoc_files = ["COPYING", "README", "lib/cgi-spa.rb", "lib/cgi-spa/builder.rb", "lib/cgi-spa/cgi-methods.rb", "lib/cgi-spa/environment.rb", "lib/cgi-spa/html-methods.rb", "lib/cgi-spa/installation.rb", "lib/cgi-spa/job-control.rb", "lib/cgi-spa/version.rb"]
16
+ s.files = ["COPYING", "Manifest", "README", "Rakefile", "cgi-spa.gemspec", "lib/cgi-spa.rb", "lib/cgi-spa/builder.rb", "lib/cgi-spa/cgi-methods.rb", "lib/cgi-spa/environment.rb", "lib/cgi-spa/html-methods.rb", "lib/cgi-spa/installation.rb", "lib/cgi-spa/job-control.rb", "lib/cgi-spa/version.rb"]
17
17
  s.homepage = %q{http://github.com/rubys/cgi-spa}
18
18
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cgi-spa", "--main", "README"]
19
19
  s.require_paths = ["lib"]
@@ -0,0 +1,102 @@
1
+ # add indented_text!, indented_data! and traceback! methods to builder
2
+ module Builder
3
+ class XmlMarkup
4
+ unless method_defined? :indented_text!
5
+ def indented_text!(text)
6
+ indented_data!(text) {|data| text! data}
7
+ end
8
+ end
9
+
10
+ unless method_defined? :indented_data!
11
+ def indented_data!(data)
12
+ return if data.strip.length == 0
13
+ lines = data.gsub(/\n\s*\n/,"\n")
14
+ unindent = lines.scan(/^ */).map {|str| str.length}.min
15
+
16
+ before = Regexp.new('^'.ljust(unindent+1))
17
+ after = " " * (@level * @indent)
18
+ data = data.gsub(before, after)
19
+
20
+ if block_given?
21
+ yield data
22
+ else
23
+ self << data
24
+ end
25
+
26
+ _newline unless data =~ /\s$/
27
+ end
28
+ end
29
+
30
+ unless method_defined? :traceback!
31
+ def traceback!(exception=$!, klass='traceback')
32
+ pre :class=>klass do
33
+ text! exception.inspect
34
+ _newline
35
+ exception.backtrace.each {|frame| text!((' '*@indent)+frame + "\n")}
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # monkey patch to ensure that tags are closed
43
+ test =
44
+ Builder::XmlMarkup.new.html do |x|
45
+ x.body do
46
+ begin
47
+ x.p do
48
+ raise Exception.new('boom')
49
+ end
50
+ rescue Exception => e
51
+ x.pre e
52
+ end
53
+ end
54
+ end
55
+
56
+ if test.index('<p>') and !test.index('</p>')
57
+ module Builder
58
+ class XmlMarkup
59
+ def method_missing(sym, *args, &block)
60
+ text = nil
61
+ attrs = nil
62
+ sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
63
+ args.each do |arg|
64
+ case arg
65
+ when Hash
66
+ attrs ||= {}
67
+ attrs.merge!(arg)
68
+ else
69
+ text ||= ''
70
+ text << arg.to_s
71
+ end
72
+ end
73
+ if block
74
+ unless text.nil?
75
+ raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
76
+ end
77
+ _indent
78
+ _start_tag(sym, attrs)
79
+ _newline
80
+ begin ### Added
81
+ _nested_structures(block)
82
+ ensure ### Added
83
+ _indent
84
+ _end_tag(sym)
85
+ _newline
86
+ end ### Added
87
+ elsif text.nil?
88
+ _indent
89
+ _start_tag(sym, attrs, true)
90
+ _newline
91
+ else
92
+ _indent
93
+ _start_tag(sym, attrs)
94
+ text! text
95
+ _end_tag(sym)
96
+ _newline
97
+ end
98
+ @target
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,51 @@
1
+ # produce json
2
+ def $cgi.json
3
+ return unless $XHR_JSON
4
+ $cgi.out 'type' => 'application/json', 'Cache-Control' => 'no-cache' do
5
+ JSON.pretty_generate(yield)+ "\n"
6
+ end
7
+ end
8
+
9
+ # produce json and quit
10
+ def $cgi.json! &block
11
+ return unless $XHR_JSON
12
+ json(&block)
13
+ Process.exit
14
+ end
15
+
16
+ # produce html/xhtml
17
+ def $cgi.html
18
+ return if $XHR_JSON
19
+ if $XHTML
20
+ $cgi.out 'type' => 'application/xhtml+xml', 'charset' => 'UTF-8' do
21
+ $x.declare! :DOCTYPE, :html
22
+ $x.html :xmlns => 'http://www.w3.org/1999/xhtml' do
23
+ yield $x
24
+ end
25
+ end
26
+ else
27
+ $cgi.out 'type' => 'text/html', 'charset' => 'UTF-8' do
28
+ $x.declare! :DOCTYPE, :html
29
+ $x.html do
30
+ yield $x
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # produce html and quit
37
+ def $cgi.html! &block
38
+ return if $XHR_JSON
39
+ html(&block)
40
+ Process.exit
41
+ end
42
+
43
+ # post specific logic (doesn't produce output)
44
+ def $cgi.post
45
+ yield unless $HTML_POST
46
+ end
47
+
48
+ # post specific content (produces output)
49
+ def $cgi.post! &block
50
+ html!(&block) unless $HTML_POST
51
+ end
@@ -0,0 +1,45 @@
1
+ # explicit request types
2
+ $HTTP_GET = ARGV.delete('--html')
3
+ $HTTP_POST = ARGV.delete('--post')
4
+ $XHR_JSON = ARGV.delete('--json')
5
+ $XHTML = ARGV.delete('--xhtml')
6
+
7
+ # Only prompt if explicitly asked for
8
+ ARGV.push '' if ARGV.empty?
9
+ ARGV.delete('--prompt') or ARGV.delete('--offline')
10
+
11
+ # standard objects
12
+ $cgi = CGI.new
13
+ $param = $cgi.params
14
+ $x = Builder::XmlMarkup.new :indent => 2
15
+
16
+ # implied request types
17
+ $HTTP_GET ||= ($cgi.request_method == 'GET')
18
+ $HTTP_POST ||= ($cgi.request_method == 'POST')
19
+ $XHR_JSON ||= ($cgi.accept.to_s =~ /json/)
20
+ $XHTML ||= ($cgi.accept.to_s =~ /xhtml/)
21
+
22
+ # get arguments if CGI couldn't find any...
23
+ $param.merge!(CGI.parse(ARGV.join('&'))) if $param.empty?
24
+
25
+ # fast path for accessing CGI parameters
26
+ def $param.method_missing(name)
27
+ self[name.to_s].join if has_key? name.to_s
28
+ end
29
+
30
+ # quick access to request_uri
31
+ SELF = ENV['REQUEST_URI'].to_s
32
+ def SELF?
33
+ SELF + "?" # avoids spoiling the cache
34
+ end
35
+
36
+ # environment objects
37
+ $USER = ENV['USER'] ||
38
+ if RUBY_PLATFORM =~ /darwin/
39
+ `dscl . -search /Users UniqueID #{Process.uid}`.split.first
40
+ else
41
+ `getent passwd #{Process.uid}`.split(':').first
42
+ end
43
+
44
+ $HOME = ENV['HOME'] || File.expand_path('~' + $USER)
45
+ $SERVER = ENV['HTTP_HOST'] || `hostname`.chomp
@@ -0,0 +1,25 @@
1
+ # smart style, knows that the content is indented text/data
2
+ def $x.style!(text)
3
+ text.slice! /^\n/
4
+ text.slice! /[ ]+\z/
5
+ $x.style :type => "text/css" do
6
+ if $XHTML
7
+ indented_text! text
8
+ else
9
+ indented_data! text
10
+ end
11
+ end
12
+ end
13
+
14
+ # smart script, knows that the content is indented text/data
15
+ def $x.script!(text)
16
+ text.slice! /^\n/
17
+ text.slice! /[ ]+\z/
18
+ $x.script :lang => "text/javascript" do
19
+ if $XHTML
20
+ indented_text! text
21
+ else
22
+ indented_data! text
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ # option to create an suexec callable wrapper
2
+ install = ARGV.find {|arg| arg =~ /--install=(.*)/}
3
+ if install and ARGV.delete(install)
4
+
5
+ # scope out the situation
6
+ dest = File.expand_path($1)
7
+ main = File.expand_path(caller.last[/^(.*):\d+(:|$)/,1])
8
+
9
+ # if destination is a directory, determine an appropriate file name
10
+ if File.directory?(dest)
11
+ if dest =~ /\/cgi-bin\/?$/
12
+ dest = File.join(dest, File.basename(main))
13
+ else
14
+ dest = File.join(dest, File.basename(main).sub(/\.rb$/,'.cgi'))
15
+ end
16
+ end
17
+
18
+ # prevent accidental overwrite
19
+ if File.exist?(dest) and not ARGV.delete('-f')
20
+ STDERR.puts "File #{dest} already exists. (Specify -f to overwrite)"
21
+ Process.exit
22
+ end
23
+
24
+ # ensure destination directory exists
25
+ destdir = File.dirname(dest)
26
+ if not File.exist?(destdir) or not File.directory?(destdir)
27
+ STDERR.puts "Directory #{destdir} does not exist."
28
+ Process.exit
29
+ end
30
+
31
+ # output wrapper
32
+ open(dest,'w') do |file|
33
+ # she-bang
34
+ file.puts "#!" + File.join(
35
+ RbConfig::CONFIG["bindir"],
36
+ RbConfig::CONFIG["ruby_install_name"] + RbConfig::CONFIG["EXEEXT"]
37
+ )
38
+
39
+ # Change directory
40
+ file.puts "Dir.chdir #{File.dirname(main).inspect}"
41
+
42
+ # Optional data from the script (after __END__)
43
+ file.puts DATA.read if Object.const_defined? :DATA
44
+
45
+ # Load script
46
+ file.puts "require #{File.basename(main).sub(/\.rb$/,'').inspect}"
47
+ end
48
+
49
+ # Mark wrapper as executable
50
+ File.chmod(0755, dest)
51
+
52
+ # Don't execute the script itself at this time
53
+ Process.exit
54
+ end
@@ -0,0 +1,25 @@
1
+ # run command/block as a background daemon
2
+ def submit(cmd=nil)
3
+ fork do
4
+ # detach from tty
5
+ Process.setsid
6
+ fork and exit
7
+
8
+ # clear working directory and mask
9
+ Dir.chdir '/'
10
+ File.umask 0000
11
+
12
+ # close open files
13
+ STDIN.reopen '/dev/null'
14
+ STDOUT.reopen '/dev/null', 'a'
15
+ STDERR.reopen STDOUT
16
+
17
+ # setup environment
18
+ ENV['USER'] ||= $USER
19
+ ENV['HOME'] ||= $HOME
20
+
21
+ # run cmd and/or block
22
+ system cmd if cmd
23
+ yield if block_given?
24
+ end
25
+ end
@@ -1,8 +1,8 @@
1
1
  module CgiSpa
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 2
5
- TINY = 1
4
+ MINOR = 3
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/lib/cgi-spa.rb CHANGED
@@ -1,169 +1,11 @@
1
- #!/usr/bin/ruby
2
1
  require 'cgi'
3
- require 'rubygems'
4
2
  require 'builder'
5
3
  require 'json'
6
4
 
7
- # standard objects
8
- $cgi = CGI.new
9
- $param = $cgi.params
10
- $x = Builder::XmlMarkup.new :indent => 2
5
+ require 'cgi-spa/environment'
6
+ require 'cgi-spa/cgi-methods'
7
+ require 'cgi-spa/html-methods'
8
+ require 'cgi-spa/job-control'
9
+ require 'cgi-spa/installation'
10
+ require 'cgi-spa/builder'
11
11
 
12
- # get arguments if CGI couldn't find any...
13
- $param.merge!(CGI.parse(ARGV.join('&'))) if $param.empty?
14
-
15
- # fast path for accessing CGI parameters
16
- def $param.method_missing(name)
17
- self[name.to_s].join if has_key? name.to_s
18
- end
19
-
20
- # quick access to request_uri
21
- SELF = ENV['REQUEST_URI'].to_s
22
- def SELF?
23
- SELF + "?" # avoids spoiling the cache
24
- end
25
-
26
- # environment objects
27
- $USER = ENV['USER'] ||
28
- if RUBY_PLATFORM =~ /darwin/
29
- `dscl . -search /Users UniqueID #{Process.uid}`.split.first
30
- else
31
- `getent passwd #{Process.uid}`.split(':').first
32
- end
33
-
34
- $HOME = ENV['HOME'] || File.expand_path('~' + $USER)
35
- $SERVER = ENV['HTTP_HOST'] || `hostname`.chomp
36
-
37
- # request types
38
- $HTTP_GET = ($cgi.request_method == 'GET')
39
- $HTTP_POST = ($cgi.request_method == 'POST')
40
- $XHR_JSON = (ENV['HTTP_ACCEPT'] =~ /json/)
41
-
42
- # run command/block as a background daemon
43
- def submit(cmd=nil)
44
- fork do
45
- # detach from tty
46
- Process.setsid
47
- fork and exit
48
-
49
- # clear working directory and mask
50
- Dir.chdir '/'
51
- File.umask 0000
52
-
53
- # close open files
54
- STDIN.reopen '/dev/null'
55
- STDOUT.reopen '/dev/null', 'a'
56
- STDERR.reopen STDOUT
57
-
58
- # setup environment
59
- ENV['USER'] ||= $USER
60
- ENV['HOME'] ||= $HOME
61
-
62
- # run cmd and/or block
63
- system cmd if cmd
64
- yield if block_given?
65
- end
66
- end
67
-
68
- # add indented_text!, indented_data! and traceback! methods to builder
69
- module Builder
70
- class XmlMarkup
71
- unless method_defined? :indented_text!
72
- def indented_text!(text)
73
- indented_data!(text) {|data| text! data}
74
- end
75
- end
76
-
77
- unless method_defined? :indented_data!
78
- def indented_data!(data)
79
- return if data.strip.length == 0
80
- lines = data.gsub(/\n\s*\n/,"\n")
81
- unindent = lines.scan(/^ */).map {|str| str.length}.min
82
-
83
- before = Regexp.new('^'.ljust(unindent+1))
84
- after = " " * (@level * @indent)
85
- data = data.gsub(before, after)
86
-
87
- if block_given?
88
- yield data
89
- else
90
- self << data
91
- end
92
-
93
- _newline unless data =~ /\s$/
94
- end
95
- end
96
-
97
- unless method_defined? :traceback!
98
- def traceback!(exception=$!, klass='traceback')
99
- pre :class=>klass do
100
- text! exception.inspect
101
- _newline
102
- exception.backtrace.each {|frame| text!((' '*@indent)+frame + "\n")}
103
- end
104
- end
105
- end
106
- end
107
- end
108
-
109
- # monkey patch to ensure that tags are closed
110
- test =
111
- Builder::XmlMarkup.new.html do |x|
112
- x.body do
113
- begin
114
- x.p do
115
- raise Exception.new('boom')
116
- end
117
- rescue Exception => e
118
- x.pre e
119
- end
120
- end
121
- end
122
-
123
- if test.index('<p>') and !test.index('</p>')
124
- module Builder
125
- class XmlMarkup
126
- def method_missing(sym, *args, &block)
127
- text = nil
128
- attrs = nil
129
- sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
130
- args.each do |arg|
131
- case arg
132
- when Hash
133
- attrs ||= {}
134
- attrs.merge!(arg)
135
- else
136
- text ||= ''
137
- text << arg.to_s
138
- end
139
- end
140
- if block
141
- unless text.nil?
142
- raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
143
- end
144
- _indent
145
- _start_tag(sym, attrs)
146
- _newline
147
- begin ### Added
148
- _nested_structures(block)
149
- ensure ### Added
150
- _indent
151
- _end_tag(sym)
152
- _newline
153
- end ### Added
154
- elsif text.nil?
155
- _indent
156
- _start_tag(sym, attrs, true)
157
- _newline
158
- else
159
- _indent
160
- _start_tag(sym, attrs)
161
- text! text
162
- _end_tag(sym)
163
- _newline
164
- end
165
- @target
166
- end
167
- end
168
- end
169
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cgi-spa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-18 00:00:00 -05:00
12
+ date: 2009-12-19 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -39,16 +39,30 @@ executables: []
39
39
  extensions: []
40
40
 
41
41
  extra_rdoc_files:
42
+ - COPYING
42
43
  - README
43
44
  - lib/cgi-spa.rb
44
- - lib/version.rb
45
+ - lib/cgi-spa/builder.rb
46
+ - lib/cgi-spa/cgi-methods.rb
47
+ - lib/cgi-spa/environment.rb
48
+ - lib/cgi-spa/html-methods.rb
49
+ - lib/cgi-spa/installation.rb
50
+ - lib/cgi-spa/job-control.rb
51
+ - lib/cgi-spa/version.rb
45
52
  files:
53
+ - COPYING
46
54
  - Manifest
47
55
  - README
48
56
  - Rakefile
49
57
  - cgi-spa.gemspec
50
58
  - lib/cgi-spa.rb
51
- - lib/version.rb
59
+ - lib/cgi-spa/builder.rb
60
+ - lib/cgi-spa/cgi-methods.rb
61
+ - lib/cgi-spa/environment.rb
62
+ - lib/cgi-spa/html-methods.rb
63
+ - lib/cgi-spa/installation.rb
64
+ - lib/cgi-spa/job-control.rb
65
+ - lib/cgi-spa/version.rb
52
66
  has_rdoc: true
53
67
  homepage: http://github.com/rubys/cgi-spa
54
68
  licenses: []