functions_framework 0.1.1 → 0.4.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.
@@ -0,0 +1,142 @@
1
+ <!--
2
+ # @title Functions Framework Overview
3
+ -->
4
+
5
+ # Functions Framework for Ruby
6
+
7
+ The Functions Framework is an open source framework for writing lightweight,
8
+ portable Ruby functions that run in a serverless environment. Functions written
9
+ to this Framework will run in many different environments, including:
10
+
11
+ * [Google Cloud Functions](https://cloud.google.com/functions) *(in preview)*
12
+ * [Cloud Run or Cloud Run for Anthos](https://cloud.google.com/run)
13
+ * Any other [Knative](https://github.com/knative)-based environment
14
+ * Your local development machine
15
+
16
+ The framework allows you to go from:
17
+
18
+ ```ruby
19
+ FunctionsFramework.http("hello") do |request|
20
+ "Hello, world!\n"
21
+ end
22
+ ```
23
+
24
+ To:
25
+
26
+ ```sh
27
+ curl http://my-url
28
+ # Output: Hello, world!
29
+ ```
30
+
31
+ Running on a fully-managed or self-managed serverless environment, without
32
+ requiring an HTTP server or complicated request handling logic.
33
+
34
+ ## Features
35
+
36
+ * Define named functions using normal Ruby constructs.
37
+ * Invoke functions in response to requests.
38
+ * Automatically unmarshal events conforming to the
39
+ [CloudEvents](https://cloudevents.io) spec.
40
+ * Automatically convert most legacy events from Google Cloud services such
41
+ as Cloud Pub/Sub and Cloud Storage, to CloudEvents.
42
+ * Spin up a local development server for quick testing.
43
+ * Integrate with standard Ruby libraries such as Rack and Minitest.
44
+ * Portable between serverless platforms.
45
+ * Supports all non-end-of-life versions of Ruby.
46
+
47
+ ## Supported Ruby versions
48
+
49
+ This library is supported on Ruby 2.4+.
50
+
51
+ Google provides official support for Ruby versions that are actively supported
52
+ by Ruby Core—that is, Ruby versions that are either in normal maintenance or
53
+ in security maintenance, and not end of life. Currently, this means Ruby 2.4
54
+ and later. Older versions of Ruby _may_ still work, but are unsupported and not
55
+ recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
56
+ about the Ruby support schedule.
57
+
58
+ ## Quickstart
59
+
60
+ Here is how to run a Hello World function on your local machine.
61
+
62
+ Create a `Gemfile` listing the Functions Framework as a dependency:
63
+
64
+ ```ruby
65
+ # Gemfile
66
+ source "https://rubygems.org"
67
+ gem "functions_framework", "~> 0.4"
68
+ ```
69
+
70
+ Create a file called `app.rb` and include the following code. This defines a
71
+ simple function called "hello".
72
+
73
+ ```ruby
74
+ # app.rb
75
+ require "functions_framework"
76
+
77
+ FunctionsFramework.http("hello") do |request|
78
+ "Hello, world!\n"
79
+ end
80
+ ```
81
+
82
+ Install the bundle, and start the framework. This spins up a local web server
83
+ running your "hello" function:
84
+
85
+ ```sh
86
+ bundle install
87
+ # ...installs the functions_framework gem and other dependencies
88
+ bundle exec functions-framework-ruby --target hello
89
+ # ...starts the functions server in the foreground
90
+ ```
91
+
92
+ In a separate shell, you can send requests to this function using curl:
93
+
94
+ ```sh
95
+ curl http://localhost:8080
96
+ # Output: Hello, world!
97
+ ```
98
+
99
+ Stop the server with `CTRL+C`.
100
+
101
+ ## Documentation
102
+
103
+ These guides provide additional getting-started information.
104
+
105
+ * **{file:docs/writing-functions.md Writing Functions}** :
106
+ How to write functions that respond to HTTP requests, industry-standard
107
+ [CloudEvents](https://cloudevents.io), as well as events sent from Google
108
+ Cloud services such as [Pub/Sub](https://cloud.google.com/pubsub) and
109
+ [Storage](https://cloud.google.com/storage).
110
+ * **{file:docs/testing-functions.md Testing Functions}** :
111
+ How to use the testing features of the Functions Framework to write local
112
+ unit tests for your functions using standard Ruby testing frameworks such
113
+ as [Minitest](https://github.com/seattlerb/minitest) and
114
+ [RSpec](https://rspec.info/).
115
+ * **{file:docs/running-a-functions-server.md Running a Functions Server}** :
116
+ How to use the `functions-framework-ruby` executable to run a local
117
+ functions server.
118
+ * **{file:docs/deploying-functions.md Deploying Functions}** :
119
+ How to deploy functions to
120
+ [Google Cloud Functions](https://cloud.google.com/functions) or
121
+ [Google Cloud Run](https://cloud.google.com/run).
122
+
123
+ The library reference documentation can be found at:
124
+ https://rubydoc.info/gems/functions_framework
125
+
126
+ Additional examples are available in the GitHub repository:
127
+ https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/examples/
128
+
129
+ ## Development
130
+
131
+ The source for the Ruby Functions Framework is available on GitHub at
132
+ https://github.com/GoogleCloudPlatform/functions-framework-ruby. For more
133
+ information on the Functions Framework contract implemented by this framework,
134
+ as well as links to Functions Frameworks for other languages, see
135
+ https://github.com/GoogleCloudPlatform/functions-framework.
136
+
137
+ The Functions Framework is open source under the Apache 2.0 license.
138
+ Contributions are welcome. Please see the contributing guide at
139
+ https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/.github/CONTRIBUTING.md.
140
+
141
+ Report issues at
142
+ https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues.
@@ -0,0 +1,122 @@
1
+ <!--
2
+ # @title Running a Functions Server
3
+ -->
4
+
5
+ # Running a Functions Server
6
+
7
+ This guide covers how to use the `functions-framework-ruby` executable to launch
8
+ a functions server hosting Ruby functions written for the Functions Framework.
9
+ For more information about the Framework as a whole, see the
10
+ {file:docs/overview.md Overview Guide}.
11
+
12
+ ## Running functions locally
13
+
14
+ The `functions-framework-ruby` command-line executable is used to run a
15
+ functions server. This executable is installed with the `functions_framework`
16
+ gem, and can be run with `bundle exec`. It wraps your function in a web server
17
+ request handler, and runs it in the [Puma](https://puma.io/) web server.
18
+
19
+ Pass the name of the function to run in the `--target` option. By default,
20
+ `functions-framework-ruby` will load functions from the file `app.rb` in the
21
+ current directory. If you want to load functions from a different file, use the
22
+ `--source` option.
23
+
24
+ ```sh
25
+ bundle install
26
+ bundle exec functions-framework-ruby --source=foo.rb --target=hello
27
+ ```
28
+
29
+ You can now send requests to your function. e.g.
30
+
31
+ ```sh
32
+ curl http://localhost:8080/
33
+ ```
34
+
35
+ The executable will write logs to the standard error stream while it is running.
36
+ To stop the server, hit `CTRL+C` or otherwise send it an appropriate signal.
37
+ The executable has no "background" or "daemon" mode. To run it in the background
38
+ from a shell, use the shell's background syntax (such as appending `&` to the
39
+ command).
40
+
41
+ By default, the executable will listen on the port specified by the `$PORT`
42
+ environment variable, or port 8080 if the variable is not set. You can also
43
+ override this by passing the `--port` option. A number of other options are
44
+ also available. See the section below on configuring the server, or pass
45
+ `--help` to the executable to display online help.
46
+
47
+ ## Running functions in Docker
48
+
49
+ The `functions-framework-ruby` executable is designed to be run in a Docker
50
+ container. This is how it is run in some container-based hosting services such
51
+ as Google Cloud Run, but you can also run it in Docker locally.
52
+
53
+ First, write a Dockerfile for your project. Following is a simple starting
54
+ point; feel free to adjust it to the needs of your project:
55
+
56
+ ```
57
+ FROM ruby:2.6
58
+ WORKDIR /app
59
+ COPY . .
60
+ RUN gem install --no-document bundler \
61
+ && bundle config --local frozen true \
62
+ && bundle config --local without "development test" \
63
+ && bundle install
64
+ ENV PORT=8080
65
+ ENTRYPOINT ["bundle", "exec", "functions-framework-ruby"]
66
+ ```
67
+
68
+ Build an image for your project using the Dockerfile.
69
+
70
+ ```sh
71
+ docker build --tag my-image .
72
+ ```
73
+
74
+ Then, you can run the Docker container locally as follows:
75
+
76
+ ```sh
77
+ docker run --rm -it -p 8080:8080 my-image --source=foo.rb --target=hello
78
+ ```
79
+
80
+ The arguments after the image name (e.g. `--source` and `--target` in the above
81
+ example) are passed to the `functions-framework-ruby` executable.
82
+
83
+ Because the docker container above maps port 8080 internally to port 8080
84
+ externally, you can use that port to send requests to your function. e.g.
85
+
86
+ ```sh
87
+ curl http://localhost:8080/
88
+ ```
89
+
90
+ You can stop the running Docker container with `CTRL+C` or by sending an
91
+ appropriate signal.
92
+
93
+ ## Configuring the server
94
+
95
+ The Ruby Functions Framework recognizes the following command line arguments to
96
+ the `functions-framework-ruby` executable. Each argument also corresponds to an
97
+ environment variable. If you specify both, the flag takes precedence.
98
+
99
+ Command-line flag | Environment variable | Description
100
+ ----------------- | -------------------- | -----------
101
+ `--port` | `PORT` | The port on which the Functions Framework listens for requests. Default: `8080`.
102
+ `--target` | `FUNCTION_TARGET` | The name of the exported function to be invoked in response to requests. Default: `function`.
103
+ `--source` | `FUNCTION_SOURCE` | The path to the file containing your function. Default: `app.rb` (in the current working directory).
104
+ `--signature-type` | `FUNCTION_SIGNATURE_TYPE` | Verifies that the function has the expected signature. Allowed values: `http`, `event`, or `cloudevent`.
105
+ `--environment` | `RACK_ENV` | Sets the Rack environment.
106
+ `--bind` | `FUNCTION_BIND_ADDR` | Binds to the given address. Default: `0.0.0.0`.
107
+ `--min-threads` | `FUNCTION_MIN_THREADS` | Sets the minimum thread pool size, overriding Puma's default.
108
+ `--max-threads` | `FUNCTION_MAX_THREADS` | Sets the maximum thread pool size, overriding Puma's default.
109
+ `--detailed-errors` | `FUNCTION_DETAILED_ERRORS` | No value. If present, shows exception details in exception responses. Defaults to false.
110
+ `--verbose` | `FUNCTION_LOGGING_LEVEL` | No value. Increases log verbosity (e.g. from INFO to DEBUG). Can be given more than once.
111
+ `--quiet` | `FUNCTION_LOGGING_LEVEL` | No value. Decreases log verbosity (e.g. from INFO to WARN). Can be given more than once.
112
+
113
+ Detailed errors are enabled by default if the `FUNCTION_DETAILED_ERRORS`
114
+ environment variable is set to a _non-empty_ string. The exact value does not
115
+ matter. Detailed errors are disabled if the variable is unset or empty.
116
+
117
+ The logging level defaults to the value of the `FUNCTION_LOGGING_LEVEL`
118
+ environment variable, which can be one of the following values: `DEBUG`, `INFO`,
119
+ `WARN`, `ERROR`, `FATAL`, or `UNKNOWN`, corresponding to Ruby's
120
+ [Logger::Severity](https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Severity.html)
121
+ constants. If `FUNCTION_LOGGING_LEVEL` is not set to one of those values, it
122
+ defaults to `INFO`.
@@ -0,0 +1,169 @@
1
+ <!--
2
+ # @title Testing Functions
3
+ -->
4
+
5
+ # Testing Functions
6
+
7
+ This guide covers writing unit tests for functions using the Functions Framework
8
+ for Ruby. For more information about the Framework, see the
9
+ {file:docs/overview.md Overview Guide}.
10
+
11
+ ## Overview of function testing
12
+
13
+ One of the benefits of the functions-as-a-service paradigm is that functions are
14
+ easy to test. In many cases, you can simply call a function with input, and test
15
+ the output. You do not need to set up (or mock) an actual server.
16
+
17
+ The Functions Framework provides utility methods that streamline the process of
18
+ setting up functions and the environment for testing, constructing input
19
+ parameters, and interpreting results. These are available in the
20
+ [Testing module](https://rubydoc.info/gems/functions_framework/FunctionsFramework/Testing).
21
+ Generally, you can include this module in your Minitest test class or RSpec
22
+ describe block.
23
+
24
+ ```ruby
25
+ require "minitest/autorun"
26
+ require "functions_framework/testing"
27
+
28
+ class MyTest < Minitest::Test
29
+ include FunctionsFramework::Testing
30
+ # define tests...
31
+ end
32
+ ```
33
+
34
+ ```ruby
35
+ require "rspec"
36
+ require "functions_framework/testing"
37
+
38
+ describe "My functions" do
39
+ include FunctionsFramework::Testing
40
+ # define examples...
41
+ end
42
+ ```
43
+
44
+ ## Loading functions for testing
45
+
46
+ To test a function, you'll need to load the Ruby file that defines the function,
47
+ and run the function to test its results. The Testing module provides a method
48
+ [load_temporary](https://rubydoc.info/gems/functions_framework/FunctionsFramework/Testing#load_temporary-instance_method),
49
+ which loads a Ruby file, defining functions but only for the scope of your test.
50
+ This allows your test to coexist with tests for other functions, even functions
51
+ with the same name from a different Ruby file.
52
+
53
+ ```ruby
54
+ require "minitest/autorun"
55
+ require "functions_framework/testing"
56
+
57
+ class MyTest < Minitest::Test
58
+ include FunctionsFramework::Testing
59
+
60
+ def test_a_function
61
+ load_temporary "foo.rb" do
62
+ # Test a function defined in foo.rb
63
+ end
64
+ end
65
+
66
+ def test_another_function
67
+ load_temporary "bar.rb" do
68
+ # Test a function defined in bar.rb
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+ When running a test suite, you'll typically need to load all the Ruby files
75
+ that define your functions. While `load_temporary` can ensure that the function
76
+ definitions do not conflict, it cannot do the same for classes, methods, and
77
+ other Ruby constructs. So, for testability, it is generally good practice to
78
+ include only functions in one of these files. If you need to write supporting
79
+ helper methods, classes, constants, or other code, include them in separate
80
+ ruby files that you `require`.
81
+
82
+ ## Testing HTTP functions
83
+
84
+ Testing an HTTP function is generally as simple as generating a request, calling
85
+ the function, and asserting against the response.
86
+
87
+ The input to an HTTP function is a
88
+ [Rack::Request](https://rubydoc.info/gems/rack/Rack/Request) object. It is
89
+ usually not hard to construct one of these objects, but the `Testing` module
90
+ includes helper methods that you can use to create simple requests for many
91
+ basic cases.
92
+
93
+ When you have constructed an input request, use
94
+ [call_http](https://rubydoc.info/gems/functions_framework/FunctionsFramework/Testing#call_http-instance_method)
95
+ to call a named function, passing the request object. This method returns a
96
+ [Rack::Response](https://rubydoc.info/gems/rack/Rack/Response) that you can
97
+ assert against.
98
+
99
+ ```ruby
100
+ require "minitest/autorun"
101
+ require "functions_framework/testing"
102
+
103
+ class MyTest < Minitest::Test
104
+ include FunctionsFramework::Testing
105
+
106
+ def test_http_function
107
+ load_temporary "app.rb" do
108
+ request = make_post_request "https://example.com/foo", "{\"name\":\"Ruby\"}",
109
+ ["Content-Type: application/json"]
110
+ response = call_http "my_function", request
111
+ assert_equal 200, response.status
112
+ assert_equal "Hello, Ruby!", response.body.join
113
+ end
114
+ end
115
+ end
116
+ ```
117
+
118
+ If the function raises an exception, the exception will be converted to a 500
119
+ response object. So if you are testing an error case, you should still check the
120
+ response object rather than looking for a raised exception.
121
+
122
+ ```ruby
123
+ require "minitest/autorun"
124
+ require "functions_framework/testing"
125
+
126
+ class MyTest < Minitest::Test
127
+ include FunctionsFramework::Testing
128
+
129
+ def test_erroring_http_function
130
+ load_temporary "app.rb" do
131
+ request = make_post_request "https://example.com/foo", "{\"name\":\"Ruby\"}",
132
+ ["Content-Type: application/json"]
133
+ response = call_http "error_function", request
134
+ assert_equal 500, response.status
135
+ assert_match(/ArgumentError/, response.body.join)
136
+ end
137
+ end
138
+ end
139
+ ```
140
+
141
+ ## Testing CloudEvent functions
142
+
143
+ Testing a CloudEvent function works similarly. The `Testing` module provides
144
+ methods to help construct example CloudEvent objects, which can then be passed
145
+ to the method
146
+ [call_event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/Testing#call_event-instance_method).
147
+
148
+ Unlike HTTP functions, event functions do not have a return value. Instead, you
149
+ will need to test side effects. A common approach is to test logs by capturing
150
+ the standard error output.
151
+
152
+ ```ruby
153
+ require "minitest/autorun"
154
+ require "functions_framework/testing"
155
+
156
+ class MyTest < Minitest::Test
157
+ include FunctionsFramework::Testing
158
+
159
+ def test_event_function
160
+ load_temporary "app.rb" do
161
+ event = make_cloud_event "Hello, world!", type: "my-type"
162
+ _out, err = capture_subprocess_io do
163
+ call_event "my_function", event
164
+ end
165
+ assert_match(/Received: "Hello, world!"/, err)
166
+ end
167
+ end
168
+ end
169
+ ```
@@ -0,0 +1,275 @@
1
+ <!--
2
+ # @title Writing Functions
3
+ -->
4
+
5
+ # Writing Functions
6
+
7
+ This guide covers writing functions using the Functions Framework for Ruby. For
8
+ more information about the Framework, see the
9
+ {file:docs/overview.md Overview Guide}.
10
+
11
+ ## About functions
12
+
13
+ Functions are Ruby blocks that are run when an input is received. Those inputs
14
+ can be HTTP requests or events in a recognized format. Functions that receive
15
+ HTTP requests return an HTTP response, but event functions have no return value.
16
+
17
+ When you define a function, you must provide an identifying name. The Functions
18
+ Framework allows you to use any string as a function name; however, many
19
+ deployment environments restrict the characters that can be used in a name. For
20
+ maximum portability, it is recommended that you use names that are allowed for
21
+ Ruby methods, i.e. beginning with a letter, and containing only letters,
22
+ numbers, and underscores.
23
+
24
+ ## Defining an HTTP function
25
+
26
+ An HTTP function is a simple web service that takes an HTTP request and returns
27
+ an HTTP response. The following example defines an HTTP function named "hello"
28
+ that returns a simple message in the HTTP response body:
29
+
30
+ ```ruby
31
+ require "functions_framework"
32
+
33
+ FunctionsFramework.http "hello" do |request|
34
+ # Return the response body.
35
+ "Hello, world!\n"
36
+ end
37
+ ```
38
+
39
+ HTTP functions take a Rack Request object and return an HTTP response. We'll
40
+ now cover these in a bit more detail.
41
+
42
+ ### Using the Request object
43
+
44
+ An HTTP function is passed a request, which is an object of type
45
+ [Rack::Request](https://rubydoc.info/gems/rack/Rack/Request). This object
46
+ provides methods for obtaining request information such as the method,
47
+ path, query parameters, body content, and headers. You can also obtain the raw
48
+ Rack environment using the `env` method. The following example includes some
49
+ request information in the response:
50
+
51
+ ```ruby
52
+ require "functions_framework"
53
+
54
+ FunctionsFramework.http "request_info_example" do |request|
55
+ # Include some request info in the response body.
56
+ "Received #{request.method} from #{request.url}!\n"
57
+ end
58
+ ```
59
+
60
+ The Functions Framework sets up a logger in the Rack environment, so you can
61
+ use the `logger` method on the request object if you want to emit logs. These
62
+ logs will be written to the standard error stream, and will appear in the
63
+ Google Cloud Logs if your function is running on a Google Cloud serverless
64
+ hosting environment.
65
+
66
+ ```ruby
67
+ require "functions_framework"
68
+
69
+ FunctionsFramework.http "logging_example" do |request|
70
+ # Log some request info.
71
+ request.logger.info "I received #{request.method} from #{request.url}!"
72
+ # A simple response body.
73
+ "ok"
74
+ end
75
+ ```
76
+
77
+ ### Response types
78
+
79
+ The above examples return simple strings as the response body. Often, however,
80
+ you will need to return more complex responses such as JSON, binary data, or
81
+ even rendered HTML. The Functions Framework recognizes a variety of return
82
+ types from an HTTP function:
83
+
84
+ * **String** : If you return a string, the framework will use it as the
85
+ response body in with a 200 (success) HTTP status code. It will set the
86
+ `Content-Type` header to `text/plain`.
87
+ * **Array** : If you return an array, the framework will assume it is a
88
+ standard three-element Rack response array, as defined in the
89
+ [Rack spec](https://github.com/rack/rack/blob/master/SPEC.rdoc).
90
+ * **Rack::Response** : You can return a
91
+ [Rack::Response](https://rubydoc.info/gems/rack/Rack/Response) object. The
92
+ Framework will call `#finish` on this object and retrieve the contents.
93
+ * **Hash** : If you return a Hash, the Framework will attempt to encode it as
94
+ JSON, and return it in the response body with a 200 (success) HTTP status
95
+ code. The `Content-Type` will be set to `application/json`.
96
+ * **StandardError** : If you return an exception object, the Framework will
97
+ return a 500 (server error) response. See the section below on
98
+ Error Handling.
99
+
100
+ ### Using Sinatra
101
+
102
+ The Functions Framework, and the functions-as-a-service (FaaS) solutions it
103
+ targets, are optimized for relatively simple HTTP requests such as webhooks and
104
+ simple APIs. If you want to deploy a large application or use a monolithic
105
+ framework such as Ruby on Rails, you may want to consider a solution such as
106
+ Google Cloud Run that is tailored to larger applications. However, a lightweight
107
+ framework such as Sinatra is sometimes useful when writing HTTP functions.
108
+
109
+ It is easy to connect an HTTP function to a Sinatra app. First, declare the
110
+ dependency on Sinatra in your `Gemfile`:
111
+
112
+ ```ruby
113
+ # Gemfile
114
+ source "https://rubygems.org"
115
+ gem "functions_framework", "~> 0.4"
116
+ gem "sinatra", "~> 2.0"
117
+ ```
118
+
119
+ Write the Sinatra app using the "modular" Sinatra interface (i.e. subclass
120
+ `Sinatra::Base`), and then run the Sinatra app directly as a Rack handler from
121
+ the function. Here is a basic example:
122
+
123
+ ```ruby
124
+ require "functions_framework"
125
+ require "sinatra/base"
126
+
127
+ class App < Sinatra::Base
128
+ get "/hello/:name" do
129
+ "Hello, #{params[:name]}!"
130
+ end
131
+ end
132
+
133
+ FunctionsFramework.http "sinatra_example" do |request|
134
+ App.call request.env
135
+ end
136
+ ```
137
+
138
+ This technique gives you access to pretty much any feature of the Sinatra web
139
+ framework, including routes, templates, and even custom middleware.
140
+
141
+ ## Defining an Event function
142
+
143
+ An event function is a handler for a standard cloud event. It can receive
144
+ industry-standard [CloudEvents](https://cloudevents.io), as well as events sent
145
+ by Google Cloud services such as [Pub/Sub](https://cloud.google.com/pubsub) and
146
+ [Storage](https://cloud.google.com/storage). Event functions do not have a
147
+ return value.
148
+
149
+ The following is a simple event handler that receives an event and logs some
150
+ information about it:
151
+
152
+ ```ruby
153
+ require "functions_framework"
154
+
155
+ FunctionsFramework.cloud_event "hello" do |event|
156
+ FunctionsFramework.logger.info "I received an event of type #{event.type}!"
157
+ end
158
+ ```
159
+
160
+ The event parameter will be either a
161
+ [CloudEvents V0.3 Event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/CloudEvents/Event/V0)
162
+ object ([see spec](https://github.com/cloudevents/spec/blob/v0.3/spec.md)) or a
163
+ [CloudEvents V1.0 Event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/CloudEvents/Event/V1)
164
+ object ([see spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md)).
165
+
166
+ Some Google Cloud services send events in a legacy event format that was defined
167
+ prior to CloudEvents. The Functions Framework will convert these legacy events
168
+ to an equivalent CloudEvents V1 type, so your function will always receive a
169
+ CloudEvent object when it is sent an event from Google Cloud. The precise
170
+ mapping between legacy events and CloudEvents is not specified in detail here,
171
+ but in general, the _data_ from the legacy event will be mapped to the `data`
172
+ field in the CloudEvent, and the _context_ from the legacy event will be mapped
173
+ to equivalent CloudEvent attributes.
174
+
175
+ ## Error handling
176
+
177
+ If your function encounters an error, it can raise an exception. The Functions
178
+ Framework will catch `StandardError` exceptions and handle them appropriately.
179
+
180
+ If you raise an exception in an HTTP function, the Functions Framework will
181
+ return a 500 (server error) response. You can control whether the exception
182
+ details (e.g. exception type, message, and backtrace) are sent with the
183
+ response by setting the detailed-errors configuration in the server. The
184
+ Framework will also log the error for you.
185
+
186
+ If you need more control over the error response, you can also construct the
187
+ HTTP response yourself. For example:
188
+
189
+ ```ruby
190
+ require "functions_framework"
191
+
192
+ FunctionsFramework.http "error_reporter" do |request|
193
+ begin
194
+ raise "whoops!"
195
+ rescue RuntimeError => e
196
+ [500, {}, ["Uh, oh, got an error message: #{e.message}."]]
197
+ end
198
+ end
199
+ ```
200
+
201
+ ## Structuring a project
202
+
203
+ A Functions Framework based "project" or "application" is a typical Ruby
204
+ application. It should include a `Gemfile` that specifies the gem dependencies
205
+ (including the `functions_framework` gem itself), and any other dependencies
206
+ needed by the function. It must include at least one Ruby source file that
207
+ defines functions, and can also include additional Ruby files defining classes
208
+ and methods that assist in the function implementation.
209
+
210
+ The "entrypoint" to the project, also called the "source", is a Ruby file. It
211
+ can define any number of functions (with distinct names), although it is often
212
+ good practice to create a separate Ruby file per function.
213
+
214
+ By convention, the source file is often called `app.rb`, but you can give it
215
+ any name. Projects can also have multiple source files that apply to different
216
+ cases.
217
+
218
+ A simple project might look like this:
219
+
220
+ ```
221
+ (project directory)
222
+ |
223
+ +- Gemfile
224
+ |
225
+ +- app.rb
226
+ |
227
+ +- lib/
228
+ | |
229
+ | +- hello.rb
230
+ |
231
+ +- test/
232
+ |
233
+ ...
234
+ ```
235
+
236
+ ```ruby
237
+ # Gemfile
238
+ source "https://rubygems.org"
239
+ gem "functions_framework", "~> 0.4"
240
+ ```
241
+
242
+ ```ruby
243
+ # app.rb
244
+ require "functions_framework"
245
+ require_relative "lib/hello"
246
+
247
+ FunctionsFramework.http "hello" do |request|
248
+ Hello.new(request).build_response
249
+ end
250
+ ```
251
+
252
+ ```ruby
253
+ # lib/hello.rb
254
+ class Hello
255
+ def initialize request
256
+ @request = request
257
+ end
258
+
259
+ def build_response
260
+ "Received request: #{request.method} #{request.url}\n"
261
+ end
262
+ end
263
+ ```
264
+
265
+ ## Next steps
266
+
267
+ To learn about writing unit tests for functions, see
268
+ {file:docs/testing-functions.md Testing Functions}.
269
+
270
+ To learn how to run your functions in a server, see
271
+ {file:docs/running-a-functions-server.md Running a Functions Server}.
272
+
273
+ To learn how to deploy your functions to Google Cloud Functions or Google Cloud
274
+ Run, see
275
+ {file:docs/deploying-functions.md Deploying Functions}.