punchblock 2.5.2 → 2.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.markdown +2 -1
  5. data/lib/punchblock/translator/asterisk/call.rb +3 -3
  6. data/lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb +15 -9
  7. data/lib/punchblock/translator/asterisk/component/output.rb +8 -1
  8. data/lib/punchblock/translator/input_component.rb +2 -0
  9. data/lib/punchblock/version.rb +1 -1
  10. data/punchblock.gemspec +1 -1
  11. data/spec/punchblock/client/component_registry_spec.rb +3 -3
  12. data/spec/punchblock/client_spec.rb +22 -15
  13. data/spec/punchblock/command/accept_spec.rb +17 -7
  14. data/spec/punchblock/command/answer_spec.rb +19 -9
  15. data/spec/punchblock/command/dial_spec.rb +76 -27
  16. data/spec/punchblock/command/hangup_spec.rb +17 -7
  17. data/spec/punchblock/command/join_spec.rb +78 -24
  18. data/spec/punchblock/command/mute_spec.rb +3 -3
  19. data/spec/punchblock/command/redirect_spec.rb +33 -12
  20. data/spec/punchblock/command/reject_spec.rb +41 -14
  21. data/spec/punchblock/command/unjoin_spec.rb +29 -12
  22. data/spec/punchblock/command/unmute_spec.rb +3 -3
  23. data/spec/punchblock/command_node_spec.rb +43 -20
  24. data/spec/punchblock/component/asterisk/agi/command_spec.rb +52 -12
  25. data/spec/punchblock/component/asterisk/ami/action_spec.rb +69 -21
  26. data/spec/punchblock/component/component_node_spec.rb +12 -12
  27. data/spec/punchblock/component/input_spec.rb +304 -87
  28. data/spec/punchblock/component/output_spec.rb +434 -173
  29. data/spec/punchblock/component/prompt_spec.rb +63 -20
  30. data/spec/punchblock/component/receive_fax_spec.rb +43 -14
  31. data/spec/punchblock/component/record_spec.rb +215 -71
  32. data/spec/punchblock/component/send_fax_spec.rb +54 -15
  33. data/spec/punchblock/connection/asterisk_spec.rb +34 -24
  34. data/spec/punchblock/connection/freeswitch_spec.rb +9 -9
  35. data/spec/punchblock/connection/xmpp_spec.rb +92 -83
  36. data/spec/punchblock/event/answered_spec.rb +14 -4
  37. data/spec/punchblock/event/asterisk/ami/event_spec.rb +34 -12
  38. data/spec/punchblock/event/complete_spec.rb +36 -16
  39. data/spec/punchblock/event/dtmf_spec.rb +9 -3
  40. data/spec/punchblock/event/end_spec.rb +43 -10
  41. data/spec/punchblock/event/input_timers_started_spec.rb +1 -1
  42. data/spec/punchblock/event/joined_spec.rb +29 -7
  43. data/spec/punchblock/event/offer_spec.rb +41 -10
  44. data/spec/punchblock/event/ringing_spec.rb +14 -4
  45. data/spec/punchblock/event/started_speaking_spec.rb +9 -3
  46. data/spec/punchblock/event/stopped_speaking_spec.rb +9 -3
  47. data/spec/punchblock/event/unjoined_spec.rb +24 -6
  48. data/spec/punchblock/protocol_error_spec.rb +16 -13
  49. data/spec/punchblock/ref_spec.rb +90 -26
  50. data/spec/punchblock/translator/asterisk/call_spec.rb +176 -161
  51. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +18 -18
  52. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +9 -9
  53. data/spec/punchblock/translator/asterisk/component/composed_prompt_spec.rb +14 -14
  54. data/spec/punchblock/translator/asterisk/component/input_spec.rb +57 -36
  55. data/spec/punchblock/translator/asterisk/component/mrcp_native_prompt_spec.rb +50 -50
  56. data/spec/punchblock/translator/asterisk/component/mrcp_prompt_spec.rb +59 -48
  57. data/spec/punchblock/translator/asterisk/component/output_spec.rb +231 -221
  58. data/spec/punchblock/translator/asterisk/component/record_spec.rb +82 -82
  59. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +10 -10
  60. data/spec/punchblock/translator/asterisk/component_spec.rb +4 -4
  61. data/spec/punchblock/translator/asterisk_spec.rb +89 -82
  62. data/spec/punchblock/translator/freeswitch/call_spec.rb +114 -99
  63. data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +19 -19
  64. data/spec/punchblock/translator/freeswitch/component/input_spec.rb +24 -24
  65. data/spec/punchblock/translator/freeswitch/component/output_spec.rb +23 -23
  66. data/spec/punchblock/translator/freeswitch/component/record_spec.rb +78 -78
  67. data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +19 -19
  68. data/spec/punchblock/translator/freeswitch/component_spec.rb +8 -8
  69. data/spec/punchblock/translator/freeswitch_spec.rb +66 -59
  70. data/spec/punchblock/uri_list_spec.rb +45 -10
  71. data/spec/punchblock_spec.rb +13 -13
  72. data/spec/spec_helper.rb +18 -11
  73. data/spec/support/mock_connection_with_event_handler.rb +1 -1
  74. metadata +5 -4
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe Answered do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:answered, 'urn:xmpp:rayo:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:answered, 'urn:xmpp:rayo:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -25,19 +25,29 @@ module Punchblock
25
25
  it { should be_instance_of described_class }
26
26
 
27
27
  it_should_behave_like 'event'
28
- its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
28
+
29
+ describe '#headers' do
30
+ subject { super().headers }
31
+ it { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
32
+ end
29
33
 
30
34
  context "with no headers provided" do
31
35
  let(:stanza) { '<answered xmlns="urn:xmpp:rayo:1"/>' }
32
36
 
33
- its(:headers) { should == {} }
37
+ describe '#headers' do
38
+ subject { super().headers }
39
+ it { should == {} }
40
+ end
34
41
  end
35
42
  end
36
43
 
37
44
  describe "when setting options in initializer" do
38
45
  subject { described_class.new headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
39
46
 
40
- its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
47
+ describe '#headers' do
48
+ subject { super().headers }
49
+ it { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
50
+ end
41
51
  end
42
52
  end
43
53
  end
@@ -8,7 +8,7 @@ module Punchblock
8
8
  module AMI
9
9
  describe Event do
10
10
  it 'registers itself' do
11
- RayoNode.class_from_registration(:event, 'urn:xmpp:rayo:asterisk:ami:1').should be == described_class
11
+ expect(RayoNode.class_from_registration(:event, 'urn:xmpp:rayo:asterisk:ami:1')).to eq(described_class)
12
12
  end
13
13
 
14
14
  describe "from a stanza" do
@@ -27,9 +27,20 @@ module Punchblock
27
27
 
28
28
  it_should_behave_like 'event'
29
29
 
30
- its(:name) { should be == 'Newchannel' }
31
- its(:headers) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
32
- its(:attributes) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} } # For BC
30
+ describe '#name' do
31
+ subject { super().name }
32
+ it { should be == 'Newchannel' }
33
+ end
34
+
35
+ describe '#headers' do
36
+ subject { super().headers }
37
+ it { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
38
+ end
39
+
40
+ describe '#attributes' do
41
+ subject { super().attributes }
42
+ it { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
43
+ end # For BC
33
44
  end
34
45
 
35
46
  describe "when setting options in initializer" do
@@ -38,17 +49,28 @@ module Punchblock
38
49
  headers: {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'}
39
50
  end
40
51
 
41
- its(:name) { should be == 'Newchannel' }
42
- its(:headers) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
43
- its(:attributes) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} } # For BC
52
+ describe '#name' do
53
+ subject { super().name }
54
+ it { should be == 'Newchannel' }
55
+ end
56
+
57
+ describe '#headers' do
58
+ subject { super().headers }
59
+ it { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
60
+ end
61
+
62
+ describe '#attributes' do
63
+ subject { super().attributes }
64
+ it { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
65
+ end # For BC
44
66
 
45
67
  describe "exporting to Rayo" do
46
68
  it "should export to XML that can be understood by its parser" do
47
69
  new_instance = RayoNode.from_xml subject.to_rayo
48
- new_instance.should be_instance_of described_class
49
- new_instance.name.should == 'Newchannel'
50
- new_instance.headers.should == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'}
51
- new_instance.attributes.should == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} # For BC
70
+ expect(new_instance).to be_instance_of described_class
71
+ expect(new_instance.name).to eq('Newchannel')
72
+ expect(new_instance.headers).to eq({'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'})
73
+ expect(new_instance.attributes).to eq({'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'}) # For BC
52
74
  end
53
75
 
54
76
  it "should render to a parent node if supplied" do
@@ -56,7 +78,7 @@ module Punchblock
56
78
  parent = Nokogiri::XML::Node.new 'foo', doc
57
79
  doc.root = parent
58
80
  rayo_doc = subject.to_rayo(parent)
59
- rayo_doc.should == parent
81
+ expect(rayo_doc).to eq(parent)
60
82
  end
61
83
  end
62
84
  end
@@ -6,17 +6,18 @@ module Punchblock
6
6
  class Event
7
7
  describe Complete do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:complete, 'urn:xmpp:rayo:ext:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:complete, 'urn:xmpp:rayo:ext:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "setting a reason" do
13
13
  let(:reason) { Complete::Stop.new }
14
14
 
15
- subject { described_class.new }
16
-
17
- before { subject.reason = reason }
18
-
19
- its(:reason) { should == reason }
15
+ describe '#reason' do
16
+ it "should set the reason" do
17
+ subject.reason = reason
18
+ subject.reason.should == reason
19
+ end
20
+ end
20
21
  end
21
22
 
22
23
  describe "comparing for equality" do
@@ -38,7 +39,7 @@ module Punchblock
38
39
  let(:component_id) { 'abcd' }
39
40
 
40
41
  it "should be equal" do
41
- subject.should be == other_complete
42
+ expect(subject).to eq(other_complete)
42
43
  end
43
44
  end
44
45
 
@@ -48,7 +49,7 @@ module Punchblock
48
49
  let(:component_id) { 'abcd' }
49
50
 
50
51
  it "should not be equal" do
51
- subject.should_not be == other_complete
52
+ expect(subject).not_to eq(other_complete)
52
53
  end
53
54
  end
54
55
 
@@ -58,7 +59,7 @@ module Punchblock
58
59
  let(:component_id) { 'abcd' }
59
60
 
60
61
  it "should not be equal" do
61
- subject.should_not be == other_complete
62
+ expect(subject).not_to eq(other_complete)
62
63
  end
63
64
  end
64
65
 
@@ -68,7 +69,7 @@ module Punchblock
68
69
  let(:component_id) { 'efgh' }
69
70
 
70
71
  it "should not be equal" do
71
- subject.should_not be == other_complete
72
+ expect(subject).not_to eq(other_complete)
72
73
  end
73
74
  end
74
75
  end
@@ -88,7 +89,10 @@ module Punchblock
88
89
 
89
90
  it_should_behave_like 'event'
90
91
 
91
- its(:reason) { should be_instance_of Complete::Stop }
92
+ describe '#reason' do
93
+ subject { super().reason }
94
+ it { should be_instance_of Complete::Stop }
95
+ end
92
96
  end
93
97
  end
94
98
 
@@ -105,7 +109,10 @@ module Punchblock
105
109
 
106
110
  it { should be_instance_of Complete::Stop }
107
111
 
108
- its(:name) { should be == :stop }
112
+ describe '#name' do
113
+ subject { super().name }
114
+ it { should be == :stop }
115
+ end
109
116
  end
110
117
 
111
118
  describe Complete::Hangup do
@@ -121,7 +128,10 @@ module Punchblock
121
128
 
122
129
  it { should be_instance_of Complete::Hangup }
123
130
 
124
- its(:name) { should be == :hangup }
131
+ describe '#name' do
132
+ subject { super().name }
133
+ it { should be == :hangup }
134
+ end
125
135
  end
126
136
 
127
137
  describe Complete::Error do
@@ -139,15 +149,25 @@ module Punchblock
139
149
 
140
150
  it { should be_instance_of Complete::Error }
141
151
 
142
- its(:name) { should be == :error }
143
- its(:details) { should be == "Something really bad happened" }
152
+ describe '#name' do
153
+ subject { super().name }
154
+ it { should be == :error }
155
+ end
156
+
157
+ describe '#details' do
158
+ subject { super().details }
159
+ it { should be == "Something really bad happened" }
160
+ end
144
161
 
145
162
  describe "when setting options in initializer" do
146
163
  subject do
147
164
  Complete::Error.new :details => 'Ooops'
148
165
  end
149
166
 
150
- its(:details) { should be == 'Ooops' }
167
+ describe '#details' do
168
+ subject { super().details }
169
+ it { should be == 'Ooops' }
170
+ end
151
171
  end
152
172
  end
153
173
 
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe DTMF do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:dtmf, 'urn:xmpp:rayo:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:dtmf, 'urn:xmpp:rayo:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -18,7 +18,10 @@ module Punchblock
18
18
 
19
19
  it_should_behave_like 'event'
20
20
 
21
- its(:signal) { should be == '#' }
21
+ describe '#signal' do
22
+ subject { super().signal }
23
+ it { should be == '#' }
24
+ end
22
25
  end
23
26
 
24
27
  describe "when setting options in initializer" do
@@ -26,7 +29,10 @@ module Punchblock
26
29
  described_class.new :signal => '#'
27
30
  end
28
31
 
29
- its(:signal) { should be == '#' }
32
+ describe '#signal' do
33
+ subject { super().signal }
34
+ it { should be == '#' }
35
+ end
30
36
  end
31
37
  end
32
38
  end
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe End do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:end, 'urn:xmpp:rayo:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:end, 'urn:xmpp:rayo:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -27,16 +27,38 @@ module Punchblock
27
27
 
28
28
  it_should_behave_like 'event'
29
29
 
30
- its(:reason) { should be == :timeout }
31
- its(:platform_code) { should be == '18' }
32
- its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
30
+ describe '#reason' do
31
+ subject { super().reason }
32
+ it { should be == :timeout }
33
+ end
34
+
35
+ describe '#platform_code' do
36
+ subject { super().platform_code }
37
+ it { should be == '18' }
38
+ end
39
+
40
+ describe '#headers' do
41
+ subject { super().headers }
42
+ it { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
43
+ end
33
44
 
34
45
  context "with no headers or reason provided" do
35
46
  let(:stanza) { '<end xmlns="urn:xmpp:rayo:1"/>' }
36
47
 
37
- its(:reason) { should be_nil}
38
- its(:platform_code) { should be_nil }
39
- its(:headers) { should == {} }
48
+ describe '#reason' do
49
+ subject { super().reason }
50
+ it { should be_nil}
51
+ end
52
+
53
+ describe '#platform_code' do
54
+ subject { super().platform_code }
55
+ it { should be_nil }
56
+ end
57
+
58
+ describe '#headers' do
59
+ subject { super().headers }
60
+ it { should == {} }
61
+ end
40
62
  end
41
63
  end
42
64
 
@@ -47,9 +69,20 @@ module Punchblock
47
69
  headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
48
70
  end
49
71
 
50
- its(:reason) { should be == :hangup }
51
- its(:platform_code) { should be == '18' }
52
- its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
72
+ describe '#reason' do
73
+ subject { super().reason }
74
+ it { should be == :hangup }
75
+ end
76
+
77
+ describe '#platform_code' do
78
+ subject { super().platform_code }
79
+ it { should be == '18' }
80
+ end
81
+
82
+ describe '#headers' do
83
+ subject { super().headers }
84
+ it { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
85
+ end
53
86
  end
54
87
  end
55
88
  end
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe InputTimersStarted do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:'input-timers-started', 'urn:xmpp:rayo:prompt:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:'input-timers-started', 'urn:xmpp:rayo:prompt:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe Joined do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:joined, 'urn:xmpp:rayo:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:joined, 'urn:xmpp:rayo:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -18,17 +18,39 @@ module Punchblock
18
18
 
19
19
  it_should_behave_like 'event'
20
20
 
21
- its(:call_uri) { should be == 'b' }
22
- its(:call_id) { should be == 'b' }
23
- its(:mixer_name) { should be == 'm' }
21
+ describe '#call_uri' do
22
+ subject { super().call_uri }
23
+ it { should be == 'b' }
24
+ end
25
+
26
+ describe '#call_id' do
27
+ subject { super().call_id }
28
+ it { should be == 'b' }
29
+ end
30
+
31
+ describe '#mixer_name' do
32
+ subject { super().mixer_name }
33
+ it { should be == 'm' }
34
+ end
24
35
  end
25
36
 
26
37
  describe "when setting options in initializer" do
27
38
  subject { described_class.new :call_uri => 'abc123', :mixer_name => 'blah' }
28
39
 
29
- its(:call_uri) { should be == 'abc123' }
30
- its(:call_id) { should be == 'abc123' }
31
- its(:mixer_name) { should be == 'blah' }
40
+ describe '#call_uri' do
41
+ subject { super().call_uri }
42
+ it { should be == 'abc123' }
43
+ end
44
+
45
+ describe '#call_id' do
46
+ subject { super().call_id }
47
+ it { should be == 'abc123' }
48
+ end
49
+
50
+ describe '#mixer_name' do
51
+ subject { super().mixer_name }
52
+ it { should be == 'blah' }
53
+ end
32
54
  end
33
55
  end
34
56
  end
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe Offer do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:offer, 'urn:xmpp:rayo:1').should be == described_class
9
+ expect(RayoNode.class_from_registration(:offer, 'urn:xmpp:rayo:1')).to eq(described_class)
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -28,14 +28,28 @@ module Punchblock
28
28
 
29
29
  it_should_behave_like 'event'
30
30
 
31
- its(:to) { should be == 'tel:+18003211212' }
32
- its(:from) { should be == 'tel:+13058881212' }
33
- its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
31
+ describe '#to' do
32
+ subject { super().to }
33
+ it { should be == 'tel:+18003211212' }
34
+ end
35
+
36
+ describe '#from' do
37
+ subject { super().from }
38
+ it { should be == 'tel:+13058881212' }
39
+ end
40
+
41
+ describe '#headers' do
42
+ subject { super().headers }
43
+ it { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
44
+ end
34
45
 
35
46
  context "with no headers provided" do
36
47
  let(:stanza) { '<offer xmlns="urn:xmpp:rayo:1"/>' }
37
48
 
38
- its(:headers) { should == {} }
49
+ describe '#headers' do
50
+ subject { super().headers }
51
+ it { should == {} }
52
+ end
39
53
  end
40
54
 
41
55
  context "with multiple headers of the same name" do
@@ -51,7 +65,10 @@ module Punchblock
51
65
  MESSAGE
52
66
  end
53
67
 
54
- its(:headers) { should == {'X-skill' => ['sales', 'complaints']} }
68
+ describe '#headers' do
69
+ subject { super().headers }
70
+ it { should == {'X-skill' => ['sales', 'complaints']} }
71
+ end
55
72
  end
56
73
  end
57
74
 
@@ -62,16 +79,30 @@ module Punchblock
62
79
  headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
63
80
  end
64
81
 
65
- its(:to) { should be == 'tel:+18003211212' }
66
- its(:from) { should be == 'tel:+13058881212' }
67
- its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
82
+ describe '#to' do
83
+ subject { super().to }
84
+ it { should be == 'tel:+18003211212' }
85
+ end
86
+
87
+ describe '#from' do
88
+ subject { super().from }
89
+ it { should be == 'tel:+13058881212' }
90
+ end
91
+
92
+ describe '#headers' do
93
+ subject { super().headers }
94
+ it { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
95
+ end
68
96
 
69
97
  context "with headers set to nil" do
70
98
  subject do
71
99
  described_class.new headers: nil
72
100
  end
73
101
 
74
- its(:headers) { should == {} }
102
+ describe '#headers' do
103
+ subject { super().headers }
104
+ it { should == {} }
105
+ end
75
106
  end
76
107
  end
77
108
  end