g5_updatable 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +45 -34
- data/app/concerns/g5_updatable/belongs_to_location.rb +14 -0
- data/app/concerns/g5_updatable/first_class_properties.rb +25 -0
- data/app/concerns/g5_updatable/urn_as_parameter.rb +7 -0
- data/app/controllers/g5_updatable/feed_controller.rb +1 -1
- data/app/models/g5_updatable/client.rb +12 -0
- data/app/models/g5_updatable/integration_setting.rb +20 -0
- data/app/models/g5_updatable/location.rb +16 -0
- data/app/serializers/g5_updatable/location_serializer.rb +12 -0
- data/db/migrate/20140709222005_create_g5_updatable_clients_and_locations.rb +24 -0
- data/db/migrate/20141030211945_create_integration_setting.rb +18 -0
- data/lib/g5_updatable/client_feed_processor.rb +34 -12
- data/lib/g5_updatable/client_updater.rb +7 -16
- data/lib/g5_updatable/engine.rb +2 -13
- data/lib/g5_updatable/integration_settings_updater.rb +21 -0
- data/lib/g5_updatable/locations_updater.rb +14 -21
- data/lib/g5_updatable/rspec/factories.rb +19 -0
- data/lib/g5_updatable/rspec.rb +3 -0
- data/lib/g5_updatable/version.rb +1 -1
- data/lib/g5_updatable.rb +2 -1
- data/lib/generators/g5_updatable/install/USAGE +0 -3
- data/lib/generators/g5_updatable/install/install_generator.rb +0 -6
- data/lib/tasks/g5_updatable_tasks.rake +12 -4
- data/spec/concerns/g5_updatable/belongs_to_location_spec.rb +30 -0
- data/spec/dummy/app/models/favorite_food.rb +3 -0
- data/spec/dummy/config/database.sample.yml +9 -0
- data/spec/dummy/config/database.travis.yml +4 -0
- data/spec/dummy/db/migrate/20140709220627_drop_clients_and_locations.rb +10 -0
- data/spec/dummy/db/migrate/20140714225203_create_favorite_foods.rb +10 -0
- data/spec/dummy/db/schema.rb +39 -21
- data/spec/dummy/log/test.log +18589 -920
- data/spec/lib/g5_updatable/client_feed_processor_spec.rb +77 -22
- data/spec/lib/g5_updatable/client_updater_spec.rb +28 -41
- data/spec/lib/g5_updatable/integration_settings_updater_spec.rb +48 -0
- data/spec/lib/g5_updatable/locations_updater_spec.rb +29 -52
- data/spec/lib/generators/g5_updatable/install_generator_spec.rb +0 -12
- data/spec/models/g5_updatable/client_spec.rb +25 -0
- data/spec/models/g5_updatable/integration_setting_spec.rb +33 -0
- data/spec/models/g5_updatable/location_spec.rb +34 -0
- data/spec/serializers/g5_updatable/location_serializer_spec.rb +21 -0
- data/spec/spec_helper.rb +13 -5
- data/spec/support/shared_example_for_urn_as_parameter.rb +7 -0
- data/spec/support/shared_examples_for_first_class_properties_json.rb +29 -0
- metadata +104 -42
- data/lib/g5_updatable/feed_mapper.rb +0 -58
- data/lib/g5_updatable/g5_client.rb +0 -10
- data/lib/g5_updatable/g5_location.rb +0 -22
- data/lib/generators/g5_updatable/install/templates/g5_updatable.rb +0 -25
- data/spec/dummy/config/database.yml +0 -25
- data/spec/dummy/config/initializers/g5_updatable.rb +0 -4
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -6434
- data/spec/dummy/spec/fabricators/client_fabricator.rb +0 -6
- data/spec/dummy/spec/fabricators/location_fabricator.rb +0 -9
- data/spec/dummy/spec/models/client_spec.rb +0 -0
- data/spec/dummy/spec/models/location_spec.rb +0 -0
- data/spec/dummy/spec/support/client_feed.html +0 -97
- data/spec/dummy/spec/support/updated_client_feed.html +0 -148
- data/spec/fabricators/client_fabricator.rb +0 -6
- data/spec/fabricators/location_fabricator.rb +0 -9
- data/spec/lib/g5_updatable/feed_mapper_spec.rb +0 -119
- data/spec/lib/tmp/config/initializers/g5_updatable.rb +0 -25
@@ -1,38 +1,93 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe G5Updatable::ClientFeedProcessor do
|
4
|
-
|
5
|
-
|
4
|
+
describe ".new" do
|
5
|
+
let(:processor) { described_class.new(passed_uid) }
|
6
|
+
subject(:client_uid) { processor.client_uid }
|
7
|
+
let(:configured_client_uid) { nil }
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
describe "#work" do
|
13
|
-
after { described_class.new(urn).work }
|
14
|
-
let(:urn) { nil }
|
9
|
+
context "when CLIENT_UID is unset" do
|
10
|
+
context "with a passed-in client_uid" do
|
11
|
+
let(:passed_uid) { "passed" }
|
12
|
+
it { should eq("passed") }
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
15
|
+
context "with no passed-in client_uid" do
|
16
|
+
let(:passed_uid) { nil }
|
18
17
|
|
19
|
-
|
20
|
-
|
18
|
+
it "explodes helpfully" do
|
19
|
+
expect { client_uid }.to raise_error(/client_uid/)
|
20
|
+
end
|
21
21
|
end
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
context "when CLIENT_UID is set" do
|
25
|
+
before do
|
26
|
+
@old_client_uid = ENV["CLIENT_UID"]
|
27
|
+
ENV["CLIENT_UID"] = "configured"
|
25
28
|
end
|
26
|
-
|
29
|
+
after { ENV["CLIENT_UID"] = @old_client_uid }
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
context "with a passed-in client_uid" do
|
32
|
+
let(:passed_uid) { "passed" }
|
33
|
+
it { should eq("passed") }
|
31
34
|
end
|
32
35
|
|
33
|
-
|
34
|
-
|
36
|
+
context "with no passed-in client_uid" do
|
37
|
+
let(:passed_uid) { nil }
|
38
|
+
it { should eq("configured") }
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
42
|
+
|
43
|
+
describe "#work" do
|
44
|
+
let(:client_updater) { double(update: nil) }
|
45
|
+
let(:locations_updater) { double(update: nil) }
|
46
|
+
let(:integration_settings_updater) { double(update: nil) }
|
47
|
+
|
48
|
+
let(:client) { G5FoundationClient::Client.new(uid: client_uid,
|
49
|
+
urn: "urn",
|
50
|
+
name: "Client Name",
|
51
|
+
locations: [{uid: location_uid, locations_integration_settings: [{uid: integration_setting_uid, vendor_action: 'inventory'}]}]) }
|
52
|
+
let(:client_uid) { "http://example.com/cilent_uid" }
|
53
|
+
let(:location_uid) { "http://example.com/cilent_uid/locations/location_uid" }
|
54
|
+
let(:integration_setting_uid) { "http://example.com/cilent_uid/locations/location_uid/integration_setting_uid" }
|
55
|
+
|
56
|
+
before do
|
57
|
+
stub_client_for_uid(client_uid, client)
|
58
|
+
allow(G5Updatable::ClientUpdater).to receive(:new) { client_updater }
|
59
|
+
allow(G5Updatable::LocationsUpdater).to receive(:new) { locations_updater }
|
60
|
+
allow(G5Updatable::IntegrationSettingsUpdater).to receive(:new) { integration_settings_updater }
|
61
|
+
G5Updatable::ClientFeedProcessor.new(client_uid).work
|
62
|
+
end
|
63
|
+
|
64
|
+
it "updates with the found client" do
|
65
|
+
expect(G5Updatable::ClientUpdater).to have_received(:new).with(client)
|
66
|
+
expect(client_updater).to have_received(:update)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "updates with the client's associated locations" do
|
70
|
+
expect(G5Updatable::LocationsUpdater).to have_received(:new).with(client.locations)
|
71
|
+
expect(locations_updater).to have_received(:update)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "updates with location's associated integration settings" do
|
75
|
+
expect(G5Updatable::IntegrationSettingsUpdater).to have_received(:new).with(client.locations.first.integration_settings)
|
76
|
+
expect(integration_settings_updater).to have_received(:update)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe :load_all_clients do
|
81
|
+
let(:clients_url) { 'http://g5-hub-clients-url' }
|
82
|
+
let(:client_uids) { %w(uid-1 uid-2) }
|
83
|
+
before do
|
84
|
+
expect(G5FoundationClient::Client).to receive(:all_client_uids).with(clients_url).and_return(client_uids)
|
85
|
+
expect(G5Updatable::ClientFeedProcessor).to receive(:new).with('uid-1').and_return(double(:work1, work: 'work-1'))
|
86
|
+
expect(G5Updatable::ClientFeedProcessor).to receive(:new).with('uid-2').and_return(double(:work1, work: 'work-2'))
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'loads every client' do
|
90
|
+
expect(G5Updatable::ClientFeedProcessor.load_all_clients(clients_url)).to eq(%w(work-1 work-2))
|
91
|
+
end
|
92
|
+
end
|
38
93
|
end
|
@@ -1,58 +1,45 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe G5Updatable::ClientUpdater do
|
4
|
-
let(:
|
5
|
-
let(:
|
6
|
-
|
7
|
-
|
8
|
-
let(:
|
4
|
+
let(:uid) { "http://example.com/uid" }
|
5
|
+
let(:properties) { {uid: uid,
|
6
|
+
urn: "urn",
|
7
|
+
name: "Client Name", } }
|
8
|
+
let(:g5_client) { G5FoundationClient::Client.new(properties) }
|
9
9
|
|
10
|
-
|
11
|
-
allow(G5Updatable).to receive(:feed_endpoint) { feed_endpoint }
|
12
|
-
allow(G5Updatable).to receive(:client_identifier) { client_identifier }
|
13
|
-
end
|
10
|
+
let(:updater) { described_class.new(g5_client) }
|
14
11
|
|
15
12
|
describe "#update" do
|
16
|
-
|
17
|
-
Fabricate(:client, uid: client_uid, name: "Foo", vertical: "Self-Storage")
|
18
|
-
end
|
13
|
+
subject { G5Updatable::Client.first }
|
19
14
|
|
20
|
-
context "
|
21
|
-
|
22
|
-
expect(client).to_not receive(:save)
|
23
|
-
updater.update
|
24
|
-
end
|
25
|
-
end
|
15
|
+
context "with no existing Client records" do
|
16
|
+
before { updater.update }
|
26
17
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
context "default parameters" do
|
31
|
-
it "updates the name attribute" do
|
32
|
-
expect { updater.update }.to change { client.reload.name }.
|
33
|
-
from("Foo").to("Farmhouse")
|
34
|
-
end
|
18
|
+
it "creates a Client" do
|
19
|
+
expect(G5Updatable::Client.count).to eq(1)
|
20
|
+
end
|
35
21
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
22
|
+
it 'does not redact keys in properties' do
|
23
|
+
expect(subject.properties.keys.collect(&:to_sym)).to eq(properties.keys)
|
39
24
|
end
|
40
25
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
26
|
+
its(:uid) { should eq(uid) }
|
27
|
+
its(:urn) { should eq("urn") }
|
28
|
+
its(:name) { should eq("Client Name") }
|
29
|
+
end
|
45
30
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
31
|
+
context "with an existing Client record" do
|
32
|
+
before do
|
33
|
+
FactoryGirl.create(:client, uid: uid, urn: "old")
|
34
|
+
updater.update
|
35
|
+
end
|
50
36
|
|
51
|
-
|
52
|
-
|
53
|
-
from("Self-Storage").to("Apartments")
|
54
|
-
end
|
37
|
+
it "does not create a new Client" do
|
38
|
+
expect(G5Updatable::Client.count).to eq(1)
|
55
39
|
end
|
40
|
+
|
41
|
+
its(:urn) { should eq("urn") }
|
42
|
+
its(:name) { should eq("Client Name") }
|
56
43
|
end
|
57
44
|
end
|
58
45
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe G5Updatable::IntegrationSettingsUpdater do
|
4
|
+
let(:uid) { "http://example.com/uid" }
|
5
|
+
let(:name) { "integration setting name" }
|
6
|
+
let(:properties) { {uid: uid,
|
7
|
+
urn: "urn",
|
8
|
+
vendor_action: "inventory",
|
9
|
+
location_uid: "location_uid",
|
10
|
+
job_frequency_in_minutes: 22,
|
11
|
+
name: name} }
|
12
|
+
let(:integration_setting) { G5FoundationClient::IntegrationSetting.new(properties) }
|
13
|
+
|
14
|
+
let(:updater) { described_class.new([integration_setting]) }
|
15
|
+
|
16
|
+
describe "#update" do
|
17
|
+
subject { G5Updatable::IntegrationSetting.first }
|
18
|
+
|
19
|
+
context "with no existing integration-setting records" do
|
20
|
+
before { updater.update }
|
21
|
+
|
22
|
+
it "creates a Location" do
|
23
|
+
expect(G5Updatable::IntegrationSetting.count).to eq(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
its(:job_frequency_in_minutes) { should eq(22) }
|
27
|
+
its(:uid) { should eq(uid) }
|
28
|
+
its(:urn) { should eq("urn") }
|
29
|
+
its(:location_uid) { should eq("location_uid") }
|
30
|
+
its(:name) { should eq(name) }
|
31
|
+
its(:vendor_action) { should eq("inventory") }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with an existing integration-setting record" do
|
35
|
+
before do
|
36
|
+
create(:integration_setting, uid: uid, urn: "old")
|
37
|
+
updater.update
|
38
|
+
end
|
39
|
+
|
40
|
+
it "does not create a new IntegrationSetting" do
|
41
|
+
expect(G5Updatable::IntegrationSetting.count).to eq(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
its(:urn) { should eq("urn") }
|
45
|
+
its(:name) { should eq(name) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,71 +1,48 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe G5Updatable::LocationsUpdater do
|
4
|
-
let(:
|
5
|
-
let(:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
let(:
|
4
|
+
let(:uid) { "http://example.com/uid" }
|
5
|
+
let(:properties) { {uid: uid,
|
6
|
+
urn: "urn",
|
7
|
+
client_uid: "client_uid",
|
8
|
+
name: "Location Name"} }
|
9
|
+
let(:g5_location) { G5FoundationClient::Location.new(properties) }
|
10
10
|
|
11
|
-
|
12
|
-
allow(G5Updatable).to receive(:feed_endpoint) { feed_endpoint }
|
13
|
-
allow(G5Updatable).to receive(:client_identifier) { client_identifier }
|
14
|
-
end
|
11
|
+
let(:updater) { described_class.new([g5_location]) }
|
15
12
|
|
16
13
|
describe "#update" do
|
17
|
-
|
18
|
-
Fabricate(:location, urn: urn, name: "Foo", neighborhood: "Eastside")
|
19
|
-
end
|
20
|
-
|
21
|
-
context "update locations disabled" do
|
22
|
-
before { allow(G5Updatable).to receive(:update_locations) { false } }
|
14
|
+
subject { G5Updatable::Location.first }
|
23
15
|
|
24
|
-
|
25
|
-
|
26
|
-
updater.update
|
27
|
-
end
|
16
|
+
context "with no existing Location records" do
|
17
|
+
before { updater.update }
|
28
18
|
|
29
|
-
it "creates
|
30
|
-
expect
|
19
|
+
it "creates a Location" do
|
20
|
+
expect(G5Updatable::Location.count).to eq(1)
|
31
21
|
end
|
32
|
-
end
|
33
22
|
|
34
|
-
context "update locations enabled" do
|
35
|
-
context "default parameters" do
|
36
|
-
it "updates the name attribute" do
|
37
|
-
expect { updater.update }.to change { location.reload.name }.
|
38
|
-
from("Foo").to("Hollywood")
|
39
|
-
end
|
40
23
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
it "creates new locations" do
|
46
|
-
expect { updater.update }.to change { Location.all.size }.from(1).to(2)
|
47
|
-
end
|
24
|
+
it 'does not redact keys in properties' do
|
25
|
+
expect(subject.properties.keys.collect(&:to_sym)).to eq(properties.keys)
|
48
26
|
end
|
49
27
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
it "updates the name attribute" do
|
56
|
-
expect { updater.update }.to change { location.reload.name }.
|
57
|
-
from("Foo").to("Hollywood")
|
58
|
-
end
|
28
|
+
its(:uid) { should eq(uid) }
|
29
|
+
its(:urn) { should eq("urn") }
|
30
|
+
its(:client_uid) { should eq("client_uid") }
|
31
|
+
its(:name) { should eq("Location Name") }
|
32
|
+
end
|
59
33
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
34
|
+
context "with an existing Location record" do
|
35
|
+
before do
|
36
|
+
FactoryGirl.create(:location, uid: uid, urn: "old")
|
37
|
+
updater.update
|
38
|
+
end
|
64
39
|
|
65
|
-
|
66
|
-
|
67
|
-
end
|
40
|
+
it "does not create a new Location" do
|
41
|
+
expect(G5Updatable::Location.count).to eq(1)
|
68
42
|
end
|
43
|
+
|
44
|
+
its(:urn) { should eq("urn") }
|
45
|
+
its(:name) { should eq("Location Name") }
|
69
46
|
end
|
70
47
|
end
|
71
48
|
end
|
@@ -11,18 +11,6 @@ describe G5Updatable::InstallGenerator, type: :generator do
|
|
11
11
|
run_generator
|
12
12
|
end
|
13
13
|
|
14
|
-
it "should copy the g5_updatable initializer" do
|
15
|
-
expect(destination_root).to have_structure {
|
16
|
-
directory "config" do
|
17
|
-
directory "initializers" do
|
18
|
-
file "g5_updatable.rb" do
|
19
|
-
contains "G5Updatable.setup do |config|"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
14
|
it "should mount the engine" do
|
27
15
|
expect(destination_root).to have_structure {
|
28
16
|
directory "config" do
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5Updatable::Client do
|
4
|
+
describe "validations" do
|
5
|
+
subject(:client) { G5Updatable::Client.new }
|
6
|
+
|
7
|
+
it { expect(client).to validate_presence_of(:uid) }
|
8
|
+
it { expect(client).to validate_presence_of(:urn) }
|
9
|
+
end
|
10
|
+
|
11
|
+
it_behaves_like "a model with first-class properties" do
|
12
|
+
let(:instance_factory_name) { :client }
|
13
|
+
end
|
14
|
+
|
15
|
+
it_behaves_like "a model that uses its URN as its parameter" do
|
16
|
+
let(:instance_factory_name) { :client }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe :locations do
|
20
|
+
let!(:location) { create(:location, client_uid: subject.uid) }
|
21
|
+
subject { create(:client) }
|
22
|
+
|
23
|
+
its(:locations) { is_expected.to eq([location]) }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5Updatable::IntegrationSetting do
|
4
|
+
describe 'validations' do
|
5
|
+
subject(:integration_setting) { G5Updatable::IntegrationSetting.new }
|
6
|
+
it { expect(integration_setting).to validate_presence_of(:location_uid) }
|
7
|
+
it { expect(integration_setting).to validate_presence_of(:uid) }
|
8
|
+
end
|
9
|
+
|
10
|
+
it_behaves_like 'a model with first-class properties' do
|
11
|
+
let(:instance_factory_name) { :integration_setting }
|
12
|
+
end
|
13
|
+
|
14
|
+
it_behaves_like 'a model that uses its URN as its parameter' do
|
15
|
+
let(:instance_factory_name) { :client }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#location" do
|
19
|
+
let(:integration_setting) { create(:integration_setting, location_uid: location.uid) }
|
20
|
+
let(:location) { create(:location) }
|
21
|
+
subject { integration_setting.location }
|
22
|
+
|
23
|
+
it { should eq(location) }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe :by_vendor_action do
|
27
|
+
let!(:inventory) { create(:integration_setting, vendor_action: G5Updatable::IntegrationSetting::INVENTORY) }
|
28
|
+
let!(:lead) { create(:integration_setting, vendor_action: G5Updatable::IntegrationSetting::LEAD) }
|
29
|
+
|
30
|
+
it { expect(G5Updatable::IntegrationSetting.by_lead).to eq([lead]) }
|
31
|
+
it { expect(G5Updatable::IntegrationSetting.by_inventory).to eq([inventory]) }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5Updatable::Location do
|
4
|
+
describe "validations" do
|
5
|
+
subject(:location) { G5Updatable::Location.new }
|
6
|
+
|
7
|
+
it { expect(location).to validate_presence_of(:uid) }
|
8
|
+
it { expect(location).to validate_presence_of(:urn) }
|
9
|
+
it { expect(location).to validate_presence_of(:client_uid) }
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like "a model with first-class properties" do
|
13
|
+
let(:instance_factory_name) { :location }
|
14
|
+
end
|
15
|
+
|
16
|
+
it_behaves_like "a model that uses its URN as its parameter" do
|
17
|
+
let(:instance_factory_name) { :location }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#client" do
|
21
|
+
let(:client) { FactoryGirl.create(:client) }
|
22
|
+
let(:location) { FactoryGirl.create(:location, client_uid: client.uid) }
|
23
|
+
subject { location.client }
|
24
|
+
|
25
|
+
it { should eq(client) }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe :integration_settings do
|
29
|
+
let!(:integration_setting) { create(:integration_setting, location_uid: subject.uid) }
|
30
|
+
subject { create(:location) }
|
31
|
+
|
32
|
+
its(:integration_settings) { is_expected.to eq([integration_setting]) }
|
33
|
+
end
|
34
|
+
end
|