cfoundry 0.6.1.rc4 → 0.7.0.rc1
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/lib/cfoundry/client.rb +1 -20
- data/lib/cfoundry/rest_client.rb +8 -5
- data/lib/cfoundry/v2/model.rb +11 -3
- data/lib/cfoundry/v2/model_magic.rb +0 -21
- data/lib/cfoundry/version.rb +1 -1
- data/spec/cfoundry/client_spec.rb +1 -7
- data/spec/cfoundry/rest_client_spec.rb +22 -4
- data/spec/cfoundry/v2/model_magic_spec.rb +7 -8
- data/spec/cfoundry/v2/model_spec.rb +89 -5
- data/spec/spec_helper.rb +0 -1
- metadata +3 -22
- data/lib/cfoundry/v1/app.rb +0 -363
- data/lib/cfoundry/v1/base.rb +0 -90
- data/lib/cfoundry/v1/client.rb +0 -193
- data/lib/cfoundry/v1/framework.rb +0 -21
- data/lib/cfoundry/v1/model.rb +0 -178
- data/lib/cfoundry/v1/model_magic.rb +0 -129
- data/lib/cfoundry/v1/runtime.rb +0 -24
- data/lib/cfoundry/v1/service.rb +0 -39
- data/lib/cfoundry/v1/service_instance.rb +0 -32
- data/lib/cfoundry/v1/service_plan.rb +0 -19
- data/lib/cfoundry/v1/user.rb +0 -22
- data/spec/cfoundry/v1/base_spec.rb +0 -96
- data/spec/cfoundry/v1/client_spec.rb +0 -17
- data/spec/cfoundry/v1/model_magic_spec.rb +0 -43
- data/spec/support/v1_fake_helper.rb +0 -144
@@ -1,129 +0,0 @@
|
|
1
|
-
require "cfoundry/validator"
|
2
|
-
|
3
|
-
module CFoundry::V1
|
4
|
-
module BaseClientMethods
|
5
|
-
end
|
6
|
-
|
7
|
-
module ClientMethods
|
8
|
-
end
|
9
|
-
|
10
|
-
module ModelMagic
|
11
|
-
attr_accessor :guid_name
|
12
|
-
|
13
|
-
def read_locations
|
14
|
-
@read_locations ||= {}
|
15
|
-
end
|
16
|
-
|
17
|
-
def write_locations
|
18
|
-
@write_locations ||= {}
|
19
|
-
end
|
20
|
-
|
21
|
-
def read_only_attributes
|
22
|
-
@read_only_attributes ||= []
|
23
|
-
end
|
24
|
-
|
25
|
-
def on_client(&blk)
|
26
|
-
ClientMethods.module_eval(&blk)
|
27
|
-
end
|
28
|
-
|
29
|
-
def on_base_client(&blk)
|
30
|
-
BaseClientMethods.module_eval(&blk)
|
31
|
-
end
|
32
|
-
|
33
|
-
def define_client_methods(klass = self)
|
34
|
-
singular = klass.object_name
|
35
|
-
plural = klass.plural_object_name
|
36
|
-
|
37
|
-
base_singular = klass.base_object_name
|
38
|
-
base_plural = klass.plural_base_object_name
|
39
|
-
|
40
|
-
on_base_client do
|
41
|
-
define_method(base_singular) do |guid|
|
42
|
-
get(base_plural, guid, :accept => :json)
|
43
|
-
end
|
44
|
-
|
45
|
-
define_method(:"create_#{base_singular}") do |payload|
|
46
|
-
post(base_plural, :content => :json, :accept => :json, :payload => payload)
|
47
|
-
end
|
48
|
-
|
49
|
-
define_method(:"delete_#{base_singular}") do |guid|
|
50
|
-
delete(base_plural, guid)
|
51
|
-
true
|
52
|
-
end
|
53
|
-
|
54
|
-
define_method(:"update_#{base_singular}") do |guid, payload|
|
55
|
-
put(base_plural, guid, :content => :json, :payload => payload)
|
56
|
-
end
|
57
|
-
|
58
|
-
define_method(base_plural) do |*args|
|
59
|
-
get(base_plural, :accept => :json)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
on_client do
|
64
|
-
if klass.guid_name
|
65
|
-
define_method(:"#{singular}_by_#{klass.guid_name}") do |guid|
|
66
|
-
obj = send(singular, guid)
|
67
|
-
obj if obj.exists?
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
define_method(singular) do |*args|
|
72
|
-
guid, _ = args
|
73
|
-
klass.new(guid, self)
|
74
|
-
end
|
75
|
-
|
76
|
-
define_method(plural) do |*args|
|
77
|
-
options, _ = args
|
78
|
-
options ||= {}
|
79
|
-
|
80
|
-
@base.send(base_plural).collect do |json|
|
81
|
-
klass.new(json[klass.guid_name], self, json)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def attribute(name, type, opts = {})
|
88
|
-
default = opts[:default]
|
89
|
-
is_guid = opts[:guid]
|
90
|
-
read_only = opts[:read_only]
|
91
|
-
write_only = opts[:write_only]
|
92
|
-
has_default = opts.key?(:default)
|
93
|
-
|
94
|
-
read_locations[name] = Array(opts[:read] || opts[:at] || name) unless write_only
|
95
|
-
write_locations[name] = Array(opts[:write] || opts[:at] || name) unless read_only
|
96
|
-
|
97
|
-
read_only_attributes << name if read_only
|
98
|
-
|
99
|
-
self.guid_name = name if is_guid
|
100
|
-
|
101
|
-
define_method(name) do
|
102
|
-
return @guid if @guid && is_guid
|
103
|
-
|
104
|
-
read = read_manifest
|
105
|
-
read.key?(name) ? read[name] : default
|
106
|
-
end
|
107
|
-
|
108
|
-
define_method(:"#{name}=") do |val|
|
109
|
-
unless has_default && val == default
|
110
|
-
CFoundry::Validator.validate_type(val, type)
|
111
|
-
end
|
112
|
-
|
113
|
-
@guid = val if is_guid
|
114
|
-
|
115
|
-
@manifest ||= {}
|
116
|
-
|
117
|
-
old = read_manifest[name]
|
118
|
-
@changes[name] = [old, val] if old != val
|
119
|
-
|
120
|
-
write_to = read_only ? self.class.read_locations : self.class.write_locations
|
121
|
-
|
122
|
-
put(val, @manifest, write_to[name])
|
123
|
-
end
|
124
|
-
|
125
|
-
private name if write_only
|
126
|
-
private :"#{name}=" if read_only
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
data/lib/cfoundry/v1/runtime.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module CFoundry::V1
|
2
|
-
class Runtime
|
3
|
-
attr_accessor :name, :description, :debug_modes
|
4
|
-
|
5
|
-
def initialize(name, description = nil, debug_modes = nil)
|
6
|
-
@name = name
|
7
|
-
@description = description
|
8
|
-
@debug_modes = debug_modes
|
9
|
-
end
|
10
|
-
|
11
|
-
def eql?(other)
|
12
|
-
other.is_a?(self.class) && other.name == @name
|
13
|
-
end
|
14
|
-
alias :== :eql?
|
15
|
-
|
16
|
-
def apps
|
17
|
-
[] # not supported by v1
|
18
|
-
end
|
19
|
-
|
20
|
-
def debug_modes
|
21
|
-
@debug_modes || []
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/cfoundry/v1/service.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
module CFoundry::V1
|
2
|
-
class Service
|
3
|
-
attr_accessor :label, :version, :description, :type, :provider, :state, :service_plans
|
4
|
-
|
5
|
-
def initialize(label, version = nil, description = nil,
|
6
|
-
type = nil, provider = "core", state = nil,
|
7
|
-
service_plans = [])
|
8
|
-
@label = label
|
9
|
-
@description = description
|
10
|
-
@version = version
|
11
|
-
@type = type
|
12
|
-
@provider = provider
|
13
|
-
@state = state
|
14
|
-
@service_plans = service_plans
|
15
|
-
end
|
16
|
-
|
17
|
-
def eql?(other)
|
18
|
-
other.is_a?(self.class) && other.label == @label
|
19
|
-
end
|
20
|
-
alias :== :eql?
|
21
|
-
|
22
|
-
def active
|
23
|
-
true
|
24
|
-
end
|
25
|
-
|
26
|
-
def deprecated?
|
27
|
-
@state == :deprecated
|
28
|
-
end
|
29
|
-
|
30
|
-
def current?
|
31
|
-
@state == :current
|
32
|
-
end
|
33
|
-
|
34
|
-
def default_service_plan
|
35
|
-
service_plans.find(&:default?)
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require "cfoundry/v1/model"
|
2
|
-
|
3
|
-
module CFoundry::V1
|
4
|
-
class ServiceInstance < Model
|
5
|
-
self.base_object_name = :service
|
6
|
-
|
7
|
-
attribute :name, :string, :guid => true
|
8
|
-
attribute :created, :integer, :at => [:meta, :created]
|
9
|
-
attribute :updated, :integer, :at => [:meta, :updated]
|
10
|
-
attribute :tags, [:string], :at => [:meta, :tags]
|
11
|
-
attribute :type, :string
|
12
|
-
attribute :vendor, :string
|
13
|
-
attribute :version, :string
|
14
|
-
attribute :tier, :string
|
15
|
-
attribute :properties, :hash
|
16
|
-
|
17
|
-
define_client_methods
|
18
|
-
|
19
|
-
alias_method :created_unix, :created
|
20
|
-
alias_method :updated_unix, :updated
|
21
|
-
|
22
|
-
# Timestamp of when the service was created.
|
23
|
-
def created
|
24
|
-
Time.at(created_unix)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Timestamp of when the service was last updated.
|
28
|
-
def updated
|
29
|
-
Time.at(updated_unix)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module CFoundry::V1
|
2
|
-
|
3
|
-
class ServicePlan
|
4
|
-
|
5
|
-
attr_accessor :name, :description
|
6
|
-
|
7
|
-
def initialize(name, description, is_default)
|
8
|
-
@name = name
|
9
|
-
@description = description
|
10
|
-
@is_default = is_default
|
11
|
-
end
|
12
|
-
|
13
|
-
def default?
|
14
|
-
@is_default
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
data/lib/cfoundry/v1/user.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
require "cfoundry/v1/model"
|
2
|
-
|
3
|
-
module CFoundry::V1
|
4
|
-
class User < Model
|
5
|
-
attribute :email, :string, :guid => true
|
6
|
-
attribute :password, :string, :write_only => true
|
7
|
-
attribute :admin, :boolean
|
8
|
-
|
9
|
-
define_client_methods
|
10
|
-
|
11
|
-
alias_method :admin?, :admin
|
12
|
-
|
13
|
-
def change_password!(new, old)
|
14
|
-
if @client.base.uaa
|
15
|
-
@client.base.uaa.change_password(guid, new, old)
|
16
|
-
else
|
17
|
-
self.password = new
|
18
|
-
update!
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe CFoundry::V1::Base do
|
4
|
-
let(:base) { CFoundry::V1::Base.new("https://api.cloudfoundry.com") }
|
5
|
-
|
6
|
-
describe '#get' do
|
7
|
-
let(:options) { {} }
|
8
|
-
subject { base.get("foo", options) }
|
9
|
-
|
10
|
-
context 'when successful' do
|
11
|
-
context 'and the accept type is JSON' do
|
12
|
-
let(:options) { {:accept => :json} }
|
13
|
-
|
14
|
-
it 'returns the parsed JSON' do
|
15
|
-
stub_request(:get, 'https://api.cloudfoundry.com/foo').to_return :status => 201, :body => "{\"hello\": \"there\"}"
|
16
|
-
expect(subject).to eq(:hello => "there")
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'and the accept type is not JSON' do
|
21
|
-
let(:options) { {:accept => :form} }
|
22
|
-
|
23
|
-
it 'returns the body' do
|
24
|
-
stub_request(:get, 'https://api.cloudfoundry.com/foo').to_return :status => 201, :body => "body"
|
25
|
-
expect(subject).to eq "body"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'when an error occurs' do
|
31
|
-
let(:response_code) { 404 }
|
32
|
-
|
33
|
-
it 'raises the correct error if JSON is parsed successfully' do
|
34
|
-
stub_request(:get, 'https://api.cloudfoundry.com/foo').to_return :status => response_code,
|
35
|
-
:body => "{\"code\": 111, \"description\": \"Something bad happened\"}"
|
36
|
-
expect {subject}.to raise_error CFoundry::SystemError, "111: Something bad happened"
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'raises the correct error if code is missing from response' do
|
40
|
-
stub_request(:get, 'https://api.cloudfoundry.com/foo').to_return :status => response_code,
|
41
|
-
:body => "{\"description\": \"Something bad happened\"}"
|
42
|
-
expect {subject}.to raise_error CFoundry::NotFound
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'raises the correct error if response body is not JSON' do
|
46
|
-
stub_request(:get, 'https://api.cloudfoundry.com/foo').to_return :status => response_code,
|
47
|
-
:body => "Error happened"
|
48
|
-
expect {subject}.to raise_error CFoundry::NotFound
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'raises a generic APIError if code is not recognized' do
|
52
|
-
stub_request(:get, 'https://api.cloudfoundry.com/foo').to_return :status => response_code,
|
53
|
-
:body => "{\"code\": 6932, \"description\": \"Something bad happened\"}"
|
54
|
-
expect {subject}.to raise_error CFoundry::APIError, "6932: Something bad happened"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "#upload_app" do
|
60
|
-
let!(:stubbed_request) do
|
61
|
-
stub_request(:post, 'https://api.cloudfoundry.com/apps/app_name/application').to_return(:status => 200).with(:body => /#{expected_zipfile}/)
|
62
|
-
end
|
63
|
-
|
64
|
-
subject { base.upload_app("app_name", zipfile)}
|
65
|
-
|
66
|
-
context "when passed a falsy zipfile" do
|
67
|
-
let(:zipfile) { false }
|
68
|
-
let(:expected_zipfile) { "empty.zip" }
|
69
|
-
|
70
|
-
it "creates a temporary empty zipfile" do
|
71
|
-
subject
|
72
|
-
|
73
|
-
stubbed_request.should have_been_requested
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context "when passed a valid zipfile" do
|
78
|
-
let(:zipfile) { "proper-zipfile.zip" }
|
79
|
-
let(:expected_zipfile) { zipfile }
|
80
|
-
|
81
|
-
before do
|
82
|
-
File.new(zipfile, "wb")
|
83
|
-
end
|
84
|
-
|
85
|
-
after do
|
86
|
-
FileUtils.rm(zipfile)
|
87
|
-
end
|
88
|
-
|
89
|
-
it "uses that zipfile" do
|
90
|
-
subject
|
91
|
-
|
92
|
-
stubbed_request.should have_been_requested
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe CFoundry::V1::Client do
|
4
|
-
let(:client) { CFoundry::V1::Client.new }
|
5
|
-
|
6
|
-
describe "#version" do
|
7
|
-
its(:version) { should eq 1 }
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "#login" do
|
11
|
-
include_examples "client login"
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "#login_prompts" do
|
15
|
-
include_examples "client login prompts"
|
16
|
-
end
|
17
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe CFoundry::V1::ModelMagic do
|
4
|
-
let(:client) { v1_fake_client }
|
5
|
-
let(:mymodel) { v1_fake_model }
|
6
|
-
let(:guid) { random_string("my-object-guid") }
|
7
|
-
let(:myobject) { mymodel.new(guid, client) }
|
8
|
-
|
9
|
-
describe "#read_manifest" do
|
10
|
-
context "with an attribute with different read/write locations" do
|
11
|
-
let(:mymodel) do
|
12
|
-
v1_fake_model do
|
13
|
-
attribute :foo, :string, :read => :x, :write => [:y, :z]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
before do
|
18
|
-
stub(client.base).my_fake_model { { :x => "abc" } }
|
19
|
-
end
|
20
|
-
|
21
|
-
it "reads from the write location" do
|
22
|
-
expect {
|
23
|
-
myobject.foo = "def"
|
24
|
-
}.to change { myobject.read_manifest[:foo] }.from("abc").to("def")
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "#write_manifest" do
|
30
|
-
context "with a read-only attribute" do
|
31
|
-
let(:mymodel) do
|
32
|
-
v1_fake_model do
|
33
|
-
attribute :foo, :string, :read_only => true
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it "does not include the attribute" do
|
38
|
-
myobject.fake(:foo => "bar")
|
39
|
-
expect(myobject.write_manifest).to_not include :foo
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,144 +0,0 @@
|
|
1
|
-
module V1Fake
|
2
|
-
module FakeMethods
|
3
|
-
def v1_fake_client(attributes = {})
|
4
|
-
CFoundry::V1::FakeClient.new.fake(attributes)
|
5
|
-
end
|
6
|
-
|
7
|
-
def v1_fake(what, attributes = {})
|
8
|
-
v1_fake_client.send(what).fake(attributes)
|
9
|
-
end
|
10
|
-
|
11
|
-
def v1_fake_list(what, count, attributes = {})
|
12
|
-
objs = []
|
13
|
-
|
14
|
-
count.times do
|
15
|
-
objs << fake(what, attributes)
|
16
|
-
end
|
17
|
-
|
18
|
-
objs
|
19
|
-
end
|
20
|
-
|
21
|
-
def v1_fake_model(name = :my_fake_model, &init)
|
22
|
-
# There is a difference between ruby 1.8.7 and 1.8.8 in the order that
|
23
|
-
# the inherited callback gets called. In 1.8.7 the inherited callback
|
24
|
-
# is called after the block; in 1.8.8 and later it's called before.
|
25
|
-
# The upshot for us is we need a failproof way of getting the name
|
26
|
-
# to klass. So we're using a global variable to hand off the value.
|
27
|
-
# Please don't shoot us. - ESH & MMB
|
28
|
-
$object_name = name
|
29
|
-
klass = Class.new(CFoundry::V1::FakeModel) do
|
30
|
-
self.object_name = name
|
31
|
-
end
|
32
|
-
|
33
|
-
klass.class_eval(&init) if init
|
34
|
-
|
35
|
-
klass.define_client_methods
|
36
|
-
|
37
|
-
klass
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def fake(attributes = {})
|
42
|
-
fake_attributes(attributes).each do |k, v|
|
43
|
-
send(:"#{k}=", v)
|
44
|
-
setup_reverse_relationship(v)
|
45
|
-
end
|
46
|
-
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
def setup_reverse_relationship(v)
|
51
|
-
# assign relationship for 'v' pointing back to this object
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
module CFoundry::V1
|
56
|
-
class Model
|
57
|
-
include V1Fake
|
58
|
-
|
59
|
-
attr_writer :client
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def get_many(plural)
|
64
|
-
@cache[plural]
|
65
|
-
end
|
66
|
-
|
67
|
-
def fake_attributes(attributes)
|
68
|
-
default_fakes.merge(attributes)
|
69
|
-
end
|
70
|
-
|
71
|
-
# override this to provide basic attributes (like name) dynamically
|
72
|
-
def default_fakes
|
73
|
-
{}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
class FakeBase < Base
|
79
|
-
end
|
80
|
-
|
81
|
-
|
82
|
-
class FakeClient < Client
|
83
|
-
include V1Fake
|
84
|
-
|
85
|
-
def initialize(target = "http://example.com", token = nil)
|
86
|
-
@base = FakeBase.new(target, token)
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def get_many(plural)
|
92
|
-
instance_variable_get(:"@#{plural}")
|
93
|
-
end
|
94
|
-
|
95
|
-
def fake_attributes(attributes)
|
96
|
-
attributes
|
97
|
-
end
|
98
|
-
|
99
|
-
def setup_reverse_relationship(v)
|
100
|
-
if v.is_a?(Model)
|
101
|
-
v.client = self
|
102
|
-
elsif v.is_a?(Array)
|
103
|
-
v.each do |x|
|
104
|
-
setup_reverse_relationship(x)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
|
111
|
-
class FakeModel < CFoundry::V1::Model
|
112
|
-
attr_reader :diff
|
113
|
-
|
114
|
-
def self.inherited(klass)
|
115
|
-
class << klass
|
116
|
-
attr_writer :object_name
|
117
|
-
end
|
118
|
-
|
119
|
-
# There is a difference between ruby 1.8.7 and 1.8.8 in the order that
|
120
|
-
# the inherited callback gets called. In 1.8.7 the inherited callback
|
121
|
-
# is called after the block; in 1.8.8 and later it's called before.
|
122
|
-
# The upshot for us is we need a failproof way of getting the name
|
123
|
-
# to klass. So we're using a global variable to hand off the value.
|
124
|
-
# Please don't shoot us. - ESH & MMB
|
125
|
-
klass.object_name = $object_name
|
126
|
-
super
|
127
|
-
end
|
128
|
-
|
129
|
-
class << self
|
130
|
-
attr_writer :object_name
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
|
135
|
-
module ModelMagic
|
136
|
-
def self.on_client(&blk)
|
137
|
-
FakeClient.module_eval(&blk)
|
138
|
-
end
|
139
|
-
|
140
|
-
def self.on_base_client(&blk)
|
141
|
-
FakeBase.module_eval(&blk)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|