From 0fd60eab3b379fa0eadc9a347e77fca7560b6c3a Mon Sep 17 00:00:00 2001 From: "S. Brent Faulkner" Date: Wed, 5 Jul 2017 02:44:18 -0400 Subject: [PATCH] include path parameters in params hash for ruby environment (#249) * include path parameters in params hash for ruby environment * Improve code organization and documentation for ruby environment. * Document Ruby examples --- environments/ruby/README.md | 20 ++++- environments/ruby/fission/context.rb | 17 ++++ environments/ruby/fission/handler.rb | 19 ++++ environments/ruby/fission/request.rb | 31 +++++++ environments/ruby/fission/specializer.rb | 14 +++ environments/ruby/server.rb | 52 +---------- examples/ruby/README.md | 107 +++++++++++++++++++++++ 7 files changed, 208 insertions(+), 52 deletions(-) create mode 100644 environments/ruby/fission/context.rb create mode 100644 environments/ruby/fission/handler.rb create mode 100644 environments/ruby/fission/request.rb create mode 100644 environments/ruby/fission/specializer.rb create mode 100644 examples/ruby/README.md diff --git a/environments/ruby/README.md b/environments/ruby/README.md index 90b74a20..01de1a4a 100644 --- a/environments/ruby/README.md +++ b/environments/ruby/README.md @@ -2,9 +2,19 @@ This is the Ruby environment for Fission. -It's a Docker image containing a Ruby 2.4.1 runtime, along with a -dynamic loader. A few common dependencies are included in the -Gemfile. +It's a Docker image containing a Ruby 2.4.1 runtime. The image uses +Rack with WEBrick to host the internal web server. + +The environment works via convention where you create a Ruby method +called `handler` with a single optional argument, a `Fission::Context` +object. + +The `Fission::Context` object gives access to the Rack env, and a +request object. Please see `fission/context.rb` for the public api. + +The `Fission::Request` object is a subclass of `Rack::Request` and +provides access to parameters and headers. See `fission/request.rb` +for the public api. ## Customizing this image @@ -39,3 +49,7 @@ Or, if you already have an environment, you can update its image: After this, fission functions that have the env parameter set to the same environment name as this command will use this environment. + +## Creating functions to use this image + +See the [examples README](examples/ruby/README.md). diff --git a/environments/ruby/fission/context.rb b/environments/ruby/fission/context.rb new file mode 100644 index 00000000..93dd41a2 --- /dev/null +++ b/environments/ruby/fission/context.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative 'request' + +module Fission + class Context + attr_reader :env + + def initialize(env) + @env = env + end + + def request + @request ||= Request.new(env) + end + end +end diff --git a/environments/ruby/fission/handler.rb b/environments/ruby/fission/handler.rb new file mode 100644 index 00000000..ed97318b --- /dev/null +++ b/environments/ruby/fission/handler.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require_relative 'context' + +module Fission + module Handler + def self.call(env) + response = if method(:handler).arity > 0 + handler(Context.new(env)) + else + handler + end + + response.is_a?(Array) ? response : Rack::Response.new([response]).finish + rescue + Rack::Response.new(['500 Internal Server Error'], 500, {}).finish + end + end +end diff --git a/environments/ruby/fission/request.rb b/environments/ruby/fission/request.rb new file mode 100644 index 00000000..7a3d37fc --- /dev/null +++ b/environments/ruby/fission/request.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Fission + HEADER_PREFIX = 'HTTP_' + PARAM_HEADER_PREFIX = 'HTTP_X_FISSION_PARAMS_' + PARAMETERS_KEY = 'fission.request.parameters' + + class Request < Rack::Request + def headers + Hash[ + *env.select { |k,v| k.start_with?(HEADER_PREFIX) } + .map { |k,v| [k.sub(/\A#{HEADER_PREFIX}/, '').split('_').map(&:capitalize).join('-'), v] } + .sort + .flatten + ] + end + + def params + env[PARAMETERS_KEY] ||= super.merge(path_parameters) + end + + def path_parameters + Hash[ + *env.select { |k,v| k.start_with?(PARAM_HEADER_PREFIX) } + .map { |k,v| [k.sub(/\A#{PARAM_HEADER_PREFIX}/, '').downcase, v] } + .sort + .flatten + ] + end + end +end diff --git a/environments/ruby/fission/specializer.rb b/environments/ruby/fission/specializer.rb new file mode 100644 index 00000000..2bfdb414 --- /dev/null +++ b/environments/ruby/fission/specializer.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Fission + CODE_PATH = '/userfunc/user' + + module Specializer + def self.call(env) + load CODE_PATH + Rack::Response.new([], 201).finish + rescue + Rack::Response.new(['500 Internal Server Error'], 500, {}).finish + end + end +end diff --git a/environments/ruby/server.rb b/environments/ruby/server.rb index 8a00f32a..a6f09b74 100644 --- a/environments/ruby/server.rb +++ b/environments/ruby/server.rb @@ -1,55 +1,9 @@ # frozen_string_literal: true + require 'rack' -CODEPATH = '/userfunc/user' - -module Fission - class Request < Rack::Request - def headers - Hash[ - *env.select { |k,v| k.start_with?('HTTP_') } - .map { |k,v| [k.sub(/\AHTTP_/, '').split('_').map(&:capitalize).join('-'), v] } - .sort - .flatten - ] - end - end - - class Context - attr_reader :env - - def initialize(env) - @env = env - end - - def request - @request ||= Request.new(env) - end - end - - module Specializer - def self.call(env) - load CODEPATH - Rack::Response.new([], 201).finish - rescue - Rack::Response.new(['500 Internal Server Error'], 500, {}).finish - end - end - - module Handler - def self.call(env) - response = if method(:handler).arity > 0 - handler(Context.new(env)) - else - handler - end - - response.is_a?(Array) ? response : Rack::Response.new([response]).finish - rescue - Rack::Response.new(['500 Internal Server Error'], 500, {}).finish - end - end -end +require_relative 'fission/specializer' +require_relative 'fission/handler' app = Rack::Builder.new do use Rack::CommonLogger, $stderr diff --git a/examples/ruby/README.md b/examples/ruby/README.md new file mode 100644 index 00000000..e4140c5b --- /dev/null +++ b/examples/ruby/README.md @@ -0,0 +1,107 @@ +# Ruby examples + +This directory contains several examples to get you started using Ruby +with Fission. + +Before running any of these functions, make sure you have created a +`ruby` Fission environment: + +``` +$ fission env create --name ruby --image USER/ruby-env +``` + +## Method signature + +A standard Ruby function has the basic form: + +```ruby +def handler(context) + return [200, {}, []] +end +``` + +If the fission context is not required, the function can be simplified: + +```ruby +def handler + [200, {}, ["Hello, world!\n"]] +end +``` + +If a simple text response is to be returned, with a status of 200, this +can be further simplified. + +```ruby +def handler + "Hello, world!\n" +end +``` + +## Hello example (`hello.rb`) + +This example is the simplest possible Ruby function, as described above. + +To run the example: + +``` +$ fission function create --name hello --env ruby --code examples/ruby/hello.rb + +$ fission route create --method GET --url /hello --function hello + +$ curl http://$FISSION_ROUTER/hello + Hello, world! +``` + +## Request data example (`request_data.rb`) + +This example shows basic use of the `Fission::Context` and +`Fission::Request` objects. + +To run the example: + +``` +$ fission function create --name request --env ruby --code examples/ruby/request_data.rb + +$ fission route create --method GET --url /request/{id} --function request + +$ curl http://$FISSION_ROUTER/request/123?key=abc + ---ENV--- + GATEWAY_INTERFACE=CGI/1.1 + PATH_INFO=/ + QUERY_STRING=key=abc + REMOTE_ADDR=172.17.0.8 + REMOTE_HOST=172.17.0.8 + REQUEST_METHOD=GET + REQUEST_URI=http://192.168.64.200:31314/?key=abc + SCRIPT_NAME= + SERVER_NAME=192.168.64.200 + SERVER_PORT=31314 + SERVER_PROTOCOL=HTTP/1.1 + SERVER_SOFTWARE=WEBrick/1.3.1 (Ruby/2.4.1/2017-03-22) + HTTP_HOST=192.168.64.200:31314 + HTTP_USER_AGENT=curl/7.52.1 + HTTP_ACCEPT=*/* + HTTP_X_FISSION_PARAMS_ID=123 + HTTP_X_FORWARDED_FOR=172.17.0.1 + HTTP_ACCEPT_ENCODING=gzip + rack.version=1=3 + ... + HTTP_VERSION=HTTP/1.1 + REQUEST_PATH=/ + + ---HEADERS--- + Accept: */* + Accept-Encoding: gzip + Host: 192.168.64.200:31314 + User-Agent: curl/7.52.1 + Version: HTTP/1.1 + X-Fission-Params-Id: 123 + X-Forwarded-For: 172.17.0.1 + + ---PARAMS--- + key=abc + id=123 + + --BODY-- + +```