liveresource 2.0.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.
- data/.gitignore +1 -0
- data/BSDL +24 -0
- data/COPYING +59 -0
- data/GPL +339 -0
- data/README.md +289 -0
- data/Rakefile +47 -0
- data/benchmark/benchmark_helper.rb +19 -0
- data/benchmark/method_benchmark.rb +94 -0
- data/lib/live_resource.rb +66 -0
- data/lib/live_resource/attributes.rb +77 -0
- data/lib/live_resource/declarations.rb +200 -0
- data/lib/live_resource/finders.rb +43 -0
- data/lib/live_resource/log_helper.rb +24 -0
- data/lib/live_resource/methods.rb +41 -0
- data/lib/live_resource/methods/dispatcher.rb +176 -0
- data/lib/live_resource/methods/forward.rb +23 -0
- data/lib/live_resource/methods/future.rb +27 -0
- data/lib/live_resource/methods/method.rb +93 -0
- data/lib/live_resource/methods/token.rb +22 -0
- data/lib/live_resource/redis_client.rb +100 -0
- data/lib/live_resource/redis_client/attributes.rb +40 -0
- data/lib/live_resource/redis_client/methods.rb +194 -0
- data/lib/live_resource/redis_client/registration.rb +25 -0
- data/lib/live_resource/resource.rb +44 -0
- data/lib/live_resource/resource_proxy.rb +180 -0
- data/old/benchmark/attribute_benchmark.rb +58 -0
- data/old/benchmark/thread_benchmark.rb +89 -0
- data/old/examples/attribute.rb +22 -0
- data/old/examples/attribute_rmw.rb +30 -0
- data/old/examples/attribute_subscriber.rb +32 -0
- data/old/examples/method_provider_sleep.rb +22 -0
- data/old/examples/methods.rb +37 -0
- data/old/lib/live_resource/subscriber.rb +98 -0
- data/old/redis_test.rb +127 -0
- data/old/state_publisher_test.rb +139 -0
- data/old/test/attribute_modify_test.rb +52 -0
- data/old/test/attribute_options_test.rb +54 -0
- data/old/test/attribute_subscriber_test.rb +94 -0
- data/old/test/composite_resource_test.rb +61 -0
- data/old/test/method_sender_test.rb +41 -0
- data/old/test/redis_api_test.rb +185 -0
- data/old/test/simple_attribute_test.rb +75 -0
- data/test/attribute_test.rb +212 -0
- data/test/declarations_test.rb +119 -0
- data/test/logger_test.rb +44 -0
- data/test/method_call_test.rb +223 -0
- data/test/method_forward_continue_test.rb +83 -0
- data/test/method_params_test.rb +81 -0
- data/test/method_routing_test.rb +59 -0
- data/test/multiple_class_test.rb +47 -0
- data/test/new_api_DISABLED.rb +127 -0
- data/test/test_helper.rb +9 -0
- data/test/volume_create_DISABLED.rb +74 -0
- metadata +129 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class ParamsTest < Test::Unit::TestCase
|
4
|
+
class ParamsClass
|
5
|
+
include LiveResource::Resource
|
6
|
+
|
7
|
+
remote_reader :name
|
8
|
+
remote_reader :age
|
9
|
+
|
10
|
+
resource_name :name
|
11
|
+
resource_class :params_class
|
12
|
+
|
13
|
+
def initialize(name, age)
|
14
|
+
remote_attribute_write(:name, name)
|
15
|
+
remote_attribute_write(:age, age)
|
16
|
+
end
|
17
|
+
|
18
|
+
def no_params_method
|
19
|
+
self.age
|
20
|
+
end
|
21
|
+
|
22
|
+
def fixed_params_method(arg1, arg2)
|
23
|
+
arg1 + arg2
|
24
|
+
end
|
25
|
+
|
26
|
+
def splat_params_method(*params)
|
27
|
+
params.length
|
28
|
+
end
|
29
|
+
|
30
|
+
def mixed_params_method(arg1, arg2, *params)
|
31
|
+
result = arg1 + arg2
|
32
|
+
params.each do |param|
|
33
|
+
result = result + param
|
34
|
+
end
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def setup
|
40
|
+
Redis.new.flushall
|
41
|
+
|
42
|
+
LiveResource::RedisClient.logger.level = Logger::INFO
|
43
|
+
|
44
|
+
# Class resources
|
45
|
+
LiveResource::register ParamsClass
|
46
|
+
|
47
|
+
@test_class = LiveResource::find(:params_class)
|
48
|
+
@test_instance = @test_class.new("bob", 42)
|
49
|
+
end
|
50
|
+
|
51
|
+
def teardown
|
52
|
+
LiveResource::stop
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_no_params_method
|
56
|
+
assert_equal 42, @test_instance.no_params_method
|
57
|
+
assert_raise(ArgumentError) { @test_instance.no_params_method("foo") }
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_fixed_params_method
|
61
|
+
assert_equal 13, @test_instance.fixed_params_method(6, 7)
|
62
|
+
assert_raise(ArgumentError) { @test_instance.fixed_params_method }
|
63
|
+
assert_raise(ArgumentError) { @test_instance.fixed_params_method(6, 7, 8) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_splat_params_method
|
67
|
+
assert_equal 0, @test_instance.splat_params_method
|
68
|
+
assert_equal 1, @test_instance.splat_params_method("foo")
|
69
|
+
assert_equal 2, @test_instance.splat_params_method("foo", "bar")
|
70
|
+
assert_equal 3, @test_instance.splat_params_method("foo", "bar", "baz")
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_mixed_params_method
|
74
|
+
assert_equal 3, @test_instance.mixed_params_method(1, 2)
|
75
|
+
assert_equal 6, @test_instance.mixed_params_method(1, 2, 3)
|
76
|
+
assert_equal 10, @test_instance.mixed_params_method(1, 2, 3, 4)
|
77
|
+
|
78
|
+
assert_raise(ArgumentError) { @test_instance.mixed_params_method }
|
79
|
+
assert_raise(ArgumentError) { @test_instance.mixed_params_method(1) }
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MethodRoutingTest < Test::Unit::TestCase
|
4
|
+
class Class1
|
5
|
+
include LiveResource::Resource
|
6
|
+
|
7
|
+
resource_class :class_1
|
8
|
+
resource_name :object_id
|
9
|
+
|
10
|
+
def method1
|
11
|
+
[1]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Class2
|
16
|
+
include LiveResource::Resource
|
17
|
+
|
18
|
+
resource_class :class_2
|
19
|
+
resource_name :object_id
|
20
|
+
|
21
|
+
def method2(i)
|
22
|
+
[i, 2]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Class3
|
27
|
+
include LiveResource::Resource
|
28
|
+
|
29
|
+
resource_class :class_3
|
30
|
+
resource_name :object_id
|
31
|
+
|
32
|
+
def method3(i, j)
|
33
|
+
[i, j, 3]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup
|
38
|
+
Redis.new.flushall
|
39
|
+
Class1.new
|
40
|
+
Class2.new
|
41
|
+
Class3.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def teardown
|
45
|
+
LiveResource::stop
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_create_method
|
49
|
+
m = LiveResource::RemoteMethod.new(:method => :method1)
|
50
|
+
c1 = LiveResource::any(:class_1)
|
51
|
+
c2 = LiveResource::any(:class_2)
|
52
|
+
c3 = LiveResource::any(:class_3)
|
53
|
+
|
54
|
+
m.add_destination(c2, :method2, [])
|
55
|
+
m.add_destination(c3, :method3, [])
|
56
|
+
|
57
|
+
assert_equal [1, 2, 3], c1.wait_for_done(c1.remote_send(m))
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class ClassMethodTest < Test::Unit::TestCase
|
4
|
+
class Class1
|
5
|
+
include LiveResource::Resource
|
6
|
+
|
7
|
+
resource_class :class_1
|
8
|
+
resource_name :object_id
|
9
|
+
|
10
|
+
def self.class_method_1
|
11
|
+
"42"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Class2
|
16
|
+
include LiveResource::Resource
|
17
|
+
|
18
|
+
resource_class :class_2
|
19
|
+
resource_name :object_id
|
20
|
+
|
21
|
+
def self.class_method_2
|
22
|
+
"foo"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup
|
27
|
+
Redis.new.flushall
|
28
|
+
|
29
|
+
LiveResource::register Class1
|
30
|
+
LiveResource::register Class2
|
31
|
+
end
|
32
|
+
|
33
|
+
def teardown
|
34
|
+
LiveResource::stop
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_classes_dont_conflict
|
38
|
+
c1 = LiveResource::find(:class_1)
|
39
|
+
c2 = LiveResource::find(:class_2)
|
40
|
+
|
41
|
+
assert_equal true, c1.respond_to?(:class_method_1)
|
42
|
+
assert_equal false, c1.respond_to?(:class_method_2)
|
43
|
+
|
44
|
+
assert_equal true, c2.respond_to?(:class_method_2)
|
45
|
+
assert_equal false, c2.respond_to?(:class_method_1)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class VolumeResource
|
4
|
+
include LiveResource::Resource
|
5
|
+
|
6
|
+
# Class type: "volume-class"
|
7
|
+
# Class instance name: "volume-class.{hostname}.{pid}"
|
8
|
+
# Instance type: "volume"
|
9
|
+
# Instance name: "volume.my_volume_name"
|
10
|
+
resource_type :volume
|
11
|
+
resource_name "<%= self.name %>"
|
12
|
+
|
13
|
+
# Attribute and method declarations stay the same.
|
14
|
+
remote_attribute :name, :online
|
15
|
+
remote_method :start, :stop
|
16
|
+
|
17
|
+
# LR will always provide class-namespace methods like all() and
|
18
|
+
# find(&block), implemented on the client side. If any class
|
19
|
+
# methods are provided, then we'll also start a class-level method
|
20
|
+
# dispatcher.
|
21
|
+
remote_class_method :create_volume
|
22
|
+
|
23
|
+
def initialize(name)
|
24
|
+
@name = name
|
25
|
+
@online = false
|
26
|
+
|
27
|
+
LiveResource::Worker::register(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def start
|
31
|
+
unless @online
|
32
|
+
info "starting"
|
33
|
+
@online = true
|
34
|
+
end
|
35
|
+
|
36
|
+
@online
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop
|
40
|
+
if @online
|
41
|
+
info "stopping"
|
42
|
+
@online = false
|
43
|
+
end
|
44
|
+
|
45
|
+
@online
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.create_volume(name)
|
49
|
+
info "creating new volume #{name}"
|
50
|
+
VolumeResource.new(name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class ModuleTest < Test::Unit::TestCase
|
55
|
+
def setup
|
56
|
+
Redis.new.flushall
|
57
|
+
|
58
|
+
LiveResource::Worker::register_class(VolumeResource)
|
59
|
+
|
60
|
+
# Run method dispatchers in separate threads; use LR::run to
|
61
|
+
# block while they run (e.g., for a dedicated worker process).
|
62
|
+
# Each instance will still have its own thread regardless.
|
63
|
+
LiveResource::Worker::start
|
64
|
+
end
|
65
|
+
|
66
|
+
def teardown
|
67
|
+
# Stop and join method dispatchers
|
68
|
+
LiveResource::Worker::stop
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_foo
|
72
|
+
# Resource finders:
|
73
|
+
# LiveResource::all(type) -> [array]
|
74
|
+
# LiveResource::find(type) -> class resource
|
75
|
+
# LiveResource::find(t,name) -> resource matching name
|
76
|
+
# LiveResource::find(t,&block) -> resource matching condition
|
77
|
+
# LiveResource::first(type) -> first resource
|
78
|
+
# LiveResource::any(type) -> picks one randomly
|
79
|
+
|
80
|
+
# Find on type alone gives the class resource.
|
81
|
+
vc = LiveResource::find(:volume)
|
82
|
+
|
83
|
+
# Sync class-level method call
|
84
|
+
vc.create_volume "foo"
|
85
|
+
|
86
|
+
# Async class-level call with future
|
87
|
+
done = vc.create_volume!?("bar")
|
88
|
+
|
89
|
+
# Wait for complete
|
90
|
+
done.value
|
91
|
+
|
92
|
+
# Find by resource type and name, in this case name is the same
|
93
|
+
# as volume name. (See resource_name declaration above.)
|
94
|
+
v1 = LiveResource::find(:volume, "foo")
|
95
|
+
|
96
|
+
# Find by resource matching type and block.
|
97
|
+
v2 = LiveResource::find(:volume) { |v| v.name == "bar" }
|
98
|
+
|
99
|
+
# Attribute read: only consults Redis
|
100
|
+
assert_equal false, v1.online
|
101
|
+
|
102
|
+
# Syncronous method call, blocks until complete
|
103
|
+
assert_equal true, v1.start
|
104
|
+
assert_equal true, v1.online
|
105
|
+
|
106
|
+
# Async method call
|
107
|
+
v2.start!
|
108
|
+
10.times { Thread.pass }
|
109
|
+
assert_equal true, v2.online
|
110
|
+
|
111
|
+
# Async method with future
|
112
|
+
done = []
|
113
|
+
|
114
|
+
# Note: each applies to instances, excluding the class instance.
|
115
|
+
LiveResource::each(:volume) { |v| done << v.stop!? }
|
116
|
+
|
117
|
+
begin
|
118
|
+
done.each { |d| d.value }
|
119
|
+
rescue e
|
120
|
+
# Log error and continue waiting for others to complete.
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
## TODO: wrap the above construct in one method that takes one
|
125
|
+
# block for the action, another for completion action.
|
126
|
+
end
|
127
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class VolumeResource
|
4
|
+
include LiveResource::Resource
|
5
|
+
|
6
|
+
resource_type :volume
|
7
|
+
resource_name "<%= self.name %>"
|
8
|
+
|
9
|
+
remote_attribute :name, :status
|
10
|
+
remote_method :start, :stop
|
11
|
+
|
12
|
+
# Internal-only volume create methods
|
13
|
+
remote_method :stage1_create_gluster_config
|
14
|
+
remote_method :stage2_create_gluster_config_done
|
15
|
+
|
16
|
+
# TODO:
|
17
|
+
# Implicit in declaring remote_class_method :new
|
18
|
+
# class << self
|
19
|
+
# alias_method object_new new
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
remote_class_method :new
|
23
|
+
|
24
|
+
def initialize(name)
|
25
|
+
@name = name
|
26
|
+
|
27
|
+
LiveResource::Worker::register(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.new(name)
|
31
|
+
info "creating new volume #{name}"
|
32
|
+
v = VolumeResource.object_new(name)
|
33
|
+
|
34
|
+
v.status = :creating
|
35
|
+
v.stage1_create_gluster_config!
|
36
|
+
end
|
37
|
+
|
38
|
+
def stage1_create_gluster_config
|
39
|
+
params = { } # stuff here that Gluster needs
|
40
|
+
|
41
|
+
# This builds up a state object that's returned to the method dispatcher.
|
42
|
+
# - Instead of reply, this is a forward
|
43
|
+
# - It goes to a :gluster instance
|
44
|
+
# - The new method and params are wrapped up in same way as a new method call
|
45
|
+
# - There's a hidden parameter which specifies the continuation method
|
46
|
+
LiveResource::any(:gluster).forward(:create_config, self).continue_to(:stage2_create_gluster_config_done)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return values from remote methods:
|
50
|
+
# - Explicit reply
|
51
|
+
# - Forward to another actor
|
52
|
+
# - Something else, that gets treated as a reply
|
53
|
+
#
|
54
|
+
# Method dispatcher may already have state for continuing to another method.
|
55
|
+
|
56
|
+
def stage2_create_gluster_config_done
|
57
|
+
@status = :stopped
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class GlusterResource
|
62
|
+
include LiveResource::Resource
|
63
|
+
|
64
|
+
resource_type :gluster
|
65
|
+
resource_name "<%= self.hostname %>"
|
66
|
+
|
67
|
+
remote_method :create_config
|
68
|
+
|
69
|
+
def create_config(volume)
|
70
|
+
# do gluster stuff here to set up volume
|
71
|
+
|
72
|
+
nil # No params necessary
|
73
|
+
end
|
74
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: liveresource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Spectra Logic
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: yard
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Remote-callable attributes and methods for IPC and cluster use.
|
47
|
+
email: public@joshcarter.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- .gitignore
|
53
|
+
- BSDL
|
54
|
+
- COPYING
|
55
|
+
- GPL
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- benchmark/benchmark_helper.rb
|
59
|
+
- benchmark/method_benchmark.rb
|
60
|
+
- lib/live_resource.rb
|
61
|
+
- lib/live_resource/attributes.rb
|
62
|
+
- lib/live_resource/declarations.rb
|
63
|
+
- lib/live_resource/finders.rb
|
64
|
+
- lib/live_resource/log_helper.rb
|
65
|
+
- lib/live_resource/methods.rb
|
66
|
+
- lib/live_resource/methods/dispatcher.rb
|
67
|
+
- lib/live_resource/methods/forward.rb
|
68
|
+
- lib/live_resource/methods/future.rb
|
69
|
+
- lib/live_resource/methods/method.rb
|
70
|
+
- lib/live_resource/methods/token.rb
|
71
|
+
- lib/live_resource/redis_client.rb
|
72
|
+
- lib/live_resource/redis_client/attributes.rb
|
73
|
+
- lib/live_resource/redis_client/methods.rb
|
74
|
+
- lib/live_resource/redis_client/registration.rb
|
75
|
+
- lib/live_resource/resource.rb
|
76
|
+
- lib/live_resource/resource_proxy.rb
|
77
|
+
- old/benchmark/attribute_benchmark.rb
|
78
|
+
- old/benchmark/thread_benchmark.rb
|
79
|
+
- old/examples/attribute.rb
|
80
|
+
- old/examples/attribute_rmw.rb
|
81
|
+
- old/examples/attribute_subscriber.rb
|
82
|
+
- old/examples/method_provider_sleep.rb
|
83
|
+
- old/examples/methods.rb
|
84
|
+
- old/lib/live_resource/subscriber.rb
|
85
|
+
- old/redis_test.rb
|
86
|
+
- old/state_publisher_test.rb
|
87
|
+
- old/test/attribute_modify_test.rb
|
88
|
+
- old/test/attribute_options_test.rb
|
89
|
+
- old/test/attribute_subscriber_test.rb
|
90
|
+
- old/test/composite_resource_test.rb
|
91
|
+
- old/test/method_sender_test.rb
|
92
|
+
- old/test/redis_api_test.rb
|
93
|
+
- old/test/simple_attribute_test.rb
|
94
|
+
- test/attribute_test.rb
|
95
|
+
- test/declarations_test.rb
|
96
|
+
- test/logger_test.rb
|
97
|
+
- test/method_call_test.rb
|
98
|
+
- test/method_forward_continue_test.rb
|
99
|
+
- test/method_params_test.rb
|
100
|
+
- test/method_routing_test.rb
|
101
|
+
- test/multiple_class_test.rb
|
102
|
+
- test/new_api_DISABLED.rb
|
103
|
+
- test/test_helper.rb
|
104
|
+
- test/volume_create_DISABLED.rb
|
105
|
+
homepage: https://github.com/joshcarter/liveresource
|
106
|
+
licenses: []
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.8.24
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Live Resource
|
129
|
+
test_files: []
|