5.5. S2CGI Web Framework

S2CGI Web Framework is a small framework for creating the Ruby Application which is executed as a CGI script on Apache HTTPD Server.

5.5.1. Setup

5.5.1.1. Download

Please export s2cgi from following svn repository.

  • https://www.seasar.org/svn/sandbox/s2container.ruby/trunk/s2cgi/
As follows, a project directory will be created.

project/
  config/        # configuration files are stored.
  lib/           # Service and Logic classes are stored.
  public/        # published to WEB.
  test/          # UnitTests are stroed.
  tpl/           # erb template files are stored.
  var/           # Data and Logs are stored.
  vendor/        # Dependent Libraries are stored.

5.5.1.2. Installation of S2Container.Ruby

Please install S2Container.Ruby following an install document.

5.5.1.3. Installation of Rack

Please install Rack, if you use it.

5.5.1.4. Setup of Apache HTTPD Server

Extension cgi is registered as a cgi script.
And publish to WEB the public directory.
AddHandler cgi-script .cgi
Alias /s2cgi/ "/path/to/project/public/"
<Directory "/path/to/project/public">
    Options ExecCGI
    AllowOverride None
    Order allow,deny
    Allow from localhost
</Directory>

5.5.1.5. Setup of s2cgi

Base URL should be set up appropriately. You can set Base URL in environment.rb file of a config directory.
% cat config/environment.rb | grep BASE_URL
BASE_URL = '/s2cgi'
%
Please check the owner and the right of execution of a cgi script of a public directory. Shebang of cgi script should be set up appropriately.
% ls -l public/cgi/quick1.cgi
-rwxr--r-- 1 apache apache 1250 Feb  1 00:00 public/cgi/quick1.cgi
% head -1 public/cgi/quick1.cgi
#!/usr/bin/env ruby
%
Please check the owner and the right of execution of var directory.
% ls -ld var
drwxr-xr-x 6 apache apache 4096 Feb  1 00:00 var
%

5.5.2. Starting of Framework

Starting of CGI Framework will be done by calling Seasar::CGI.run method. As an example, let's create the following quick1.cgi in public/cgi directory.

% cat public/cgi/quick1.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/cgi.rb'
Seasar::CGI.run
%
Next, the erb template file of quick1.cgi is created as quick1.html in a tpl/cgi directory.
% cat tpl/cgi/quick1.html
Hello World
%
Please browse http://localhost/s2cgi/cgi/quick1.cgi, you can see "Hello World".

5.5.3. Seasar::CGI::Page Class

You can set the instance of Seasar::CGI::Page class as the argument of Seasar::CGI.run method. As an example, let's create the following quick2.cgi in public/cgi directory.

% cat public/cgi/quick2.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/cgi.rb'

class Page < Seasar::CGI::Page; end
Seasar::CGI.run(Page.new)
%
Next, the erb template file of quick2.cgi is created as quick2.html in a tpl/cgi directory.
% cat tpl/cgi/quick2.html
Hello World 2
%
Please browse http://localhost/s2cgi/cgi/quick2.cgi, you can see "Hello World 2".

Seasar::CGI.run method gets the component of Seasar::CGI::Page class from S2Container, when an argument is nil. When the component of Seasar::CGI::Page class does not exist in a container, the instance of Seasar::CGI::Page class is generated automatically.

Seasar::CGI::Page class generates contents from the erb template file of the tpl directory as default processing. As for the path to an erb template file, the path below BASE_URL of SCRIPT_NAME is used. As an example, when SCRIPT_NAME is "/s2cgi/cgi/quick2.cgi", and BASE_URL is "/s2cgi", then the path of an erb tenplate file will be "tpl/cgi/quick2.html".

In the case of getting Page component from S2Container, the CGI script is as follows.

% cat public/cgi/quick3.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/cgi.rb'

class Page < Seasar::CGI::Page
  s2comp
end
Seasar::CGI.run
%

5.5.4. Class Variables

The class variable of Seasar::CGI::Page class is shown below.

@@fatal. 

It has a procedure object which will be called when an error occurs after framework has started. The caught error instance and the instance of Seasar::CGI::Page class are passed to a procedure object.

By the default, it is set up as follows within config/cgi.rb.

Seasar::CGI::Page.fatal {|e, page|
  s2logger.fatal() {"#{e.message} #{e.backtrace}"}
  print "Location: #{BASE_URL}fatal.html\n\n";
}

@@tpl_dir. 

The path to the directory where an erb template file is saved is set. The tpl directory of the project directory is set up by the default.

@@tpl_ext. 

The extension of an erb template file is set up. a default value is "html".


5.5.5. Instance Variables

The instance variables of Seasar::CGI::Page class is shown below. You are able to use them in get/post method.

@cgi. 

cgi instance is set. (cgi is the attachment library of ruby)

@contents. 

response contents is set. The result string value of puts, p, render method is added.

@auto_render. 

It is set up by Boolean whether a render method is called automatically, when the size of @contents is 0 after calling get/post method.

@auto_response. 

It is set up by Boolean whether @contents is outputted by @cgi.out, after calling get/post method.

@headers. 

The HTTP headers are set as Hash, which are outputted by @cgi.out at the time of auto_response.


5.5.6. get/post method

In Page class, next processing of WEB layer is performed.

  • web page processing. ( redirect method, etc. )
  • choosing, rendering erb template file. ( render/partial method )
  • validation of request parameter.
  • session processing.
  • calling Service, Logic component.
  • sending HTTP headers, etc.

If "get" method is defined in Page class, it will be called when a request method is GET. "post" method is also the same, it will be called when a request method is POST. As an example, let's create the following cgi script.

% cat public/cgi/quick5.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/cgi.rb'

class Page < Seasar::CGI::Page
  s2comp
  def get
    puts 'Hello World 5'
  end
end
Seasar::CGI.run
%

Please browse http://localhost/s2cgi/cgi/quick5.cgi, you can see "Hello World 5". puts method and p method are overridden in Seasar::CGI::Page. The puts method adds String expression of an object to the response contents. The p method adds inspection of an object to the response contents. When the size of response contents is not 0, since automatic reading of an erb template file is not performed, it does not need to prepare an erb template file with the above-mentioned sample.


5.5.7. param method

The param method returns the request parameter of the key specified by the argument. When the key does not exist in the request parameter, nil is returned. When two or more keys exist, Array instance is returned.

param(name)
  • 1st: key of request parameter

The param method is used also when verifying a request parameter. Please refer to it for the validation setup here.


5.5.8. render method

Seasar::CGI::Page#render method processes an erb template file, and adds a string result to @contents. An erb template file can be specified by an argument. Specification of an erb template file is specified with the relative path below the directory specified by @@tpl_dir (class variable). It is as follows when specifying "@@tpl_dir/cgi/quick6.html" template file.

render 'cgi/quick6'

When an argument is omitted, the relative path excluding BASE_URL from SCRIPT_NAME is used. When SCRIPT_NAME is "/s2cgi/cgi/quick6.cgi", and BASE_URL is "/s2cgi", then an erb template will be "cgi/quick6". @@tpl_ext value is used for the extension of a template file.

The render method searches a layout file automatically. It does not process a template file, when a layout file is found. A layout file is processed as a template file. In search of a layout file, it is checked whether the file which added "_layout" to the template file exists. When it does not exist, it is checked whether the file "layout" exists in the same directory as a template file. When a template file is "cgi/quick6", It checks whether "cgi/quick6_layout" exists first, and when it does not exist, it checks whether "cgi/layout" exists.


5.5.9. partial method

Seasar::CGI::Page#partial method processes an erb template file, and return a string result. An erb template file can be specified by an argument. It is used, when reading the template of main contents within a layout file or dividing a template file partially. When you specify the template file of main contents within a layout file, please specify @template instance variable as an argument.

<!-- layout file -->
<%= partial(@template) %>  # include main contents

5.5.10. redirect meshot

Seasar::CGI::Page#redirect method redirects by setting URL specified by the argument as Location of a HTTP header. After carrying out redirection, exit is performed and CGI script is ended. The request parameter at the time of redirection can be set up as Hash by the second argument.

redirect('http://xxx.com/index.cgi', :year => 2009)

5.5.11. validate method

The validate method receives the block including the procedure which verifies a request parameter. In the first argument, :all, :get, and :post can be specified and it can specify at the time of access of which request method it verifies. Validate block must return Boolean as a return value. When a return value is False, the call of the get/post method corresponding to a request method is not performed.

class Page < Seasar::CGI::Page
  validate {         # same as :all. performs in GET and POST request.
    param :year, :numeric
    valid?
  }

  validate(:get) {   # performs in GET request.
    param :year, :numeric
    valid?
  }

  validate(:post) {  # performs in POST request.
    param :year, :numeric
    valid?
  }
end

Within Block passed to a validate method, in order to verify a request parameter, the following method can be used.

param method. 

param(name, type = nil, options = nil)
  • 1st: key of request parameter
  • 2nd: type of validation
  • 3rd: options passed to validation methodン

A request parameter is specified by name and the verification method to apply is specified by type. Two or more validation types can be registered into one request parameter.


valid? method. 

valid?(name = nil, type = nil)
  • 1st: key of request parameter
  • 2nd: type of validation
  • return: Boolean

It returns True, If all the registration entries corresponding to a name and a type which are specified by arguments are valid. It returns False when there is at least one Error. When Argument name is nil, the registration entry corresponding to a type is applicable. When Argument type is nil, the registration entry corresponding to a name is applicable. When both name and type are nil, all the registration entries are applicable.


valids method. 

valids(name = nil, type = nil)
  • 1st: key of request parameter
  • 2nd: type of validation
  • return: Array of Seasar::Validate::Entry instance

It returns array of entries with valid validation result within the entries which were searched with the name and the type specified by arguments.


error? method. 

error?(name = nil, type = nil)
  • 1st: key of request parameter
  • 2nd: type of validation
  • return: Boolean

It returns True, If there is at least one Error within the registration entries corresponding to the name and the type which are specified by arguments. It returns False when all the entries are valid.

As follows, you can use it within an erb template file.

username : <input type="text" name="username" value=""/>
<% if error?(:username) %><span class="err_msg">error message.</span><% end %>

errors method. 

errors(name = nil, type = nil)
  • 1st: key of request parameter
  • 2nd: type of validation
  • return: Array of Seasar::Validate::Entry instance

It returns array of entries with Error validation result within the entries which were searched with the name and the type specified by arguments.


if_errors method. 

if_errors(name = nil, type = nil, &procedure)
  • 1st: key of request parameter
  • 2nd: type of validation
  • 3nd: block

If there is at least one Error within the registration entries corresponding to the name and the type which are specified by arguments, it passes all the error entry to the block and calls it.

As follows, you can use it within an erb template file.

<% if_errors do |errors| %>
  <%=h errors.size %> invalid parameters are found.
<% end %>

5.5.11.1. Validation Type

The method used for every validation type is defined in Seasar::Validate::Utils module. A method name becomes the name which added "?" to the validation type. A method name becomes "int?" when a validation type is ":int."
Moreover, the procedure object registered into Seasar::Validate::Utils::Validators Hash is also used. Symbol specified by a validation type is used as a Hash key.


:int. 

It is verified whether it is Integer.
The method used is Seasar::Validate::Utils.int?.
An option is specified by Hash. (omissible)

  • :min => Minimum value
  • :max => Maximum value
  • :include => Include :min、:max value or not. default is True.
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :year, :int, :min => 2000, :max => 2050

:numeric. 

It is verified whether it is a numerical string.
The method used is Seasar::Validate::Utils.numeric?.
An option is specified by Hash. (omissible)

  • :min => Minimum value
  • :max => Maximum value
  • :include => Include :min、:max value or not. default is True.
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :year, :int, :min => 2000, :max => 2050

:string. 

It is verified whether it is String.
The method used is Seasar::Validate::Utils.string?.
An option is specified by Hash. (omissible)

  • :min => Minimum length
  • :max => Maximum length
  • :include => Include :min、:max value or not. default is True.
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :name, :string, :min => 6, :max => 8

:array. 

It is verified whether it is Array.
The method used is Seasar::Validate::Utils.array?.
An option is specified by Hash. (omissible)

  • :min => Minimum size
  • :max => Maximum size
  • :include => Include :min、:max value or not. default is True.
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :names, :array, :min => 6, :max => 8

:member, :in. 

It is verified whether it is member of array specified as an option.
The method used is Seasar::Validate::Utils.member?.
An option is specified by Hash or Array.

  • :items => Member Array
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :names, :member, %w[foo bar hoge huga]

:regexp. 

It is verified whether Regexp specified as an option is matched.
The method used is Seasar::Validate::Utils.regexp?.
An option is specified by Hash or Regexp.

  • :regexp => Regexp
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :name, :regexp, /^abc/

:alpha. 

It verifies whether it is an alphabetical-letter sequence.
The method used is Seasar::Validate::Utils.alpha?.
An option is specified by Hash. (omissible)

  • :case => :down or :up
  • :required => Request parameter must be exists or not. default is True.
The example of param method is as follows.
param :name, :alpha, :case => :down

5.5.11.2. validate_get/validate_post method

Instead of registering verification block by a validate method, Validation can be performed by defining a validate_get method to GET access, or defining a validate_post method to POST access.

class Page < Seasar::CGI::Page
  def validate_get   # performs in GET request.
    param :year, :numeric
    valid?
  end

  def validate_post  # performs in POST request.
    param :year, :numeric
    valid?
  end
end

5.5.12. Session management

The session of S2CGI, CGI::Session of a Ruby attachment library is used. The Hash value set as Session.options is used for the option passed to the argument of a new method. By the default, it is set up as follows within config/cgi.rb.

Seasar::CGI::Session.options = {'tmpdir' => SESSION_DIR, 'database_manager' => CGI::Session::PStore}

Seasar::CGI::Page#get_session method. 

CGI::Session is generated and returned. nil is returned when the session is not started.

Seasar::CGI::Page#start_session method. 

The new instance of CGI::Session is returned.


5.5.13. Dependency Injection

If Page class defined within a CGI script file is registered as a component Seasar::CGI.run method will get the registered Page component from S2Container, and will perform it. A setup of DI is also performed in a Page class.

Let's create following service class in a lib/example directory.

% cat lib/example/some-service.rb
module Example
  class SomeService
    s2comp
    def add(a, b)
      return a + b
    end
  end
end

Please create the following CGI scripts. In Page class, ":some_service" accessor method which receives SomeService instance is defined.

% cat public/cgi/quick6.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/cgi.rb'
require 'example/some-service'
class Page < Seasar::CGI::Page
  s2comp
  attr_accessor :some_service
  def get
    puts '1 + 2 = ' + @some_service.add(1, 2).to_s
  end
end
Seasar::CGI.run
%

Please browse http://localhost/s2cgi/cgi/quick6.cgi, you can see "1 + 2 = 3".


5.5.14. Unit Test

Please create UnitTest of the service class or the logic class in the test directory.
As follows, you can run UnitTest created in the test directory by test-suite.rb.

% ruby test/test-suite.rb
Loaded suite .
Started
...
Finished in 0.001 seconds.

3 tests, 8 assertions, 0 failures, 0 errors
%

5.5.15. Rack CGI

5.5.15.1. Starting of Framework

Starting of CGI Framework will be done by calling Seasar::Rack::CGI.run method. As an example, let's create the following quick1.cgi in public/rack directory.

% cat public/rack/quick1.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/rack.rb'
Seasar::Rack::CGI.run
%
Next, the erb template file of quick1.cgi is created as quick1.html in a tpl/rack directory.
% cat tpl/rack/quick1.html
Hello World
%
Please browse http://localhost/s2cgi/rack/quick1.cgi, you can see "Hello World".

5.5.15.2. Seasar::Rack::CGI::Page class

You can set the instance of Seasar::Rack::CGI::Page class as the argument of Seasar::CGI.run method. As an example, let's create the following quick2.cgi in public/rack directory.

% cat public/rack/quick2.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/rack.rb'

class Page < Seasar::Rack::CGI::Page; end
Seasar::Rack::CGI.run(Page.new)
%
Next, the erb template file of quick2.cgi is created as quick2.html in a tpl/rack directory.
% cat tpl/cgi/quick2.html
Hello World 2
%
Please browse http://localhost/s2cgi/rack/quick2.cgi, you can see "Hello World 2".

Seasar::Rack::CGI.run method gets the component of Seasar::Rack::CGI::Page class from S2Container, when an argument is nil. When the component of Seasar::Rack::CGI::Page class does not exist in a container, the instance of Seasar::Rack::CGI::Page class is generated automatically.


5.5.15.3. Instance Variables

@env. 

Rack environment argument passed to Seasar::Rack::CGI::Page#call method is set.

@request. 

Rack::Request instance is set.

@response. 

Rack::Response instance is set. @response.finish method is automatically called after a call of a get/post method.

@auto_render. 

It is set up by Boolean whether a render method is called automatically, when the size of @response.body.size is 0 and @response.status is 200 after calling get/post method.


5.5.15.4. Rack Up

The procedure object which performs Rack Up is set as Seasar::Rack::CGI::Page.rack class variable. As a default value, it is set up as follows in config/rack.rb.
Seasar::Rack::CGI::Page.rack {|page|
  Rack::Builder.app do
    use Seasar::Rack::CGI::Stdin2StringIO
    use Rack::ShowStatus
    use Rack::ShowExceptions
    use Rack::MethodOverride
    use Rack::ContentLength
    use Rack::Session::Cookie
    run page
  end
}

Rack Up procedure object is called within a CGI.run method. The Page instance gotten from S2Container or the Page instance generated newly is passed as an argument.
Moreover, Rack Up procedure object can be set up also as a block argument of Seasar::Rack::CGI.run method.

% cat public/rack/quick3.cgi
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname(__FILE__) + '/../../config/rack.rb'
class Page < Seasar::Rack::CGI::Page
  s2comp
  def get
    puts 'Hello World 3'
  end
end
Seasar::Rack::CGI.run {|page|
  Rack::Builder.app do
    use Seasar::Rack::CGI::Stdin2StringIO
    run page
  end
}
%


© Copyright The Seasar Foundation and the others 2008-2009, all rights reserved.