functions_framework 0.5.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 370556a72cd31e4d24499b0715ca7f96a0598cb856fb28fed3d3eaabee0a80ae
4
- data.tar.gz: 04a24cf1e2f205fe124eb0bda891f647177b93c43ba1c2286ed52ffbad18cb1d
3
+ metadata.gz: f6d562086a839aaf78661b9417d3b931df10c74c98cb07b1710d40d6294b0490
4
+ data.tar.gz: fd070c121ce3e0c1bab7f1a738178a37c72c5d25c7726d3f3540e687d01bbe25
5
5
  SHA512:
6
- metadata.gz: 34d91fa0417632a253ddba1db7aa85f54697eb645caaaf96c9253a36fca241bca80bf17a40796dd5c8521d2c7bbe04ff23353764491d28b5e4335e0ee022f05a
7
- data.tar.gz: 0d8d59ef3e45d3980136d7015e8066052bffe3942c442f715368dcf6ac5dd98b0cb282c313fa1a387ac70514747701e75c879c549faa9802444fe6b4d56115c7
6
+ metadata.gz: 478554bfc8b8bafb2efd5bb880b8e053e2442a878bcd1363a4c6ddeab77a811b49530b84f467009b01f2299b70105f17a8a9840d9b35ebbec94359ca14d7114c
7
+ data.tar.gz: 6873da52a257f542cb782fde59ba7da7f96042f4b69eaf470ea23d81cdb8479e8ff2791a6fbd0fd36ec166039f9718b7ce1b14632c690b44bea2833e280835b5
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ### v0.7.1 / 2021-01-26
4
+
5
+ * DOCS: Fixed several errors in the writing-functions doc samples
6
+ * DOCS: Updated documentation to note public release of GCF support
7
+
8
+ ### v0.7.0 / 2020-09-25
9
+
10
+ * Now requires Ruby 2.5 or later.
11
+ * BREAKING CHANGE: Renamed "context" hash to "globals" and made it read-only for normal functions.
12
+ * BREAKING CHANGE: Server config is no longer passed to startup blocks.
13
+ * ADDED: Provided a "logger" convenience method in the context object.
14
+ * ADDED: Globals can be set from startup blocks, which is useful for initializing shared resources.
15
+ * ADDED: Support for testing startup tasks in the Testing module.
16
+ * ADDED: Support for controlling logging in the Testing module.
17
+ * FIXED: Fixed crash introduced in 0.6.0 when a block didn't declare an expected argument.
18
+ * FIXED: Better support for running concurrent tests.
19
+ * DOCS: Expanded documentation on initialization, execution context, and shared resources.
20
+ * DEPRECATED: The functions-framework executable is deprecated. Use functions-framework-ruby instead.
21
+
22
+ ### v0.6.0 / 2020-09-17
23
+
24
+ * ADDED: You can use the --version flag to print the framework version
25
+ * ADDED: You can use the --verify flag to verify that a given function is defined
26
+ * ADDED: You can now define blocks that are executed at server startup
27
+
28
+ ### v0.5.2 / 2020-09-06
29
+
30
+ * FIXED: Use global $stderr rather than STDERR for logger
31
+ * DOCS: Fix instructions for deployment to Google Cloud Functions
32
+
33
+ ### v0.5.1 / 2020-07-20
34
+
35
+ * Updated some documentation links. No functional changes.
36
+
3
37
  ### v0.5.0 / 2020-07-09
4
38
 
5
39
  * Removed embedded CloudEvents classes and added the official CloudEvents SDK as a dependency. A `FunctionsFramework::CloudEvents` alias provides backward compatibility.
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # Functions Framework [![Documentation](https://img.shields.io/badge/docs-FunctionsFramework-red.svg)](https://rubydoc.info/gems/functions_framework/FunctionsFramework) [![Gem Version](https://badge.fury.io/rb/functions_framework.svg)](https://badge.fury.io/rb/functions_framework)
1
+ # Functions Framework for Ruby [![Documentation](https://img.shields.io/badge/docs-FunctionsFramework-red.svg)](https://googlecloudplatform.github.io/functions-framework-ruby/latest) [![Gem Version](https://badge.fury.io/rb/functions_framework.svg)](https://badge.fury.io/rb/functions_framework)
2
2
 
3
3
  An open source framework for writing lightweight, portable Ruby functions that
4
4
  run in a serverless environment. Functions written to this Framework will run
5
5
  in many different environments, including:
6
6
 
7
- * [Google Cloud Functions](https://cloud.google.com/functions) *(in preview)*
8
- * [Cloud Run or Cloud Run for Anthos](https://cloud.google.com/run)
7
+ * [Google Cloud Functions](https://cloud.google.com/functions) *(public preview)*
8
+ * [Google Cloud Run](https://cloud.google.com/run)
9
9
  * Any other [Knative](https://github.com/knative)-based environment
10
10
  * Your local development machine
11
11
 
@@ -42,11 +42,11 @@ requiring an HTTP server or complicated request handling logic.
42
42
 
43
43
  ## Supported Ruby versions
44
44
 
45
- This library is supported on Ruby 2.4+.
45
+ This library is supported on Ruby 2.5+.
46
46
 
47
47
  Google provides official support for Ruby versions that are actively supported
48
48
  by Ruby Core—that is, Ruby versions that are either in normal maintenance or
49
- in security maintenance, and not end of life. Currently, this means Ruby 2.4
49
+ in security maintenance, and not end of life. Currently, this means Ruby 2.5
50
50
  and later. Older versions of Ruby _may_ still work, but are unsupported and not
51
51
  recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
52
52
  about the Ruby support schedule.
@@ -60,7 +60,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
60
60
  ```ruby
61
61
  # Gemfile
62
62
  source "https://rubygems.org"
63
- gem "functions_framework", "~> 0.5"
63
+ gem "functions_framework", "~> 0.7"
64
64
  ```
65
65
 
66
66
  Create a file called `app.rb` and include the following code. This defines a
@@ -98,26 +98,26 @@ Stop the server with `CTRL+C`.
98
98
 
99
99
  These guides provide additional getting-started information.
100
100
 
101
- * **[Writing Functions](https://rubydoc.info/gems/functions_framework/file/docs/writing-functions.md)**
101
+ * **[Writing Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.writing-functions.html)**
102
102
  : How to write functions that respond to HTTP requests, industry-standard
103
103
  [CloudEvents](https://cloudevents.io), as well as events sent from Google
104
104
  Cloud services such as [Pub/Sub](https://cloud.google.com/pubsub) and
105
105
  [Storage](https://cloud.google.com/storage).
106
- * **[Testing Functions](https://rubydoc.info/gems/functions_framework/file/docs/testing-functions.md)**
106
+ * **[Testing Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.testing-functions.html)**
107
107
  : How to use the testing features of the Functions Framework to write local
108
108
  unit tests for your functions using standard Ruby testing frameworks such
109
109
  as [Minitest](https://github.com/seattlerb/minitest) and
110
110
  [RSpec](https://rspec.info/).
111
- * **[Running a Functions Server](https://rubydoc.info/gems/functions_framework/file/docs/running-a-functions-server.md)**
111
+ * **[Running a Functions Server](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.running-a-functions-server.html)**
112
112
  : How to use the `functions-framework-ruby` executable to run a local
113
113
  functions server.
114
- * **[Deploying Functions](https://rubydoc.info/gems/functions_framework/file/docs/deploying-functions.md)**
114
+ * **[Deploying Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.deploying-functions.html)**
115
115
  : How to deploy functions to
116
116
  [Google Cloud Functions](https://cloud.google.com/functions) or
117
117
  [Google Cloud Run](https://cloud.google.com/run).
118
118
 
119
119
  The library reference documentation can be found at:
120
- https://rubydoc.info/gems/functions_framework
120
+ https://googlecloudplatform.github.io/functions-framework-ruby
121
121
 
122
122
  Additional examples are available in the `examples` directory:
123
123
  https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/examples/
@@ -16,4 +16,7 @@
16
16
 
17
17
  require "functions_framework/cli"
18
18
 
19
- ::FunctionsFramework::CLI.new.parse_args(::ARGV).run
19
+ puts "WARNING: The functions-framework executable is deprecated and will be"
20
+ puts "removed in a future version. Please use functions-framework-ruby instead."
21
+
22
+ ::FunctionsFramework::CLI.new.parse_args(::ARGV).run.complete
@@ -16,4 +16,4 @@
16
16
 
17
17
  require "functions_framework/cli"
18
18
 
19
- ::FunctionsFramework::CLI.new.parse_args(::ARGV).run
19
+ ::FunctionsFramework::CLI.new.parse_args(::ARGV).run.complete
@@ -31,12 +31,8 @@ Functions Framework is designed especially for functions that can be hosted on
31
31
  Cloud Functions.
32
32
 
33
33
  You can run Ruby functions on Google Cloud Functions by selecting the `ruby26`
34
- runtime. This runtime uses a recent release of Ruby 2.6. Support for other
35
- versions of Ruby may be added in the future.
36
-
37
- > **Note:** Ruby support on Cloud Functions is currently in limited preview.
38
- > It is not yet suitable for production workloads, and support is best-effort
39
- > only. Access is currently limited to selected early-access users.
34
+ runtime or `ruby27` runtime to use a recent release of Ruby 2.6 or Ruby 2.7.
35
+ Support for Ruby 3.0 is forthcoming.
40
36
 
41
37
  ### Deploying and updating your function
42
38
 
@@ -46,30 +42,39 @@ is to `bundle install` or `bundle update` and run your local tests prior to
46
42
  deploying. Cloud Functions will not accept your function unless an up-to-date
47
43
  `Gemfile.lock` is present.
48
44
 
49
- Choose a name for your function. This function name is how it will appear in the
50
- cloud console, and will also be part of the function's URL. (It's different from
51
- the name you provide when writing your function; Cloud Functions calls that name
52
- the "function target".)
45
+ Also, make sure your source file (which defines your function) is called
46
+ `app.rb`. The Functions Framework lets you choose a function source file, but
47
+ Cloud Functions currently requires you to use `app.rb`.
48
+
49
+ Decide _which_ function in the source file to invoke, that is, the name that you
50
+ used when writing the function. This is called the **target**. (Note that if you
51
+ did not specify a name for the function, it defaults to the name `function`.)
52
+
53
+ Choose a Cloud Functions **name** for your function. The **name** identifies
54
+ this function deployment (e.g. in the cloud console) and is also part of the
55
+ function's default URL. (Note: the **name** and the **target** do not have to
56
+ be the same value.)
53
57
 
54
58
  Then, issue the gcloud command to deploy:
55
59
 
56
60
  ```sh
57
- gcloud functions deploy $YOUR_FUNCTION_NAME --project=$YOUR_PROJECT_ID \
58
- --runtime=ruby26 --trigger-http --source=$YOUR_FUNCTION_SOURCE \
59
- --entry-point=$YOUR_FUNCTION_TARGET
61
+ gcloud functions deploy $YOUR_FUNCTION_NAME \
62
+ --project=$YOUR_PROJECT_ID \
63
+ --runtime=ruby27 \
64
+ --trigger-http \
65
+ --entry-point=$YOUR_FUNCTION_TARGET
60
66
  ```
61
67
 
62
- The source file defaults to `./app.rb` and the function target defaults to
63
- `function`, so those flags can be omitted if you're using the defaults. The
64
- project flag can also be omitted if you've set it as the default with
65
- `gcloud config set project`.
68
+ The `--entry-point=` flag can be omitted if the **target** has the same value
69
+ as the **name**. Additionally, the `--project` flag can be omitted if you've
70
+ set your default project using `gcloud config set project`.
66
71
 
67
72
  If your function handles events rather than HTTP requests, you'll need to
68
73
  replace `--trigger-http` with a different trigger. For details, see the
69
74
  [reference documentation](https://cloud.google.com/sdk/gcloud/reference/functions/deploy)
70
75
  for `gcloud functions deploy`.
71
76
 
72
- To update your deployment, just redeploy using the same function name.
77
+ To update your deployment, just redeploy using the same function **name**.
73
78
 
74
79
  ### Configuring Cloud Functions deployments
75
80
 
@@ -81,7 +86,7 @@ and above, set `FUNCTION_LOGGING_LEVEL` to `WARN` when deploying:
81
86
 
82
87
  ```sh
83
88
  gcloud functions deploy $YOUR_FUNCTION_NAME --project=$YOUR_PROJECT_ID \
84
- --runtime=ruby26 --trigger-http --source=$YOUR_FUNCTION_SOURCE \
89
+ --runtime=ruby27 --trigger-http --source=$YOUR_FUNCTION_SOURCE \
85
90
  --entry-point=$YOUR_FUNCTION_TARGET \
86
91
  --set-env-vars=FUNCTION_LOGGING_LEVEL=WARN
87
92
  ```
@@ -116,7 +121,7 @@ Dockerfile that you can use as a starting point. Feel free to adjust it to the
116
121
  needs of your project:
117
122
 
118
123
  ```
119
- FROM ruby:2.6
124
+ FROM ruby:2.7
120
125
  WORKDIR /app
121
126
  COPY . .
122
127
  RUN gem install --no-document bundler \
@@ -142,8 +147,8 @@ command may ask you for permission to enable the Cloud Build API for the project
142
147
  if it isn't already enabled.
143
148
 
144
149
  Because you provide your own Docker image when deploying to Cloud Run, you can
145
- use any version of Ruby supported by the Functions Framework, from 2.4 through
146
- 2.7.
150
+ use any version of Ruby supported by the Functions Framework, from 2.5 through
151
+ 3.0.
147
152
 
148
153
  ### Deploying an image to Cloud Run
149
154
 
@@ -8,8 +8,8 @@ The Functions Framework is an open source framework for writing lightweight,
8
8
  portable Ruby functions that run in a serverless environment. Functions written
9
9
  to this Framework will run in many different environments, including:
10
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)
11
+ * [Google Cloud Functions](https://cloud.google.com/functions) *(public preview)*
12
+ * [Google Cloud Run](https://cloud.google.com/run)
13
13
  * Any other [Knative](https://github.com/knative)-based environment
14
14
  * Your local development machine
15
15
 
@@ -46,11 +46,11 @@ requiring an HTTP server or complicated request handling logic.
46
46
 
47
47
  ## Supported Ruby versions
48
48
 
49
- This library is supported on Ruby 2.4+.
49
+ This library is supported on Ruby 2.5+.
50
50
 
51
51
  Google provides official support for Ruby versions that are actively supported
52
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
53
+ in security maintenance, and not end of life. Currently, this means Ruby 2.5
54
54
  and later. Older versions of Ruby _may_ still work, but are unsupported and not
55
55
  recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
56
56
  about the Ruby support schedule.
@@ -64,7 +64,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
64
64
  ```ruby
65
65
  # Gemfile
66
66
  source "https://rubygems.org"
67
- gem "functions_framework", "~> 0.5"
67
+ gem "functions_framework", "~> 0.7"
68
68
  ```
69
69
 
70
70
  Create a file called `app.rb` and include the following code. This defines a
@@ -121,7 +121,7 @@ These guides provide additional getting-started information.
121
121
  [Google Cloud Run](https://cloud.google.com/run).
122
122
 
123
123
  The library reference documentation can be found at:
124
- https://rubydoc.info/gems/functions_framework
124
+ https://googlecloudplatform.github.io/functions-framework-ruby
125
125
 
126
126
  Additional examples are available in the GitHub repository:
127
127
  https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/examples/
@@ -17,9 +17,8 @@ the output. You do not need to set up (or mock) an actual server.
17
17
  The Functions Framework provides utility methods that streamline the process of
18
18
  setting up functions and the environment for testing, constructing input
19
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.
20
+ {FunctionsFramework::Testing} module. Generally, you can include this module in
21
+ your Minitest test class or RSpec describe block.
23
22
 
24
23
  ```ruby
25
24
  require "minitest/autorun"
@@ -45,10 +44,10 @@ end
45
44
 
46
45
  To test a function, you'll need to load the Ruby file that defines the function,
47
46
  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.
47
+ {FunctionsFramework::Testing#load_temporary}, which loads a Ruby file, defining
48
+ functions but only for the scope of your test. This allows your test to coexist
49
+ with tests for other functions, even functions with the same name from a
50
+ different Ruby file.
52
51
 
53
52
  ```ruby
54
53
  require "minitest/autorun"
@@ -91,8 +90,8 @@ includes helper methods that you can use to create simple requests for many
91
90
  basic cases.
92
91
 
93
92
  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
93
+ {FunctionsFramework::Testing#call_http} to call a named function, passing the
94
+ request object. This method returns a
96
95
  [Rack::Response](https://rubydoc.info/gems/rack/Rack/Response) that you can
97
96
  assert against.
98
97
 
@@ -142,8 +141,7 @@ end
142
141
 
143
142
  Testing a CloudEvent function works similarly. The `Testing` module provides
144
143
  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).
144
+ to the method {FunctionsFramework::Testing#call_event}.
147
145
 
148
146
  Unlike HTTP functions, event functions do not have a return value. Instead, you
149
147
  will need to test side effects. A common approach is to test logs by capturing
@@ -167,3 +165,53 @@ class MyTest < Minitest::Test
167
165
  end
168
166
  end
169
167
  ```
168
+
169
+ ## Testing startup tasks
170
+
171
+ When a functions server is starting up, it calls startup tasks automatically.
172
+ In the testing environment, when you call a function using the
173
+ {FunctionsFramework::Testing#call_http} or
174
+ {FunctionsFramework::Testing#call_event} methods, the testing environment will
175
+ also automatically execute any startup tasks for you.
176
+
177
+ You can also call startup tasks explicitly to test them in isolation, using the
178
+ {FunctionsFramework::Testing#run_startup_tasks} method. Pass the name of a
179
+ function, and the testing module will execute all defined startup blocks, in
180
+ order, as if the server were preparing that function for execution.
181
+ {FunctionsFramework::Testing#run_startup_tasks} returns the resulting globals
182
+ as a hash, so you can assert against its contents.
183
+
184
+ If you use {FunctionsFramework::Testing#run_startup_tasks} to run the startup
185
+ tasks explicitly, they will not be run again when you call the function itself
186
+ using {FunctionsFramework::Testing#call_http} or
187
+ {FunctionsFramework::Testing#call_event}. However, if startup tasks have
188
+ already been run implicitly by {FunctionsFramework::Testing#call_http} or
189
+ {FunctionsFramework::Testing#call_event}, then attempting to run them again
190
+ explicitly by calling {FunctionsFramework::Testing#run_startup_tasks} will
191
+ result in an exception.
192
+
193
+ There is currently no way to run a single startup block in isolation. If you
194
+ have multiple startup blocks defined, they are always executed together.
195
+
196
+ Following is an example test that runs startup tasks explicitly and asserts
197
+ against the effect on the globals.
198
+
199
+ ```ruby
200
+ require "minitest/autorun"
201
+ require "functions_framework/testing"
202
+
203
+ class MyTest < Minitest::Test
204
+ include FunctionsFramework::Testing
205
+
206
+ def test_startup_tasks
207
+ load_temporary "app.rb" do
208
+ globals = run_startup_tasks "my_function"
209
+ assert_equal "foo", globals[:my_global]
210
+
211
+ request = make_get_request "https://example.com/foo"
212
+ response = call_http "my_function", request
213
+ assert_equal 200, response.status
214
+ end
215
+ end
216
+ end
217
+ ```
@@ -53,7 +53,7 @@ require "functions_framework"
53
53
 
54
54
  FunctionsFramework.http "request_info_example" do |request|
55
55
  # Include some request info in the response body.
56
- "Received #{request.method} from #{request.url}!\n"
56
+ "Received #{request.request_method} from #{request.url}!\n"
57
57
  end
58
58
  ```
59
59
 
@@ -68,7 +68,7 @@ require "functions_framework"
68
68
 
69
69
  FunctionsFramework.http "logging_example" do |request|
70
70
  # Log some request info.
71
- request.logger.info "I received #{request.method} from #{request.url}!"
71
+ request.logger.info "I received #{request.request_method} from #{request.url}!"
72
72
  # A simple response body.
73
73
  "ok"
74
74
  end
@@ -110,9 +110,8 @@ It is easy to connect an HTTP function to a Sinatra app. First, declare the
110
110
  dependency on Sinatra in your `Gemfile`:
111
111
 
112
112
  ```ruby
113
- # Gemfile
114
113
  source "https://rubygems.org"
115
- gem "functions_framework", "~> 0.5"
114
+ gem "functions_framework", "~> 0.7"
116
115
  gem "sinatra", "~> 2.0"
117
116
  ```
118
117
 
@@ -158,9 +157,9 @@ end
158
157
  ```
159
158
 
160
159
  The event parameter will be either a
161
- [CloudEvents V0.3 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V0)
160
+ [CloudEvents V0.3 Event](https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V0)
162
161
  object ([see spec](https://github.com/cloudevents/spec/blob/v0.3/spec.md)) or a
163
- [CloudEvents V1.0 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V1)
162
+ [CloudEvents V1.0 Event](https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1)
164
163
  object ([see spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md)).
165
164
 
166
165
  Some Google Cloud services send events in a legacy event format that was defined
@@ -198,6 +197,192 @@ FunctionsFramework.http "error_reporter" do |request|
198
197
  end
199
198
  ```
200
199
 
200
+ ## The runtime environment
201
+
202
+ A serverless environment may be somewhat different from server-based runtime
203
+ environments you might be used to. Serverless runtimes often provide a simpler
204
+ programming model, transparent scaling, and cost savings, but they do so by
205
+ controlling how your code is managed and executed. The Functions Framework is
206
+ designed around a "functions-as-a-service" (FaaS) paradigm, which runs
207
+ self-contained stateless functions that have an input and a return value. It's
208
+ important to understand what that means for your Ruby code in order to get the
209
+ most out of a cloud serverless product.
210
+
211
+ For example, multithreading is a core element of the Functions Framework. When
212
+ you write functions, you should assume that multiple executions may be taking
213
+ place concurrently in different threads, and thus you should avoid operations
214
+ that can cause concurrency issues or race conditions. The easiest way to do
215
+ this is to make your functions self-contained and stateless. Avoid global
216
+ variables and don't share mutable data between different function executions.
217
+
218
+ Additionally, a serverless runtime may throttle the CPU whenever no actual
219
+ function executions are taking place. This lets it reduce the CPU resources
220
+ used (and therefore the cost to you), while keeping your application warmed up
221
+ and ready to respond to new requests quickly. An important implication, though,
222
+ is that you should avoid starting up background threads or processes. They may
223
+ not get any CPU time during periods when your Ruby application is not actually
224
+ executing a function.
225
+
226
+ In the sections below, we'll discuss a few techniques and features of the
227
+ Functions Framework to help you write Ruby code that fits well into a
228
+ serverless paradigm.
229
+
230
+ ### Startup tasks
231
+
232
+ It is sometimes useful to perform one-time initialization that applies to many
233
+ function executions, for example to warm up caches, perform precomputation, or
234
+ establish shared remote connections. To run code during initialization, use
235
+ {FunctionsFramework.on_startup} to define a _startup task_.
236
+
237
+ ```ruby
238
+ require "functions_framework"
239
+
240
+ FunctionsFramework.on_startup do |function|
241
+ # Perform initialization here.
242
+ require "my_cache"
243
+ MyCache.warmup
244
+ end
245
+
246
+ FunctionsFramework.http "hello" do |request|
247
+ # Initialization will be done by the time a normal function is called.
248
+ end
249
+ ```
250
+
251
+ Startup tasks are run once per Ruby instance, before the framework starts
252
+ receiving requests and executing functions. You can define multiple startup
253
+ tasks, and they will run in order, and are guaranteed to complete before any
254
+ function is executed.
255
+
256
+ The block is optionally passed the {FunctionsFramework::Function} representing
257
+ the function that will be run. You code can, for example, perform different
258
+ initialization depending on the {FunctionsFramework::Function#name} or
259
+ {FunctionsFramework::Function#type}.
260
+
261
+ **In most cases, initialization code should live in an `on_startup` block
262
+ instead of at the "top level" of your Ruby file.** This is because some
263
+ serverless runtimes may load your Ruby code at build or deployment time (for
264
+ example, to verify that it properly defines the requested function), and this
265
+ will execute any code present at the top level of the Ruby file. If top-level
266
+ code is long-running or depends on runtime resources or environment variables,
267
+ this could cause the deployment to fail. By performing initialization in an
268
+ `on_startup` block instead, you ensure it will run only when an actual runtime
269
+ server is starting up, not at build/deployment time.
270
+
271
+ ```ruby
272
+ require "functions_framework"
273
+
274
+ # DO NOT perform initialization here because this could get run at build time.
275
+ # require "my_cache"
276
+ # MyCache.warmup
277
+
278
+ # Instead initialize in an on_startup block, which is executed only when a
279
+ # runtime server is starting up.
280
+ FunctionsFramework.on_startup do
281
+ # Perform initialization here.
282
+ require "my_cache"
283
+ MyCache.warmup
284
+ end
285
+
286
+ # ...
287
+ ```
288
+
289
+ ### The execution context and global data
290
+
291
+ When your function block executes, the _object context_ (i.e. `self`) is set to
292
+ an instance of {FunctionsFramework::Function::Callable}. Each function
293
+ invocation (including functions that might be running concurrently in separate
294
+ threads) runs within a different instance, to help you avoid having functions
295
+ interfere with each other.
296
+
297
+ The object context also defines a few methods that may be useful when writing
298
+ your function.
299
+
300
+ First, you can obtain the logger by calling the
301
+ {FunctionsFramework::Function::Callable#logger} convenience method. This is
302
+ the same logger that is provided by the HTTP request object or by the
303
+ {FunctionsFramework.logger} global method.
304
+
305
+ Second, you can access global shared data by passing a key to
306
+ {FunctionsFramework::Function::Callable#global}. _Global shared data_ is a set
307
+ of key-value pairs that are available to every function invocation. By default,
308
+ two keys are available to all functions:
309
+
310
+ * `:function_name` whose String value is the name of the running function.
311
+ * `:function_type` whose value is either `:http` or `:cloud_event` depending
312
+ on the type of the running function.
313
+
314
+ Following is a simple example using the `logger` and `global` methods of the
315
+ context object:
316
+
317
+ ```ruby
318
+ require "functions_framework"
319
+
320
+ FunctionsFramework.cloud_event "hello" do |event|
321
+ logger.info "Now running the function called #{global(:function_name)}"
322
+ end
323
+ ```
324
+
325
+ To avoid concurrency issues, global shared data is immutable when executing a
326
+ function. You cannot add or delete keys or change the value of existing keys.
327
+ However, the global data is settable during startup tasks, because startup
328
+ tasks never run concurrently. You can use this feature to initialize shared
329
+ resources, as described below.
330
+
331
+ Using the global data mechanism is generally preferred over actual Ruby global
332
+ variables, because the Functions Framework can help you avoid concurrent edits.
333
+ Additionally, the framework will isolate the sets of global data associated
334
+ with different sets of functions, which lets you test functions in isolation
335
+ without the tests interfering with one another by writing to global variables.
336
+
337
+ ### Sharing resources
338
+
339
+ Although functions should generally be self-contained and stateless, it is
340
+ sometimes useful to share certain kinds of resources across multiple function
341
+ invocations that run on the same Ruby instance. For example, you might
342
+ establish a single connection to a remote database or other service, and share
343
+ it across function invocations to avoid incurring the overhead of
344
+ re-establishing it for every function invocation.
345
+
346
+ The best practice for sharing a resource across function invocations is to
347
+ initialize it in a {FunctionsFramework.on_startup} block, and reference it from
348
+ global shared data. (As discussed above, prefer to initialize shared resources
349
+ in a startup task rather than at the top level of a Ruby file, and prefer using
350
+ the Functions Framework's global data mechanism rather than Ruby's global
351
+ variables.)
352
+
353
+ Here is a simple example:
354
+
355
+ ```ruby
356
+ require "functions_framework"
357
+
358
+ # Use an on_startup block to initialize a shared client and store it in
359
+ # the global shared data.
360
+ FunctionsFramework.on_startup do
361
+ require "google/cloud/storage"
362
+ set_global :storage_client, Google::Cloud::Storage.new
363
+ end
364
+
365
+ # The shared storage_client can be accessed by all function invocations
366
+ # via the global shared data.
367
+ FunctionsFramework.http "storage_example" do |request|
368
+ bucket = global(:storage_client).bucket "my-bucket"
369
+ file = bucket.file "path/to/my-file.txt"
370
+ file.download.to_s
371
+ end
372
+ ```
373
+
374
+ Importantly, if you do share a resource across function invocations, make sure
375
+ the resource is thread-safe, so that separate functions running concurrently in
376
+ different threads can access them safely. The API clients provided by Google,
377
+ for example, are thread-safe and can be used concurrently.
378
+
379
+ Also of note: There is no guaranteed cleanup hook. The Functions Framework does
380
+ not provide a way to register a cleanup task, and we recommend against using
381
+ resources that require explicit "cleanup". This is because serverless runtimes
382
+ may perform CPU throttling, and therefore there may not be an opportunity for
383
+ cleanup tasks to run. (For example, you could register a `Kernel.at_exit` task,
384
+ but the Ruby VM may still terminate without calling it.)
385
+
201
386
  ## Structuring a project
202
387
 
203
388
  A Functions Framework based "project" or "application" is a typical Ruby
@@ -207,15 +392,16 @@ needed by the function. It must include at least one Ruby source file that
207
392
  defines functions, and can also include additional Ruby files defining classes
208
393
  and methods that assist in the function implementation.
209
394
 
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.
395
+ By convention, the "main" Ruby file that defines functions should be called
396
+ `app.rb` and be located at the root of the project. The path to this file is
397
+ sometimes known as the **function source**. The Functions Framework allows you
398
+ to specify an arbitrary source, but some hosting environments (such as Google
399
+ Cloud Functions) require it to be `./app.rb`.
213
400
 
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.
401
+ A source file can define any number of functions (with distinct names). Each of
402
+ the names is known as a **function target**.
217
403
 
218
- A simple project might look like this:
404
+ Following is a typical layout for a Functions Framework based project.
219
405
 
220
406
  ```
221
407
  (project directory)
@@ -236,13 +422,16 @@ A simple project might look like this:
236
422
  ```ruby
237
423
  # Gemfile
238
424
  source "https://rubygems.org"
239
- gem "functions_framework", "~> 0.5"
425
+ gem "functions_framework", "~> 0.7"
240
426
  ```
241
427
 
242
428
  ```ruby
243
429
  # app.rb
244
430
  require "functions_framework"
245
- require_relative "lib/hello"
431
+
432
+ FunctionsFramework.on_startup do
433
+ require_relative "lib/hello"
434
+ end
246
435
 
247
436
  FunctionsFramework.http "hello" do |request|
248
437
  Hello.new(request).build_response
@@ -257,7 +446,7 @@ class Hello
257
446
  end
258
447
 
259
448
  def build_response
260
- "Received request: #{request.method} #{request.url}\n"
449
+ "Received request: #{@request.request_method} #{@request.url}\n"
261
450
  end
262
451
  end
263
452
  ```