pra 1.6.0 → 1.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.
@@ -1,136 +0,0 @@
1
- require_relative '../../../lib/pra/curses_pull_request_presenter'
2
-
3
- describe Pra::CursesPullRequestPresenter do
4
- describe '.new' do
5
- it 'construct given pull request' do
6
- pull_request = double('pull request')
7
- Pra::CursesPullRequestPresenter.new(pull_request)
8
- end
9
-
10
- it 'assigns pull request to an instance variable' do
11
- pull_request = double('pull request')
12
- curses_pull_request = Pra::CursesPullRequestPresenter.new(pull_request)
13
- expect(curses_pull_request.instance_variable_get(:@pull_request)).to eq pull_request
14
- end
15
- end
16
-
17
- describe '#force_length' do
18
- it 'right pads the given string up to the specified length' do
19
- pull_request = double
20
- curses_pull_request = Pra::CursesPullRequestPresenter.new(pull_request)
21
- expect(curses_pull_request.send(:force_length, 'capture_api', 15)).to eq 'capture_api '
22
- end
23
-
24
- it 'truncates the given string down to the specified length' do
25
- pull_request = double
26
- curses_pull_request = Pra::CursesPullRequestPresenter.new(pull_request)
27
- expect(curses_pull_request.send(:force_length, 'capture_api_012345678912345', 15)).to eq 'capture_api_012'
28
- end
29
- end
30
-
31
- describe '#repository' do
32
- it 'forces the repository length to 15' do
33
- repository = double
34
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', repository: repository))
35
- expect(curses_pull_request).to receive(:force_length).with(repository, 15)
36
- curses_pull_request.repository
37
- end
38
- end
39
-
40
- describe '#title' do
41
- it 'forces the title length to 20' do
42
- title = double
43
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', title: title))
44
- expect(curses_pull_request).to receive(:force_length).with(title, 20)
45
- curses_pull_request.title
46
- end
47
- end
48
-
49
- describe '#from_reference' do
50
- it 'forces the from_reference length to 20' do
51
- from_reference = double
52
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', from_reference: from_reference))
53
- expect(curses_pull_request).to receive(:force_length).with(from_reference, 20)
54
- curses_pull_request.from_reference
55
- end
56
- end
57
-
58
- describe '#to_reference' do
59
- it 'forces the to_reference length to 20' do
60
- to_reference = double
61
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', to_reference: to_reference))
62
- expect(curses_pull_request).to receive(:force_length).with(to_reference, 20)
63
- curses_pull_request.to_reference
64
- end
65
- end
66
-
67
- describe '#author' do
68
- it 'forces the author length to 20' do
69
- author = double
70
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', author: author))
71
- expect(curses_pull_request).to receive(:force_length).with(author, 20)
72
- curses_pull_request.author
73
- end
74
- end
75
-
76
- describe '#assignee' do
77
- context 'when assignee is nil' do
78
- it 'returns an empty string with length of 20' do
79
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull_request', assignee: nil))
80
- expect(curses_pull_request.assignee).to eq(' '*20)
81
- end
82
- end
83
-
84
- context 'when assignee is NOT nil' do
85
- context 'when assignee is blacklisted' do
86
- it 'returns an empty string with length of 20' do
87
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull_request', assignee: nil))
88
- expect(curses_pull_request.assignee).to eq(' '*20)
89
- end
90
- end
91
- end
92
-
93
- context 'when assignee is NOT blacklisted' do
94
- it 'returns assignee with a length of 20' do
95
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull_request', assignee: 'IPT-Capture'))
96
- allow(curses_pull_request).to receive(:assignee_blacklist).and_return(['IPT'])
97
- expect(curses_pull_request.assignee).to eq('IPT-Capture ')
98
- end
99
- end
100
-
101
- context 'when assignee IS blacklisted' do
102
- it 'returns an empty string with length of 20' do
103
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull_request', assignee: 'IPT-Capture'))
104
- allow(curses_pull_request).to receive(:assignee_blacklist).and_return(['IPT-Capture'])
105
- expect(curses_pull_request.assignee).to eq(' '*20)
106
- end
107
- end
108
- end
109
-
110
- describe '#service_id' do
111
- it 'forces the service_id length to 20' do
112
- service_id = double
113
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', service_id: service_id))
114
- expect(curses_pull_request).to receive(:force_length).with(service_id, 10)
115
- curses_pull_request.service_id
116
- end
117
- end
118
-
119
- describe '#blacklisted?' do
120
- context 'when assignee IS blacklisted' do
121
- it 'returns true' do
122
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', assignee: 'IPT-Capture'))
123
- allow(curses_pull_request).to receive(:assignee_blacklist).and_return(['IPT-Capture'])
124
- expect(curses_pull_request.send(:blacklisted?, 'IPT-Capture')).to eq true
125
- end
126
- end
127
-
128
- context 'when assignee IS not blacklisted' do
129
- it 'returns false' do
130
- curses_pull_request = Pra::CursesPullRequestPresenter.new(double('pull request', assignee: 'IPT-Capture'))
131
- allow(curses_pull_request).to receive(:assignee_blacklist).and_return(['IPT'])
132
- expect(curses_pull_request.send(:blacklisted?, 'IPT-Capture')).to eq false
133
- end
134
- end
135
- end
136
- end
@@ -1,4 +0,0 @@
1
- require_relative "../../../lib/pra/curses_window_system"
2
-
3
- describe Pra::CursesWindowSystem do
4
- end
@@ -1,30 +0,0 @@
1
- require 'pra/error_log'
2
-
3
- describe Pra::ErrorLog do
4
- describe '.log' do
5
- let(:log_path) { double('log path') }
6
- let(:file) { double('file object', puts: nil) }
7
- let(:message) { double('error message') }
8
- let(:backtrace) { ['backtrace line 1', 'backtrace line 2'] }
9
- let(:error) { double('error', message: message, backtrace: backtrace) }
10
-
11
- it 'opens the log file for appending' do
12
- allow(Pra::Config).to receive(:error_log_path).and_return(log_path)
13
- expect(File).to receive(:open).with(log_path, 'a')
14
- Pra::ErrorLog.log(double)
15
- end
16
-
17
- it 'prints the error message to the file' do
18
- allow(File).to receive(:open).and_yield(file)
19
- expect(file).to receive(:puts).with(message)
20
- Pra::ErrorLog.log(error)
21
- end
22
-
23
- it 'prints the backtrace to the file' do
24
- allow(File).to receive(:open).and_yield(file)
25
- expect(file).to receive(:puts).with('backtrace line 1')
26
- expect(file).to receive(:puts).with('backtrace line 2')
27
- Pra::ErrorLog.log(error)
28
- end
29
- end
30
- end
@@ -1,140 +0,0 @@
1
- require_relative "../../../lib/pra/github_pull_source"
2
-
3
- describe Pra::GithubPullSource do
4
- describe "#pull_requests" do
5
- it "gets all the repositories" do
6
- subject.should_receive(:repositories).and_return([])
7
- subject.pull_requests
8
- end
9
-
10
- it "gets the pull requests for each repository" do
11
- config = {
12
- "protocol" => "https",
13
- "host" => "my.github.instance",
14
- "username" => "foo",
15
- "password" => "bar",
16
- "repositories" => [
17
- { "owner" => "reachlocal", "repository" => "snapdragon" }
18
- ]
19
- }
20
- pull_source = Pra::GithubPullSource.new(config)
21
- pull_source.should_receive(:get_repo_pull_requests).with({ "owner" => "reachlocal", "repository" => "snapdragon" }).and_return([])
22
- pull_source.pull_requests
23
- end
24
-
25
- it "returns the collection of all of the pull requests for the configured repos" do
26
- config = {
27
- "protocol" => "https",
28
- "host" => "my.github.instance",
29
- "username" => "foo",
30
- "password" => "bar",
31
- "repositories" => [
32
- { "owner" => "reachlocal", "repository" => "snapdragon" },
33
- { "owner" => "realpractice", "repository" => "rliapi" }
34
- ]
35
- }
36
- pull_request_one = double('pull request one')
37
- pull_request_two = double('pull request two')
38
- pull_source = Pra::GithubPullSource.new(config)
39
- pull_source.stub(:get_repo_pull_requests).with({ "owner" => "reachlocal", "repository" => "snapdragon" }).and_return([pull_request_one])
40
- pull_source.stub(:get_repo_pull_requests).with({ "owner" => "realpractice", "repository" => "rliapi" }).and_return([pull_request_two])
41
- pull_source.pull_requests.should eq([pull_request_one, pull_request_two])
42
- end
43
- end
44
-
45
- describe "#repositories" do
46
- it "returns the repositories segment of the config" do
47
- config = {
48
- "protocol" => "https",
49
- "host" => "my.github.instance",
50
- "username" => "foo",
51
- "password" => "bar",
52
- "repositories" => [
53
- { "owner" => "reachlocal", "repository" => "snapdragon" }
54
- ]
55
- }
56
- pull_source = Pra::GithubPullSource.new(config)
57
- pull_source.repositories.should eq([{ "owner" => "reachlocal", "repository" => "snapdragon" }])
58
- end
59
- end
60
-
61
- describe "#get_repo_pull_requests" do
62
- it "requests the pull requests for the given repo" do
63
- config = {
64
- "protocol" => "https",
65
- "host" => "my.github.instance",
66
- "username" => "foo",
67
- "password" => "bar",
68
- "repositories" => [
69
- { "owner" => "reachlocal", "repository" => "snapdragon" }
70
- ]
71
- }
72
- pull_source = Pra::GithubPullSource.new(config)
73
- pull_source.stub(:rest_api_pull_request_resource).with({ "owner" => "reachlocal", "repository" => "snapdragon" }).and_return('[]')
74
- pull_source.get_repo_pull_requests({ "owner" => "reachlocal", "repository" => "snapdragon" })
75
- end
76
- end
77
-
78
- describe "#rest_api_pull_request_url" do
79
- let(:config) do
80
- {
81
- "protocol" => "https",
82
- "host" => "my.github.instance",
83
- "username" => "foo",
84
- "password" => "bar",
85
- "repositories" => [
86
- { "owner" => "reachlocal", "repository" => "snapdragon" }
87
- ]
88
- }
89
- end
90
-
91
- it "returns the pull request url compiled from the config options" do
92
- pull_source = Pra::GithubPullSource.new(config)
93
- pull_source.rest_api_pull_request_url({ "owner" => "reachlocal", "repository" => "snapdragon" }).should eq("https://my.github.instance/repos/reachlocal/snapdragon/pulls")
94
- end
95
- end
96
-
97
- describe "#rest_api_pull_request_resource" do
98
- let(:config) do
99
- {
100
- "protocol" => "https",
101
- "host" => "my.github.instance",
102
- "username" => "foo",
103
- "password" => "bar",
104
- "repositories" => [
105
- { "owner" => "reachlocal", "repository" => "snapdragon" }
106
- ]
107
- }
108
- end
109
-
110
- let(:repo_config) { {"owner" => "reachlocal", "repository" => "snapdragon"} }
111
-
112
- subject { Pra::GithubPullSource.new(config) }
113
-
114
- it "creates a Faraday connection" do
115
- expect(Faraday).to receive(:new).and_return(double.as_null_object)
116
- subject.rest_api_pull_request_resource(repo_config)
117
- end
118
-
119
- it "set the http basic auth credentials" do
120
- conn = double('faraday connection').as_null_object
121
- allow(Faraday).to receive(:new).and_return(conn)
122
- expect(conn).to receive(:basic_auth).with("foo", "bar")
123
- subject.rest_api_pull_request_resource(repo_config)
124
- end
125
-
126
- it "makes request using faraday connection" do
127
- conn = double('faraday connection').as_null_object
128
- allow(Faraday).to receive(:new).and_return(conn)
129
- expect(conn).to receive(:get)
130
- subject.rest_api_pull_request_resource(repo_config)
131
- end
132
-
133
- it "returns the responses body" do
134
- conn = double('faraday connection').as_null_object
135
- allow(Faraday).to receive(:new).and_return(conn)
136
- expect(conn).to receive(:get).and_return(double('response', body: 'hoopytbody'))
137
- expect(subject.rest_api_pull_request_resource(repo_config)).to eq('hoopytbody')
138
- end
139
- end
140
- end
@@ -1,127 +0,0 @@
1
- require 'pra/pull_request_service/fetch_status'
2
-
3
- describe Pra::PullRequestService::FetchStatus do
4
- describe '.new' do
5
- it 'assigns the status' do
6
- status = double
7
- fetch = Pra::PullRequestService::FetchStatus.new(status, double)
8
- expect(fetch.status).to eq(status)
9
- end
10
-
11
- it 'assigns the pull requests' do
12
- pulls = [double]
13
- fetch = Pra::PullRequestService::FetchStatus.new(double, pulls)
14
- expect(fetch.pull_requests).to eq(pulls)
15
- end
16
-
17
- it 'assigns the error' do
18
- error = double
19
- fetch = Pra::PullRequestService::FetchStatus.new(double, double, error)
20
- expect(fetch.error).to eq(error)
21
- end
22
- end
23
-
24
- describe '.success' do
25
- let(:pulls) { double }
26
- subject { Pra::PullRequestService::FetchStatus.success(pulls) }
27
-
28
- it 'sets the status to success' do
29
- expect(subject.status).to eq(:success)
30
- end
31
-
32
- it 'assigns the pull requests' do
33
- expect(subject.pull_requests).to eq(pulls)
34
- end
35
- end
36
-
37
- describe '.error' do
38
- let(:error) { double }
39
- subject { Pra::PullRequestService::FetchStatus.error(error) }
40
-
41
- it 'sets the status to error' do
42
- expect(subject.status).to eq(:error)
43
- end
44
-
45
- it 'sets the pull requests to a dummy value' do
46
- expect(subject.pull_requests).to eq(:no_pull_requests)
47
- end
48
- end
49
-
50
- describe '#success?' do
51
- context 'when status is :success' do
52
- subject { Pra::PullRequestService::FetchStatus.new(:success, double) }
53
-
54
- it 'returns true' do
55
- expect(subject.success?).to be_truthy
56
- end
57
- end
58
-
59
- context 'when status is not :success' do
60
- subject { Pra::PullRequestService::FetchStatus.new(double, double) }
61
-
62
- it 'returns false' do
63
- expect(subject.success?).to be_falsey
64
- end
65
- end
66
- end
67
-
68
- describe '#error?' do
69
- context 'when status is :error' do
70
- subject { Pra::PullRequestService::FetchStatus.new(:error, double) }
71
-
72
- it 'returns true' do
73
- expect(subject.error?).to be_truthy
74
- end
75
- end
76
-
77
- context 'when status is not :error' do
78
- subject { Pra::PullRequestService::FetchStatus.new(double, double) }
79
-
80
- it 'returns false' do
81
- expect(subject.error?).to be_falsey
82
- end
83
- end
84
- end
85
-
86
- describe '#on_success' do
87
- let(:pulls) { double }
88
- let(:error) { double }
89
-
90
- context 'when status is success' do
91
- subject { Pra::PullRequestService::FetchStatus.success(pulls) }
92
-
93
- it 'yields the pull requests to the block' do
94
- expect { |success_block| subject.on_success(&success_block) }.to yield_with_args(pulls)
95
- end
96
- end
97
-
98
- context 'when status is not success' do
99
- subject { Pra::PullRequestService::FetchStatus.error(double) }
100
-
101
- it 'does not yield' do
102
- expect {|success_block| subject.on_success(&success_block) }.not_to yield_control
103
- end
104
- end
105
- end
106
-
107
- describe '#on_error' do
108
- let(:pulls) { double }
109
- let(:error) { double }
110
-
111
- context 'when status is error' do
112
- subject { Pra::PullRequestService::FetchStatus.error(error) }
113
-
114
- it 'yields the error to the block' do
115
- expect { |error_block| subject.on_error(&error_block) }.to yield_with_args(error)
116
- end
117
- end
118
-
119
- context 'when status is not error' do
120
- subject { Pra::PullRequestService::FetchStatus.success(double) }
121
-
122
- it 'does not yield' do
123
- expect {|error_block| subject.on_error(&error_block) }.not_to yield_control
124
- end
125
- end
126
- end
127
- end
@@ -1,118 +0,0 @@
1
- require_relative '../../../lib/pra/pull_request_service'
2
-
3
- describe Pra::PullRequestService do
4
- describe ".fetch_pull_requests" do
5
- let(:pull_request_one) { double('pull request one') }
6
- let(:pull_request_two) { double('pull request two') }
7
- let(:pull_source_one) { double('good pull source one', :pull_requests => [pull_request_one]) }
8
- let(:pull_source_two) { double('good pull source two', :pull_requests => [pull_request_two]) }
9
-
10
- it "gets all the pull-request sources" do
11
- subject.should_receive(:pull_sources).and_return([])
12
- subject.fetch_pull_requests
13
- end
14
-
15
- it "gets the pull requests from each pull-request source" do
16
- allow(subject).to receive(:pull_sources).and_return([pull_source_one, pull_source_two])
17
- expect(subject).to receive(:fetch_with_status).with(pull_source_one)
18
- expect(subject).to receive(:fetch_with_status).with(pull_source_two)
19
- subject.fetch_pull_requests {}
20
- end
21
-
22
- it "yields each pull source with its fetch status object" do
23
- status1 = double
24
- status2 = double
25
- allow(subject).to receive(:pull_sources).and_return([pull_source_one, pull_source_two])
26
- allow(subject).to receive(:fetch_with_status).with(pull_source_one).and_return(status1)
27
- allow(subject).to receive(:fetch_with_status).with(pull_source_two).and_return(status2)
28
- expect { |b| subject.fetch_pull_requests(&b) }.to yield_successive_args(status1, status2)
29
- end
30
- end
31
-
32
- describe '.fetch_with_status' do
33
- let(:pulls) { double('pull requests') }
34
- let(:error) { Exception.new('error fetching pull requests') }
35
- let(:good_source) { double('good pull source', :pull_requests => pulls) }
36
- let(:bad_source) { double('bad pull source') }
37
-
38
- before do
39
- allow(bad_source).to receive(:pull_requests).and_raise(error)
40
- end
41
-
42
- context 'when it fetches successfully' do
43
- it 'builds a success status object for the requests from each pull source' do
44
- expect(Pra::PullRequestService::FetchStatus).to receive(:success).with(pulls)
45
- subject.fetch_with_status(good_source)
46
- end
47
-
48
- it 'returns the status object' do
49
- status = double('success status object')
50
- allow(Pra::PullRequestService::FetchStatus).to receive(:success).with(pulls).and_return(status)
51
- expect(subject.fetch_with_status(good_source)).to eq(status)
52
- end
53
- end
54
-
55
- context 'when fetching raises an exception' do
56
- it 'builds an error status object with the error' do
57
- expect(Pra::PullRequestService::FetchStatus).to receive(:error).with(error)
58
- subject.fetch_with_status(bad_source)
59
- end
60
-
61
- it 'returns the status object' do
62
- status = double('error status object')
63
- allow(Pra::PullRequestService::FetchStatus).to receive(:error).and_return(status)
64
- expect(subject.fetch_with_status(bad_source)).to eq(status)
65
- end
66
- end
67
- end
68
-
69
- describe "#pull_sources" do
70
- it "gets the users config" do
71
- subject.stub(:map_config_to_pull_sources)
72
- Pra::Config.should_receive(:load_config)
73
- subject.pull_sources
74
- end
75
-
76
- it "maps the pull-request sources from the config to PullSource objects" do
77
- config = double('users config')
78
- Pra::Config.stub(:load_config).and_return(config)
79
- subject.should_receive(:map_config_to_pull_sources).with(config)
80
- subject.pull_sources
81
- end
82
-
83
- it "returns the mapped pull-request sources" do
84
- Pra::Config.stub(:load_config)
85
- sources = double('pull sources')
86
- subject.stub(:map_config_to_pull_sources).and_return(sources)
87
- subject.pull_sources.should eq(sources)
88
- end
89
- end
90
-
91
- describe "#map_config_to_pull_sources" do
92
- it "gets the pull sources from the config" do
93
- config = double('config')
94
- config.should_receive(:pull_sources).and_return([])
95
- subject.map_config_to_pull_sources(config)
96
- end
97
-
98
- it "creates a PullSource based object for each configured pull source" do
99
- pull_source_config_one = double('pull source config one')
100
- pull_source_config_two = double('pull source config two')
101
- config = double('config', pull_sources: [pull_source_config_one, pull_source_config_two])
102
- Pra::PullSourceFactory.should_receive(:build_pull_source).with(pull_source_config_one)
103
- Pra::PullSourceFactory.should_receive(:build_pull_source).with(pull_source_config_two)
104
- subject.map_config_to_pull_sources(config)
105
- end
106
-
107
- it "returns an array of the constructed PullSource based objects" do
108
- pull_source_one = double('pull source one')
109
- pull_source_two = double('pull source two')
110
- pull_source_config_one = double('pull source config one')
111
- pull_source_config_two = double('pull source config two')
112
- config = double('config', pull_sources: [pull_source_config_one, pull_source_config_two])
113
- Pra::PullSourceFactory.stub(:build_pull_source).with(pull_source_config_one).and_return(pull_source_one)
114
- Pra::PullSourceFactory.stub(:build_pull_source).with(pull_source_config_two).and_return(pull_source_two)
115
- subject.map_config_to_pull_sources(config).should eq([pull_source_one, pull_source_two])
116
- end
117
- end
118
- end
@@ -1,4 +0,0 @@
1
- require_relative "../../../lib/pra/pull_request"
2
-
3
- describe Pra::PullRequest do
4
- end
@@ -1,40 +0,0 @@
1
- require_relative "../../../lib/pra/pull_source_factory"
2
-
3
- describe Pra::PullSourceFactory do
4
- describe ".build_pull_source" do
5
- it "maps the pull source type to class" do
6
- pull_source_type_specific_configs = double('pull source type specific configs')
7
- pull_source_config = { "type" => "stash", "config" => pull_source_type_specific_configs }
8
- subject.should_receive(:map_type_to_klass).with("stash").and_return(Pra::StashPullSource)
9
- subject.build_pull_source(pull_source_config)
10
- end
11
-
12
- it "constructs the mapped PullSource based object using the given pull source type specific config" do
13
- pull_source_type_specific_configs = double('pull source type specific configs')
14
- pull_source_config = { "type" => "stash", "config" => pull_source_type_specific_configs }
15
- Pra::StashPullSource.should_receive(:new).with(pull_source_type_specific_configs)
16
- subject.build_pull_source(pull_source_config)
17
- end
18
-
19
- it "returns the instance of the previously constructed PullSource based object" do
20
- pull_source = double('the constructed pull source')
21
- pull_source_config = { "type" => "stash", "config" => {} }
22
- Pra::StashPullSource.should_receive(:new).and_return(pull_source)
23
- subject.build_pull_source(pull_source_config).should eq(pull_source)
24
- end
25
- end
26
-
27
- describe ".map_type_to_klass" do
28
- context "when given type is 'stash'" do
29
- it "returns the StashPullSource class" do
30
- subject.map_type_to_klass("stash").should eq(Pra::StashPullSource)
31
- end
32
- end
33
-
34
- context "When given type is 'github'" do
35
- it "returns the GithubPullSource class" do
36
- subject.map_type_to_klass("github").should eq(Pra::GithubPullSource)
37
- end
38
- end
39
- end
40
- end
@@ -1,17 +0,0 @@
1
- require_relative "../../../lib/pra/pull_source"
2
-
3
- describe Pra::PullSource do
4
- describe "#initialize" do
5
- it "assigns the given config hash to an instance variable" do
6
- config = double('config hash')
7
- pull_source = Pra::PullSource.new(config)
8
- pull_source.instance_variable_get(:@config).should eq(config)
9
- end
10
- end
11
-
12
- describe "#pull_requests" do
13
- it "raises an exception forcing inheriting classes to implement this method" do
14
- expect { subject.pull_requests }.to raise_error(Pra::PullSource::NotImplemented)
15
- end
16
- end
17
- end