functions_framework 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +3 -3
- data/bin/functions-framework +3 -0
- data/docs/overview.md +3 -3
- data/docs/testing-functions.md +50 -0
- data/docs/writing-functions.md +194 -65
- data/lib/functions_framework.rb +11 -8
- data/lib/functions_framework/function.rb +142 -48
- data/lib/functions_framework/registry.rb +10 -20
- data/lib/functions_framework/server.rb +19 -14
- data/lib/functions_framework/testing.rb +103 -16
- data/lib/functions_framework/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: faa749d3b692f754f7ad60dfc3a5b18c2b9b26c757769ab51e16786a6289b4ad
|
4
|
+
data.tar.gz: 8d918a95adc8caa077fdc19fbc1ec3f3b531a6de279b19da2282bbd4479305d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e372039d4885f765f634f1f90975282b12c28f97acf7ba9a5ad4dba100a444c5d6a494a8792a9ed2c2fe6cb6f969ee886dab0f26eda8334ebe21288d506413e
|
7
|
+
data.tar.gz: 1f61e76bbc9ba84283e120e77da66dc488ded6178bd07691d42d8c5472605b8d839dd85c75747b3611dfe87bffae3db2d2a4803389ff56fb3b0415f663fce2b3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v0.7.0 / 2020-09-25
|
4
|
+
|
5
|
+
* Now requires Ruby 2.5 or later.
|
6
|
+
* BREAKING CHANGE: Renamed "context" hash to "globals" and made it read-only for normal functions.
|
7
|
+
* BREAKING CHANGE: Server config is no longer passed to startup blocks.
|
8
|
+
* ADDED: Provided a "logger" convenience method in the context object.
|
9
|
+
* ADDED: Globals can be set from startup blocks, which is useful for initializing shared resources.
|
10
|
+
* ADDED: Support for testing startup tasks in the Testing module.
|
11
|
+
* ADDED: Support for controlling logging in the Testing module.
|
12
|
+
* FIXED: Fixed crash introduced in 0.6.0 when a block didn't declare an expected argument.
|
13
|
+
* FIXED: Better support for running concurrent tests.
|
14
|
+
* DOCS: Expanded documentation on initialization, execution context, and shared resources.
|
15
|
+
* DEPRECATED: The functions-framework executable is deprecated. Use functions-framework-ruby instead.
|
16
|
+
|
3
17
|
### v0.6.0 / 2020-09-17
|
4
18
|
|
5
19
|
* ADDED: You can use the --version flag to print the framework version
|
data/README.md
CHANGED
@@ -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.
|
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.
|
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.
|
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
|
data/bin/functions-framework
CHANGED
@@ -16,4 +16,7 @@
|
|
16
16
|
|
17
17
|
require "functions_framework/cli"
|
18
18
|
|
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
|
+
|
19
22
|
::FunctionsFramework::CLI.new.parse_args(::ARGV).run.complete
|
data/docs/overview.md
CHANGED
@@ -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.
|
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.
|
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.
|
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
|
data/docs/testing-functions.md
CHANGED
@@ -165,3 +165,53 @@ class MyTest < Minitest::Test
|
|
165
165
|
end
|
166
166
|
end
|
167
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
|
+
```
|
data/docs/writing-functions.md
CHANGED
@@ -111,7 +111,7 @@ dependency on Sinatra in your `Gemfile`:
|
|
111
111
|
|
112
112
|
```ruby
|
113
113
|
source "https://rubygems.org"
|
114
|
-
gem "functions_framework", "~> 0.
|
114
|
+
gem "functions_framework", "~> 0.7"
|
115
115
|
gem "sinatra", "~> 2.0"
|
116
116
|
```
|
117
117
|
|
@@ -197,67 +197,191 @@ FunctionsFramework.http "error_reporter" do |request|
|
|
197
197
|
end
|
198
198
|
```
|
199
199
|
|
200
|
-
##
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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.)
|
261
385
|
|
262
386
|
## Structuring a project
|
263
387
|
|
@@ -271,12 +395,14 @@ and methods that assist in the function implementation.
|
|
271
395
|
By convention, the "main" Ruby file that defines functions should be called
|
272
396
|
`app.rb` and be located at the root of the project. The path to this file is
|
273
397
|
sometimes known as the **function source**. The Functions Framework allows you
|
274
|
-
to specify an arbitrary source, but
|
398
|
+
to specify an arbitrary source, but some hosting environments (such as Google
|
275
399
|
Cloud Functions) require it to be `./app.rb`.
|
276
400
|
|
277
401
|
A source file can define any number of functions (with distinct names). Each of
|
278
402
|
the names is known as a **function target**.
|
279
403
|
|
404
|
+
Following is a typical layout for a Functions Framework based project.
|
405
|
+
|
280
406
|
```
|
281
407
|
(project directory)
|
282
408
|
|
|
@@ -296,13 +422,16 @@ the names is known as a **function target**.
|
|
296
422
|
```ruby
|
297
423
|
# Gemfile
|
298
424
|
source "https://rubygems.org"
|
299
|
-
gem "functions_framework", "~> 0.
|
425
|
+
gem "functions_framework", "~> 0.7"
|
300
426
|
```
|
301
427
|
|
302
428
|
```ruby
|
303
429
|
# app.rb
|
304
430
|
require "functions_framework"
|
305
|
-
|
431
|
+
|
432
|
+
FunctionsFramework.on_startup do
|
433
|
+
require_relative "lib/hello"
|
434
|
+
end
|
306
435
|
|
307
436
|
FunctionsFramework.http "hello" do |request|
|
308
437
|
Hello.new(request).build_response
|
data/lib/functions_framework.rb
CHANGED
@@ -175,10 +175,8 @@ module FunctionsFramework
|
|
175
175
|
# run only when preparing to run functions. They are not run, for example,
|
176
176
|
# if an app is loaded to verify its integrity during deployment.
|
177
177
|
#
|
178
|
-
# Startup tasks are passed
|
179
|
-
#
|
180
|
-
# {FunctionsFramework::Server::Config} specifying the (frozen) server
|
181
|
-
# configuration. Tasks have no return value.
|
178
|
+
# Startup tasks are passed the {FunctionsFramework::Function} identifying
|
179
|
+
# the function to execute, and have no return value.
|
182
180
|
#
|
183
181
|
# @param block [Proc] The startup task
|
184
182
|
# @return [self]
|
@@ -189,8 +187,9 @@ module FunctionsFramework
|
|
189
187
|
end
|
190
188
|
|
191
189
|
##
|
192
|
-
#
|
193
|
-
#
|
190
|
+
# Run startup tasks, then start the functions framework server in the
|
191
|
+
# background. The startup tasks and target function will be looked up in
|
192
|
+
# the global registry.
|
194
193
|
#
|
195
194
|
# @param target [FunctionsFramework::Function,String] The function to run,
|
196
195
|
# or the name of the function to look up in the global registry.
|
@@ -206,8 +205,12 @@ module FunctionsFramework
|
|
206
205
|
function = global_registry[target]
|
207
206
|
raise ::ArgumentError, "Undefined function: #{target.inspect}" if function.nil?
|
208
207
|
end
|
209
|
-
|
210
|
-
|
208
|
+
globals = function.populate_globals
|
209
|
+
server = Server.new function, globals, &block
|
210
|
+
global_registry.startup_tasks.each do |task|
|
211
|
+
task.call function, globals: globals, logger: server.config.logger
|
212
|
+
end
|
213
|
+
globals.freeze
|
211
214
|
server.respond_to_signals
|
212
215
|
server.start
|
213
216
|
end
|
@@ -18,44 +18,91 @@ module FunctionsFramework
|
|
18
18
|
#
|
19
19
|
# A function has a name, a type, and an implementation.
|
20
20
|
#
|
21
|
+
# ## Function implementations
|
22
|
+
#
|
21
23
|
# The implementation in general is an object that responds to the `call`
|
22
|
-
# method.
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
24
|
+
# method.
|
25
|
+
#
|
26
|
+
# * For a function of type `:http`, the `call` method takes a single
|
27
|
+
# `Rack::Request` argument and returns one of various HTTP response
|
28
|
+
# types. See {FunctionsFramework::Registry.add_http}.
|
29
|
+
# * For a function of type `:cloud_event`, the `call` method takes a single
|
30
|
+
# [CloudEvent](https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event)
|
31
|
+
# argument, and does not return a value. See
|
32
|
+
# {FunctionsFramework::Registry.add_cloud_event}.
|
33
|
+
# * For a function of type `:startup_task`, the `call` method takes a
|
34
|
+
# single {FunctionsFramework::Function} argument, and does not return a
|
35
|
+
# value. See {FunctionsFramework::Registry.add_startup_task}.
|
29
36
|
#
|
30
|
-
#
|
31
|
-
# every function execution. Note that this means it may be called multiple
|
32
|
-
# times concurrently in separate threads.
|
37
|
+
# The implementation can be specified in one of three ways:
|
33
38
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
39
|
+
# * A callable object can be passed in the `callable` keyword argument. The
|
40
|
+
# object's `call` method will be invoked for every function execution.
|
41
|
+
# Note that this means it may be called multiple times concurrently in
|
42
|
+
# separate threads.
|
43
|
+
# * A callable _class_ can be passed in the `callable` keyword argument.
|
44
|
+
# This class should subclass {FunctionsFramework::Function::Callable} and
|
45
|
+
# define the `call` method. A separate instance of this class will be
|
46
|
+
# created for each function invocation.
|
47
|
+
# * A block can be provided. It will be used to define the `call` method in
|
48
|
+
# an anonymous subclass of {FunctionsFramework::Function::Callable}.
|
49
|
+
# Thus, providing a block is really just syntactic sugar for providing a
|
50
|
+
# class. (This means, for example, that the `return` keyword will work
|
51
|
+
# as expected within the block because it is treated as a method.)
|
40
52
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
53
|
+
# When the implementation is provided as a callable class or block, it is
|
54
|
+
# executed in the context of a {FunctionsFramework::Function::Callable}
|
55
|
+
# object. This object provides a convenience accessor for the Logger, and
|
56
|
+
# access to _globals_, which are data defined by the application startup
|
57
|
+
# process and available to each function invocation. Typically, globals are
|
58
|
+
# used for shared global resources such as service connections and clients.
|
47
59
|
#
|
48
60
|
class Function
|
61
|
+
##
|
62
|
+
# Create a new HTTP function definition.
|
63
|
+
#
|
64
|
+
# @param name [String] The function name
|
65
|
+
# @param callable [Class,#call] A callable object or class.
|
66
|
+
# @param block [Proc] The function code as a block.
|
67
|
+
# @return [FunctionsFramework::Function]
|
68
|
+
#
|
69
|
+
def self.http name, callable: nil, &block
|
70
|
+
new name, :http, callable: callable, &block
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Create a new CloudEvents function definition.
|
75
|
+
#
|
76
|
+
# @param name [String] The function name
|
77
|
+
# @param callable [Class,#call] A callable object or class.
|
78
|
+
# @param block [Proc] The function code as a block.
|
79
|
+
# @return [FunctionsFramework::Function]
|
80
|
+
#
|
81
|
+
def self.cloud_event name, callable: nil, &block
|
82
|
+
new name, :cloud_event, callable: callable, &block
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Create a new startup task function definition.
|
87
|
+
#
|
88
|
+
# @param callable [Class,#call] A callable object or class.
|
89
|
+
# @param block [Proc] The function code as a block.
|
90
|
+
# @return [FunctionsFramework::Function]
|
91
|
+
#
|
92
|
+
def self.startup_task callable: nil, &block
|
93
|
+
new nil, :startup_task, callable: callable, &block
|
94
|
+
end
|
95
|
+
|
49
96
|
##
|
50
97
|
# Create a new function definition.
|
51
98
|
#
|
52
99
|
# @param name [String] The function name
|
53
|
-
# @param type [Symbol] The type of function. Valid types are `:http
|
54
|
-
# `:cloud_event`.
|
100
|
+
# @param type [Symbol] The type of function. Valid types are `:http`,
|
101
|
+
# `:cloud_event`, and `:startup_task`.
|
55
102
|
# @param callable [Class,#call] A callable object or class.
|
56
103
|
# @param block [Proc] The function code as a block.
|
57
104
|
#
|
58
|
-
def initialize name, type, callable
|
105
|
+
def initialize name, type, callable: nil, &block
|
59
106
|
@name = name
|
60
107
|
@type = type
|
61
108
|
@callable = @callable_class = nil
|
@@ -64,7 +111,7 @@ module FunctionsFramework
|
|
64
111
|
elsif callable.is_a? ::Class
|
65
112
|
@callable_class = callable
|
66
113
|
elsif block_given?
|
67
|
-
@callable_class = ::Class.new
|
114
|
+
@callable_class = ::Class.new Callable do
|
68
115
|
define_method :call, &block
|
69
116
|
end
|
70
117
|
else
|
@@ -83,18 +130,38 @@ module FunctionsFramework
|
|
83
130
|
attr_reader :type
|
84
131
|
|
85
132
|
##
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
133
|
+
# Populate the given globals hash with this function's info.
|
134
|
+
#
|
135
|
+
# @param globals [Hash] Initial globals hash (optional).
|
136
|
+
# @return [Hash] A new globals hash with this function's info included.
|
137
|
+
#
|
138
|
+
def populate_globals globals = nil
|
139
|
+
result = { function_name: name, function_type: type }
|
140
|
+
result.merge! globals if globals
|
141
|
+
result
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Call the function given a set of arguments. Set the given logger and/or
|
146
|
+
# globals in the context if the callable supports it.
|
147
|
+
#
|
148
|
+
# If the given arguments exceeds what the function will accept, the args
|
149
|
+
# are silently truncated. However, if the function requires more arguments
|
150
|
+
# than are provided, an ArgumentError is raised.
|
151
|
+
#
|
152
|
+
# @param args [Array] Argument to pass to the function.
|
153
|
+
# @param logger [Logger] Logger for use by function executions.
|
154
|
+
# @param globals [Hash] Globals for the function execution context
|
155
|
+
# @return [Object] The function return value.
|
156
|
+
#
|
157
|
+
def call *args, globals: nil, logger: nil
|
158
|
+
callable = @callable || @callable_class.new(globals: globals, logger: logger)
|
159
|
+
params = callable.method(:call).parameters.map(&:first)
|
160
|
+
unless params.include? :rest
|
161
|
+
max_params = params.count(:req) + params.count(:opt)
|
162
|
+
args = args.take max_params
|
163
|
+
end
|
164
|
+
callable.call(*args)
|
98
165
|
end
|
99
166
|
|
100
167
|
##
|
@@ -102,29 +169,56 @@ module FunctionsFramework
|
|
102
169
|
#
|
103
170
|
# An object of this class is `self` while a function block is running.
|
104
171
|
#
|
105
|
-
class
|
172
|
+
class Callable
|
106
173
|
##
|
107
174
|
# Create a callable object with the given context.
|
108
175
|
#
|
109
|
-
# @param
|
110
|
-
#
|
111
|
-
# implementations should be prepared to accept any abritrary keys.
|
176
|
+
# @param globals [Hash] A set of globals available to the call.
|
177
|
+
# @param logger [Logger] A logger for use by the function call.
|
112
178
|
#
|
113
|
-
def initialize
|
114
|
-
@
|
179
|
+
def initialize globals: nil, logger: nil
|
180
|
+
@__globals = globals || {}
|
181
|
+
@__logger = logger || FunctionsFramework.logger
|
115
182
|
end
|
116
183
|
|
117
184
|
##
|
118
|
-
#
|
185
|
+
# Get the given named global.
|
186
|
+
#
|
187
|
+
# For most function calls, the following globals will be defined:
|
119
188
|
#
|
120
|
-
# * **:logger** (`Logger`) A logger for use by this function call.
|
121
189
|
# * **:function_name** (`String`) The name of the running function.
|
122
190
|
# * **:function_type** (`Symbol`) The type of the running function,
|
123
191
|
# either `:http` or `:cloud_event`.
|
124
192
|
#
|
125
|
-
#
|
193
|
+
# You can also set additional globals from a startup task.
|
194
|
+
#
|
195
|
+
# @param key [Symbol,String] The name of the global to get.
|
196
|
+
# @return [Object]
|
126
197
|
#
|
127
|
-
|
198
|
+
def global key
|
199
|
+
@__globals[key]
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# Set a global. This can be called from startup tasks, but the globals
|
204
|
+
# are frozen when the server starts, so this call will raise an exception
|
205
|
+
# if called from a normal function.
|
206
|
+
#
|
207
|
+
# @param key [Symbol,String]
|
208
|
+
# @param value [Object]
|
209
|
+
#
|
210
|
+
def set_global key, value
|
211
|
+
@__globals[key] = value
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# A logger for use by this call.
|
216
|
+
#
|
217
|
+
# @return [Logger]
|
218
|
+
#
|
219
|
+
def logger
|
220
|
+
@__logger
|
221
|
+
end
|
128
222
|
end
|
129
223
|
end
|
130
224
|
end
|
@@ -12,20 +12,16 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require "monitor"
|
16
|
-
|
17
15
|
module FunctionsFramework
|
18
16
|
##
|
19
17
|
# Registry providing lookup of functions by name.
|
20
18
|
#
|
21
19
|
class Registry
|
22
|
-
include ::MonitorMixin
|
23
|
-
|
24
20
|
##
|
25
21
|
# Create a new empty registry.
|
26
22
|
#
|
27
23
|
def initialize
|
28
|
-
@mutex = ::
|
24
|
+
@mutex = ::Mutex.new
|
29
25
|
@functions = {}
|
30
26
|
@start_tasks = []
|
31
27
|
end
|
@@ -51,17 +47,12 @@ module FunctionsFramework
|
|
51
47
|
end
|
52
48
|
|
53
49
|
##
|
54
|
-
#
|
50
|
+
# Return an array of startup tasks.
|
55
51
|
#
|
56
|
-
# @
|
57
|
-
# @return [self]
|
52
|
+
# @return [Array<FunctionsFramework::Function>]
|
58
53
|
#
|
59
|
-
def
|
60
|
-
|
61
|
-
tasks.each do |task|
|
62
|
-
task.call server.function, server.config
|
63
|
-
end
|
64
|
-
self
|
54
|
+
def startup_tasks
|
55
|
+
@mutex.synchronize { @start_tasks.dup }
|
65
56
|
end
|
66
57
|
|
67
58
|
##
|
@@ -85,7 +76,7 @@ module FunctionsFramework
|
|
85
76
|
name = name.to_s
|
86
77
|
@mutex.synchronize do
|
87
78
|
raise ::ArgumentError, "Function already defined: #{name}" if @functions.key? name
|
88
|
-
@functions[name] = Function.
|
79
|
+
@functions[name] = Function.http name, &block
|
89
80
|
end
|
90
81
|
self
|
91
82
|
end
|
@@ -106,7 +97,7 @@ module FunctionsFramework
|
|
106
97
|
name = name.to_s
|
107
98
|
@mutex.synchronize do
|
108
99
|
raise ::ArgumentError, "Function already defined: #{name}" if @functions.key? name
|
109
|
-
@functions[name] = Function.
|
100
|
+
@functions[name] = Function.cloud_event name, &block
|
110
101
|
end
|
111
102
|
self
|
112
103
|
end
|
@@ -115,16 +106,15 @@ module FunctionsFramework
|
|
115
106
|
# Add a startup task.
|
116
107
|
#
|
117
108
|
# Startup tasks are generally run just before a server starts. They are
|
118
|
-
# passed
|
119
|
-
#
|
120
|
-
# specifying the (frozen) server configuration. Tasks have no return value.
|
109
|
+
# passed the {FunctionsFramework::Function} identifying the function to
|
110
|
+
# execute, and have no return value.
|
121
111
|
#
|
122
112
|
# @param block [Proc] The startup task
|
123
113
|
# @return [self]
|
124
114
|
#
|
125
115
|
def add_startup_task &block
|
126
116
|
@mutex.synchronize do
|
127
|
-
@start_tasks << block
|
117
|
+
@start_tasks << Function.startup_task(&block)
|
128
118
|
end
|
129
119
|
self
|
130
120
|
end
|
@@ -27,17 +27,22 @@ module FunctionsFramework
|
|
27
27
|
include ::MonitorMixin
|
28
28
|
|
29
29
|
##
|
30
|
-
# Create a new web server given a function
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
30
|
+
# Create a new web server given a function definition, a set of application
|
31
|
+
# globals, and server configuration.
|
32
|
+
#
|
33
|
+
# To configure the server, pass a block that takes a
|
34
|
+
# {FunctionsFramework::Server::Config} object as the parameter. This block
|
35
|
+
# is the only opportunity to modify the configuration; once the server is
|
36
|
+
# initialized, configuration is frozen.
|
35
37
|
#
|
36
38
|
# @param function [FunctionsFramework::Function] The function to execute.
|
39
|
+
# @param globals [Hash] Globals to pass to invocations. This hash should
|
40
|
+
# normally be frozen so separate function invocations cannot interfere
|
41
|
+
# with one another's globals.
|
37
42
|
# @yield [FunctionsFramework::Server::Config] A config object that can be
|
38
43
|
# manipulated to configure this server.
|
39
44
|
#
|
40
|
-
def initialize function
|
45
|
+
def initialize function, globals
|
41
46
|
super()
|
42
47
|
@config = Config.new
|
43
48
|
yield @config if block_given?
|
@@ -46,9 +51,9 @@ module FunctionsFramework
|
|
46
51
|
@app =
|
47
52
|
case function.type
|
48
53
|
when :http
|
49
|
-
HttpApp.new function, @config
|
54
|
+
HttpApp.new function, globals, @config
|
50
55
|
when :cloud_event
|
51
|
-
EventApp.new function, @config
|
56
|
+
EventApp.new function, globals, @config
|
52
57
|
else
|
53
58
|
raise "Unrecognized function type: #{function.type}"
|
54
59
|
end
|
@@ -379,9 +384,10 @@ module FunctionsFramework
|
|
379
384
|
|
380
385
|
## @private
|
381
386
|
class HttpApp < AppBase
|
382
|
-
def initialize function, config
|
387
|
+
def initialize function, globals, config
|
383
388
|
super config
|
384
389
|
@function = function
|
390
|
+
@globals = globals
|
385
391
|
end
|
386
392
|
|
387
393
|
def call env
|
@@ -391,8 +397,7 @@ module FunctionsFramework
|
|
391
397
|
logger = env["rack.logger"] ||= @config.logger
|
392
398
|
request = ::Rack::Request.new env
|
393
399
|
logger.info "FunctionsFramework: Handling HTTP #{request.request_method} request"
|
394
|
-
|
395
|
-
calling_context.call request
|
400
|
+
@function.call request, globals: @globals, logger: logger
|
396
401
|
rescue ::StandardError => e
|
397
402
|
e
|
398
403
|
end
|
@@ -402,9 +407,10 @@ module FunctionsFramework
|
|
402
407
|
|
403
408
|
## @private
|
404
409
|
class EventApp < AppBase
|
405
|
-
def initialize function, config
|
410
|
+
def initialize function, globals, config
|
406
411
|
super config
|
407
412
|
@function = function
|
413
|
+
@globals = globals
|
408
414
|
@cloud_events = ::CloudEvents::HttpBinding.default
|
409
415
|
@legacy_events = LegacyEventConverter.new
|
410
416
|
end
|
@@ -439,8 +445,7 @@ module FunctionsFramework
|
|
439
445
|
|
440
446
|
def handle_cloud_event event, logger
|
441
447
|
logger.info "FunctionsFramework: Handling CloudEvent"
|
442
|
-
|
443
|
-
calling_context.call event
|
448
|
+
@function.call event, globals: @globals, logger: logger
|
444
449
|
"ok"
|
445
450
|
rescue ::StandardError => e
|
446
451
|
e
|
@@ -75,19 +75,71 @@ module FunctionsFramework
|
|
75
75
|
Testing.load_for_testing path, &block
|
76
76
|
end
|
77
77
|
|
78
|
+
##
|
79
|
+
# Run startup tasks for the given function name and return the initialized
|
80
|
+
# globals hash.
|
81
|
+
#
|
82
|
+
# Normally, this will be run automatically prior to the first call to the
|
83
|
+
# function using {call_http} or {call_event}, if it has not already been
|
84
|
+
# run. However, you can call it explicitly to test its behavior. It cannot
|
85
|
+
# be called more than once for any given function.
|
86
|
+
#
|
87
|
+
# By default, the {FunctionsFramework.logger} will be used, but you can
|
88
|
+
# override that by providing your own logger. In particular, to disable
|
89
|
+
# logging, you can pass `Logger.new(nil)`.
|
90
|
+
#
|
91
|
+
# @param name [String] The name of the function to start up.
|
92
|
+
# @param logger [Logger] Use the given logger instead of the Functions
|
93
|
+
# Framework's global logger. Optional.
|
94
|
+
# @param lenient [Boolean] If false (the default), raise an error if the
|
95
|
+
# given function has already had its startup tasks run. If true,
|
96
|
+
# duplicate requests to run startup tasks are ignored.
|
97
|
+
# @return [Hash] The initialized globals.
|
98
|
+
#
|
99
|
+
def run_startup_tasks name, logger: nil, lenient: false
|
100
|
+
function = Testing.current_registry[name]
|
101
|
+
raise "Unknown function name #{name}" unless function
|
102
|
+
globals = Testing.current_globals name
|
103
|
+
if globals
|
104
|
+
raise "Function #{name} has already been started up" unless lenient
|
105
|
+
else
|
106
|
+
globals = function.populate_globals
|
107
|
+
Testing.current_registry.startup_tasks.each do |task|
|
108
|
+
task.call function, globals: globals, logger: logger
|
109
|
+
end
|
110
|
+
Testing.current_globals name, globals
|
111
|
+
end
|
112
|
+
globals.freeze
|
113
|
+
end
|
114
|
+
|
78
115
|
##
|
79
116
|
# Call the given HTTP function for testing. The underlying function must
|
80
|
-
# be of type `:http`.
|
117
|
+
# be of type `:http`. Returns the Rack response.
|
118
|
+
#
|
119
|
+
# By default, the startup tasks will be run for the given function if they
|
120
|
+
# have not already been run. You can, however, disable running startup
|
121
|
+
# tasks by providing an explicit globals hash.
|
122
|
+
#
|
123
|
+
# By default, the {FunctionsFramework.logger} will be used, but you can
|
124
|
+
# override that by providing your own logger. In particular, to disable
|
125
|
+
# logging, you can pass `Logger.new(nil)`.
|
81
126
|
#
|
82
127
|
# @param name [String] The name of the function to call
|
83
128
|
# @param request [Rack::Request] The Rack request to send
|
129
|
+
# @param globals [Hash] Do not run startup tasks, and instead provide the
|
130
|
+
# globals directly. Optional.
|
131
|
+
# @param logger [Logger] Use the given logger instead of the Functions
|
132
|
+
# Framework's global logger. Optional.
|
84
133
|
# @return [Rack::Response]
|
85
134
|
#
|
86
|
-
def call_http name, request
|
87
|
-
|
135
|
+
def call_http name, request, globals: nil, logger: nil
|
136
|
+
globals ||= run_startup_tasks name, logger: logger, lenient: true
|
137
|
+
function = Testing.current_registry[name]
|
88
138
|
case function&.type
|
89
139
|
when :http
|
90
|
-
Testing.interpret_response
|
140
|
+
Testing.interpret_response do
|
141
|
+
function.call request, globals: globals, logger: logger
|
142
|
+
end
|
91
143
|
when nil
|
92
144
|
raise "Unknown function name #{name}"
|
93
145
|
else
|
@@ -99,15 +151,28 @@ module FunctionsFramework
|
|
99
151
|
# Call the given event function for testing. The underlying function must
|
100
152
|
# be of type :cloud_event`.
|
101
153
|
#
|
154
|
+
# By default, the startup tasks will be run for the given function if they
|
155
|
+
# have not already been run. You can, however, disable running startup
|
156
|
+
# tasks by providing an explicit globals hash.
|
157
|
+
#
|
158
|
+
# By default, the {FunctionsFramework.logger} will be used, but you can
|
159
|
+
# override that by providing your own logger. In particular, to disable
|
160
|
+
# logging, you can pass `Logger.new(nil)`.
|
161
|
+
#
|
102
162
|
# @param name [String] The name of the function to call
|
103
163
|
# @param event [::CloudEvents::Event] The event to send
|
164
|
+
# @param globals [Hash] Do not run startup tasks, and instead provide the
|
165
|
+
# globals directly. Optional.
|
166
|
+
# @param logger [Logger] Use the given logger instead of the Functions
|
167
|
+
# Framework's global logger. Optional.
|
104
168
|
# @return [nil]
|
105
169
|
#
|
106
|
-
def call_event name, event
|
107
|
-
|
170
|
+
def call_event name, event, globals: nil, logger: nil
|
171
|
+
globals ||= run_startup_tasks name, logger: logger, lenient: true
|
172
|
+
function = Testing.current_registry[name]
|
108
173
|
case function&.type
|
109
174
|
when :cloud_event
|
110
|
-
function.
|
175
|
+
function.call event, globals: globals, logger: logger
|
111
176
|
nil
|
112
177
|
when nil
|
113
178
|
raise "Unknown function name #{name}"
|
@@ -208,27 +273,49 @@ module FunctionsFramework
|
|
208
273
|
extend self
|
209
274
|
|
210
275
|
@testing_registries = {}
|
276
|
+
@main_globals = {}
|
211
277
|
@mutex = ::Mutex.new
|
212
278
|
|
213
279
|
class << self
|
214
280
|
## @private
|
215
281
|
def load_for_testing path
|
216
282
|
old_registry = ::FunctionsFramework.global_registry
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
283
|
+
::Thread.current[:functions_framework_testing_registry] =
|
284
|
+
@mutex.synchronize do
|
285
|
+
if @testing_registries.key? path
|
286
|
+
::FunctionsFramework.global_registry = @testing_registries[path]
|
287
|
+
else
|
288
|
+
new_registry = ::FunctionsFramework::Registry.new
|
289
|
+
::FunctionsFramework.global_registry = new_registry
|
290
|
+
::Kernel.load path
|
291
|
+
@testing_registries[path] = new_registry
|
292
|
+
end
|
225
293
|
end
|
226
|
-
|
294
|
+
::Thread.current[:functions_framework_testing_globals] = {}
|
227
295
|
yield
|
228
296
|
ensure
|
297
|
+
::Thread.current[:functions_framework_testing_registry] = nil
|
298
|
+
::Thread.current[:functions_framework_testing_globals] = nil
|
229
299
|
::FunctionsFramework.global_registry = old_registry
|
230
300
|
end
|
231
301
|
|
302
|
+
## @private
|
303
|
+
def current_registry
|
304
|
+
::Thread.current[:functions_framework_testing_registry] ||
|
305
|
+
::FunctionsFramework.global_registry
|
306
|
+
end
|
307
|
+
|
308
|
+
## @private
|
309
|
+
def current_globals name, globals = nil
|
310
|
+
name = name.to_s
|
311
|
+
globals_by_name = ::Thread.current[:functions_framework_testing_globals] || @main_globals
|
312
|
+
if globals
|
313
|
+
globals_by_name[name] = globals
|
314
|
+
else
|
315
|
+
globals_by_name[name]
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
232
319
|
## @private
|
233
320
|
def interpret_response
|
234
321
|
response =
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: functions_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Azuma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cloud_events
|
@@ -54,8 +54,8 @@ dependencies:
|
|
54
54
|
version: '2.1'
|
55
55
|
description: The Functions Framework is an open source framework for writing lightweight,
|
56
56
|
portable Ruby functions that run in a serverless environment. Functions written
|
57
|
-
to this Framework will run
|
58
|
-
|
57
|
+
to this Framework will run on Google Cloud Functions, Google Cloud Run, or any other
|
58
|
+
Knative-based environment.
|
59
59
|
email:
|
60
60
|
- dazuma@google.com
|
61
61
|
executables:
|
@@ -87,10 +87,10 @@ homepage: https://github.com/GoogleCloudPlatform/functions-framework-ruby
|
|
87
87
|
licenses:
|
88
88
|
- Apache-2.0
|
89
89
|
metadata:
|
90
|
-
changelog_uri: https://googlecloudplatform.github.io/functions-framework-ruby/v0.
|
90
|
+
changelog_uri: https://googlecloudplatform.github.io/functions-framework-ruby/v0.7.0/file.CHANGELOG.html
|
91
91
|
source_code_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby
|
92
92
|
bug_tracker_uri: https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues
|
93
|
-
documentation_uri: https://googlecloudplatform.github.io/functions-framework-ruby/v0.
|
93
|
+
documentation_uri: https://googlecloudplatform.github.io/functions-framework-ruby/v0.7.0
|
94
94
|
post_install_message:
|
95
95
|
rdoc_options: []
|
96
96
|
require_paths:
|
@@ -99,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
100
100
|
- - ">="
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: 2.
|
102
|
+
version: 2.5.0
|
103
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
104
|
requirements:
|
105
105
|
- - ">="
|