intercom 2.5.4 → 3.0.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -2
  3. data/README.md +97 -118
  4. data/changes.txt +3 -0
  5. data/intercom.gemspec +1 -1
  6. data/lib/intercom.rb +13 -153
  7. data/lib/intercom/admin.rb +0 -2
  8. data/lib/intercom/api_operations/convert.rb +5 -5
  9. data/lib/intercom/api_operations/delete.rb +4 -6
  10. data/lib/intercom/api_operations/find.rb +9 -15
  11. data/lib/intercom/api_operations/find_all.rb +16 -21
  12. data/lib/intercom/api_operations/list.rb +4 -10
  13. data/lib/intercom/api_operations/load.rb +6 -6
  14. data/lib/intercom/api_operations/save.rb +23 -28
  15. data/lib/intercom/client.rb +95 -0
  16. data/lib/intercom/{collection_proxy.rb → client_collection_proxy.rb} +5 -8
  17. data/lib/intercom/company.rb +0 -16
  18. data/lib/intercom/contact.rb +0 -11
  19. data/lib/intercom/conversation.rb +0 -10
  20. data/lib/intercom/event.rb +0 -2
  21. data/lib/intercom/extended_api_operations/segments.rb +13 -0
  22. data/lib/intercom/extended_api_operations/tags.rb +4 -5
  23. data/lib/intercom/extended_api_operations/users.rb +3 -5
  24. data/lib/intercom/message.rb +0 -2
  25. data/lib/intercom/note.rb +1 -10
  26. data/lib/intercom/options.rb +11 -0
  27. data/lib/intercom/request.rb +9 -5
  28. data/lib/intercom/segment.rb +0 -7
  29. data/lib/intercom/service/admin.rb +14 -0
  30. data/lib/intercom/service/base_service.rb +21 -0
  31. data/lib/intercom/service/company.rb +28 -0
  32. data/lib/intercom/service/contact.rb +22 -0
  33. data/lib/intercom/service/conversation.rb +31 -0
  34. data/lib/intercom/service/event.rb +14 -0
  35. data/lib/intercom/service/message.rb +14 -0
  36. data/lib/intercom/service/note.rb +22 -0
  37. data/lib/intercom/service/segment.rb +16 -0
  38. data/lib/intercom/service/subscription.rb +21 -0
  39. data/lib/intercom/service/tag.rb +30 -0
  40. data/lib/intercom/service/user.rb +28 -0
  41. data/lib/intercom/subscription.rb +0 -8
  42. data/lib/intercom/tag.rb +0 -16
  43. data/lib/intercom/user.rb +0 -16
  44. data/lib/intercom/utils.rb +0 -1
  45. data/lib/intercom/version.rb +1 -1
  46. data/spec/spec_helper.rb +117 -0
  47. data/spec/unit/intercom/admin_spec.rb +11 -4
  48. data/spec/unit/intercom/client_collection_proxy_spec.rb +35 -0
  49. data/spec/unit/intercom/client_spec.rb +21 -0
  50. data/spec/unit/intercom/company_spec.rb +24 -11
  51. data/spec/unit/intercom/contact_spec.rb +7 -5
  52. data/spec/unit/intercom/conversation_spec.rb +28 -0
  53. data/spec/unit/intercom/event_spec.rb +6 -5
  54. data/spec/unit/intercom/message_spec.rb +9 -8
  55. data/spec/unit/intercom/note_spec.rb +9 -2
  56. data/spec/unit/intercom/segment_spec.rb +12 -0
  57. data/spec/unit/intercom/subscription_spec.rb +7 -5
  58. data/spec/unit/intercom/tag_spec.rb +13 -7
  59. data/spec/unit/intercom/user_spec.rb +41 -33
  60. data/spec/unit/intercom_spec.rb +0 -83
  61. metadata +30 -20
  62. data/lib/intercom/api_operations/count.rb +0 -16
  63. data/lib/intercom/count.rb +0 -21
  64. data/lib/intercom/extended_api_operations/reply.rb +0 -16
  65. data/lib/intercom/generic_handlers/base_handler.rb +0 -22
  66. data/lib/intercom/generic_handlers/count.rb +0 -59
  67. data/lib/intercom/generic_handlers/tag.rb +0 -71
  68. data/lib/intercom/generic_handlers/tag_find_all.rb +0 -47
  69. data/lib/intercom/notification.rb +0 -20
  70. data/lib/intercom/traits/generic_handler_binding.rb +0 -29
  71. data/spec/unit/intercom/collection_proxy_spec.rb +0 -34
  72. data/spec/unit/intercom/notification_spec.rb +0 -68
@@ -1,15 +1,7 @@
1
- require 'intercom/api_operations/list'
2
- require 'intercom/api_operations/find_all'
3
- require 'intercom/api_operations/save'
4
- require 'intercom/api_operations/delete'
5
1
  require 'intercom/traits/api_resource'
6
2
 
7
3
  module Intercom
8
4
  class Subscription
9
- include ApiOperations::List
10
- include ApiOperations::Find
11
- include ApiOperations::Save
12
- include ApiOperations::Delete
13
5
  include Traits::ApiResource
14
6
  end
15
7
  end
@@ -1,23 +1,7 @@
1
- require 'intercom/api_operations/count'
2
- require 'intercom/api_operations/save'
3
- require 'intercom/api_operations/list'
4
- require 'intercom/api_operations/find'
5
- require 'intercom/api_operations/find_all'
6
1
  require 'intercom/traits/api_resource'
7
- require 'intercom/traits/generic_handler_binding'
8
- require 'intercom/generic_handlers/tag'
9
- require 'intercom/generic_handlers/tag_find_all'
10
2
 
11
3
  module Intercom
12
4
  class Tag
13
- include ApiOperations::Count
14
- include ApiOperations::Save
15
- include ApiOperations::List
16
- include ApiOperations::Find
17
- include ApiOperations::FindAll
18
5
  include Traits::ApiResource
19
- include Traits::GenericHandlerBinding
20
- include GenericHandlers::Tag
21
- include GenericHandlers::TagFindAll
22
6
  end
23
7
  end
@@ -1,24 +1,8 @@
1
- require 'intercom/api_operations/count'
2
- require 'intercom/api_operations/list'
3
- require 'intercom/api_operations/load'
4
- require 'intercom/api_operations/find'
5
- require 'intercom/api_operations/find_all'
6
- require 'intercom/api_operations/save'
7
- require 'intercom/api_operations/delete'
8
- require 'intercom/extended_api_operations/tags'
9
1
  require 'intercom/traits/incrementable_attributes'
10
2
  require 'intercom/traits/api_resource'
11
3
 
12
4
  module Intercom
13
5
  class User
14
- include ApiOperations::Count
15
- include ApiOperations::List
16
- include ApiOperations::Load
17
- include ApiOperations::Find
18
- include ApiOperations::FindAll
19
- include ApiOperations::Save
20
- include ApiOperations::Delete
21
- include ExtendedApiOperations::Tags
22
6
  include Traits::IncrementableAttributes
23
7
  include Traits::ApiResource
24
8
 
@@ -45,7 +45,6 @@ module Intercom
45
45
  end
46
46
 
47
47
  def define_lightweight_class(class_name)
48
- #File.open('./intercom_ruby_dynamically_defined_classes.log', 'a') {|f| f.puts("Dynamically defining the class Intercom::#{class_name}") } #HACK
49
48
  new_class_definition = Class.new(Object) do
50
49
  include Traits::ApiResource
51
50
  end
@@ -1,3 +1,3 @@
1
1
  module Intercom #:nodoc:
2
- VERSION = "2.5.4"
2
+ VERSION = "3.0.0b1"
3
3
  end
@@ -57,6 +57,45 @@ def test_user(email="bob@example.com")
57
57
  }
58
58
  end
59
59
 
60
+ def test_admin_list
61
+ {
62
+ "type" => "admin.list",
63
+ "admins" => [
64
+ {
65
+ "type" => "admin",
66
+ "id" => "1234",
67
+ "name" => "Hoban Washburne",
68
+ "email" => "wash@serenity.io"
69
+ }
70
+ ]
71
+ }
72
+ end
73
+
74
+ def test_company
75
+ {
76
+ "type" => "company",
77
+ "id" => "531ee472cce572a6ec000006",
78
+ "name" => "Blue Sun",
79
+ "plan" => {
80
+ "type" =>"plan",
81
+ "id" =>"1",
82
+ "name" =>"Paid"
83
+ },
84
+ "company_id" => "6",
85
+ "remote_created_at" => 1394531169,
86
+ "created_at" => 1394533506,
87
+ "updated_at" => 1396874658,
88
+ "last_request_at" => 1396874658,
89
+ "monthly_spend" => 49,
90
+ "session_count" => 26,
91
+ "user_count" => 10,
92
+ "custom_attributes" => {
93
+ "paid_subscriber" => true,
94
+ "team_mates" => 0
95
+ }
96
+ }
97
+ end
98
+
60
99
  def test_messages
61
100
  [test_message, test_message]
62
101
  end
@@ -122,6 +161,84 @@ def page_of_users(include_next_link= false)
122
161
  }
123
162
  end
124
163
 
164
+ def test_conversation
165
+ {
166
+ "type" => "conversation",
167
+ "id" => "147",
168
+ "created_at" => 1400850973,
169
+ "updated_at" => 1400857494,
170
+ "conversation_message" => {
171
+ "type" => "conversation_message",
172
+ "subject" => "",
173
+ "body" => "<p>Hi Alice,</p>\n\n<p>We noticed you using our Product, do you have any questions?</p> \n<p>- Jane</p>",
174
+ "author" => {
175
+ "type" => "admin",
176
+ "id" => "25"
177
+ },
178
+ "attachments" => [
179
+ {
180
+ "name" => "signature",
181
+ "url" => "http =>//someurl.com/signature.jpg"
182
+ }
183
+ ]
184
+ },
185
+ "user" => {
186
+ "type" => "user",
187
+ "id" => "536e564f316c83104c000020"
188
+ },
189
+ "assignee" => {
190
+ "type" => "admin",
191
+ "id" => "25"
192
+ },
193
+ "open" => true,
194
+ "read" => true,
195
+ "conversation_parts" => {
196
+ "type" => "conversation_part.list",
197
+ "conversation_parts" => [
198
+ ]
199
+ }
200
+ }
201
+ end
202
+
203
+ def segment
204
+ {
205
+ "type" => "segment",
206
+ "id" => "5310d8e7598c9a0b24000002",
207
+ "name" => "Active",
208
+ "created_at" => 1393613031,
209
+ "updated_at" => 1393613031
210
+ }
211
+ end
212
+
213
+ def segment_list
214
+ {
215
+ "type" => "segment.list",
216
+ "segments" => [
217
+ {
218
+ "created_at" => 1393613031,
219
+ "id" => "5310d8e7598c9a0b24000002",
220
+ "name" => "Active",
221
+ "type" => "segment",
222
+ "updated_at" => 1393613031
223
+ },
224
+ {
225
+ "created_at" => 1393613030,
226
+ "id" => "5310d8e6598c9a0b24000001",
227
+ "name" => "New",
228
+ "type" => "segment",
229
+ "updated_at" => 1393613030
230
+ },
231
+ {
232
+ "created_at" => 1393613031,
233
+ "id" => "5310d8e7598c9a0b24000003",
234
+ "name" => "Slipping Away",
235
+ "type" => "segment",
236
+ "updated_at" => 1393613031
237
+ }
238
+ ]
239
+ }
240
+ end
241
+
125
242
  def test_tag
126
243
  {
127
244
  "id" => "4f73428b5e4dfc000b000112",
@@ -1,9 +1,16 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe "Intercom::Admin" do
4
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
5
+
4
6
  it "returns a CollectionProxy for all without making any requests" do
5
- Intercom.expects(:execute_request).never
6
- all = Intercom::Admin.all
7
- all.must_be_instance_of(Intercom::CollectionProxy)
7
+ client.expects(:execute_request).never
8
+ all = client.admins.all
9
+ all.must_be_instance_of(Intercom::ClientCollectionProxy)
10
+ end
11
+
12
+ it 'gets an admin list' do
13
+ client.expects(:get).with("/admins", {}).returns(test_admin_list)
14
+ client.admins.all.each { |a| }
8
15
  end
9
- end
16
+ end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe Intercom::ClientCollectionProxy do
4
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
5
+
6
+ it "stops iterating if no next link" do
7
+ client.expects(:get).with("/users", {}).returns(page_of_users(false))
8
+ emails = []
9
+ client.users.all.each { |user| emails << user.email }
10
+ emails.must_equal %W(user1@example.com user2@example.com user3@example.com)
11
+ end
12
+
13
+ it "keeps iterating if next link" do
14
+ client.expects(:get).with("/users", {}).returns(page_of_users(true))
15
+ client.expects(:get).with('https://api.intercom.io/users?per_page=50&page=2', {}).returns(page_of_users(false))
16
+ emails = []
17
+ client.users.all.each { |user| emails << user.email }
18
+ end
19
+
20
+ it "supports indexed array access" do
21
+ client.expects(:get).with("/users", {}).returns(page_of_users(false))
22
+ client.users.all[0].email.must_equal 'user1@example.com'
23
+ end
24
+
25
+ it "supports map" do
26
+ client.expects(:get).with("/users", {}).returns(page_of_users(false))
27
+ emails = client.users.all.map { |user| user.email }
28
+ emails.must_equal %W(user1@example.com user2@example.com user3@example.com)
29
+ end
30
+
31
+ it "supports querying" do
32
+ client.expects(:get).with("/users", {:tag_name => 'Taggart J'}).returns(page_of_users(false))
33
+ client.users.find_all(:tag_name => 'Taggart J').map(&:email).must_equal %W(user1@example.com user2@example.com user3@example.com)
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Intercom
4
+ describe Client do
5
+ let(:app_id) { 'myappid' }
6
+ let(:api_key) { 'myapikey' }
7
+ let(:client) { Client.new(app_id: app_id, api_key: api_key) }
8
+
9
+ it 'should set the base url' do
10
+ client.base_url.must_equal('https://api.intercom.io')
11
+ end
12
+
13
+ it 'should be able to change the base url' do
14
+ prev = client.options(Intercom::Client.set_base_url('https://mymockintercom.io'))
15
+ client.base_url.must_equal('https://mymockintercom.io')
16
+ client.options(prev)
17
+ client.base_url.must_equal('https://api.intercom.io')
18
+ end
19
+
20
+ end
21
+ end
@@ -1,23 +1,36 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Intercom::Company do
4
-
4
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
5
+
5
6
  describe 'when no response raises error' do
6
7
  it 'on find' do
7
- Intercom.expects(:get).with("/companies", {:company_id => '4'}).returns(nil)
8
- proc {Intercom::Company.find(:company_id => '4')}.must_raise Intercom::HttpError
8
+ client.expects(:get).with("/companies", {:company_id => '4'}).returns(nil)
9
+ proc {client.companies.find(:company_id => '4')}.must_raise Intercom::HttpError
9
10
  end
10
-
11
+
11
12
  it 'on find_all' do
12
- Intercom.expects(:get).with("/companies", {}).returns(nil)
13
- proc {Intercom::Company.all.each {|company| }}.must_raise Intercom::HttpError
13
+ client.expects(:get).with("/companies", {}).returns(nil)
14
+ proc {client.companies.all.each {|company| }}.must_raise Intercom::HttpError
14
15
  end
15
-
16
+
16
17
  it 'on load' do
17
- Intercom.expects(:get).with("/companies", {:company_id => '4'}).returns({'type' =>'user', 'id' =>'aaaaaaaaaaaaaaaaaaaaaaaa', 'company_id' => '4', 'name' => 'MyCo'})
18
- company = Intercom::Company.find(:company_id => '4')
19
- Intercom.expects(:get).with('/companies/aaaaaaaaaaaaaaaaaaaaaaaa', {}).returns(nil)
20
- proc {company.load}.must_raise Intercom::HttpError
18
+ client.expects(:get).with("/companies", {:company_id => '4'}).returns({'type' =>'user', 'id' =>'aaaaaaaaaaaaaaaaaaaaaaaa', 'company_id' => '4', 'name' => 'MyCo'})
19
+ company = client.companies.find(:company_id => '4')
20
+ client.expects(:get).with('/companies/aaaaaaaaaaaaaaaaaaaaaaaa', {}).returns(nil)
21
+ proc {client.companies.load(company)}.must_raise Intercom::HttpError
22
+ end
23
+ end
24
+
25
+ it 'gets users in a company' do
26
+ client.expects(:get).with("/companies/abc123/users", {}).returns(page_of_users(false))
27
+ client.companies.users('abc123').each do |u|
21
28
  end
22
29
  end
30
+
31
+ it 'finds a company' do
32
+ client.expects(:get).with("/companies/531ee472cce572a6ec000006", {}).returns(test_company)
33
+ company = client.companies.find(id: '531ee472cce572a6ec000006')
34
+ company.name.must_equal("Blue Sun")
35
+ end
23
36
  end
@@ -1,9 +1,11 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe "Intercom::Contact" do
4
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
5
+
4
6
  it 'should not throw ArgumentErrors when there are no parameters' do
5
- Intercom.expects(:post)
6
- Intercom::Contact.create
7
+ client.expects(:post)
8
+ client.contacts.create
7
9
  end
8
10
 
9
11
  describe 'converting' do
@@ -11,15 +13,15 @@ describe "Intercom::Contact" do
11
13
  let(:user) { Intercom::User.from_api(id: 'user_id') }
12
14
 
13
15
  it do
14
- Intercom.expects(:post).with(
16
+ client.expects(:post).with(
15
17
  "/contacts/convert",
16
18
  {
17
19
  contact: { user_id: contact.user_id },
18
- user: user.identity_hash
20
+ user: { 'id' => user.id }
19
21
  }
20
22
  ).returns(test_user)
21
23
 
22
- contact.convert(user)
24
+ client.contacts.convert(contact, user)
23
25
  end
24
26
  end
25
27
  end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Intercom::Conversation" do
4
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
5
+
6
+ it "gets a conversation" do
7
+ client.expects(:get).with("/conversations/147", {}).returns(test_conversation)
8
+ client.conversations.find(:id => "147")
9
+ end
10
+
11
+ it 'marks a conversation as read' do
12
+ client.expects(:put).with('/conversations/147', { read: true })
13
+ client.conversations.mark_read('147')
14
+ end
15
+
16
+ it 'replies to a conversation' do
17
+ client.expects(:post).with('/conversations/147/reply', { type: 'user', body: 'Thanks again', message_type: 'comment', user_id: 'ac4', conversation_id: '147' }).returns(test_conversation)
18
+ client.conversations.reply(id: '147', type: 'user', body: 'Thanks again', message_type: 'comment', user_id: 'ac4')
19
+ end
20
+
21
+ # it "creates a subscription" do
22
+ # client.expects(:post).with("/subscriptions", {'url' => "http://example.com", 'topics' => ["user.created"]}).returns(test_subscription)
23
+ # subscription = client.subscriptions.create(:url => "http://example.com", :topics => ["user.created"])
24
+ # subscription.request.topics[0].must_equal "user.created"
25
+ # subscription.request.url.must_equal "http://example.com"
26
+ # end
27
+
28
+ end
@@ -4,11 +4,12 @@ describe "Intercom::Event" do
4
4
 
5
5
  let(:user) {Intercom::User.new("email" => "jim@example.com", :user_id => "12345", :created_at => Time.now, :name => "Jim Bob")}
6
6
  let(:created_time) {Time.now - 300}
7
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
7
8
 
8
9
  it "creates an event with metadata" do
9
- Intercom.expects(:post).with('/events', {'event_name' => 'Eventful 1', 'created_at' => created_time.to_i, 'email' => 'joe@example.com', 'metadata' => {'invitee_email' => 'pi@example.org', :invite_code => 'ADDAFRIEND', 'found_date' => 12909364407}}).returns(:status => 202)
10
+ client.expects(:post).with('/events', {'event_name' => 'Eventful 1', 'created_at' => created_time.to_i, 'email' => 'joe@example.com', 'metadata' => {'invitee_email' => 'pi@example.org', :invite_code => 'ADDAFRIEND', 'found_date' => 12909364407}}).returns(:status => 202)
10
11
 
11
- Intercom::Event.create(:event_name => "Eventful 1", :created_at => created_time,
12
+ client.events.create(:event_name => "Eventful 1", :created_at => created_time,
12
13
  :email => 'joe@example.com',
13
14
  :metadata => {
14
15
  "invitee_email" => "pi@example.org",
@@ -18,8 +19,8 @@ describe "Intercom::Event" do
18
19
  end
19
20
 
20
21
  it "creates an event without metadata" do
21
- Intercom.expects(:post).with('/events', {'event_name' => 'sale of item', 'email' => 'joe@example.com'})
22
- Intercom::Event.create(:event_name => "sale of item", :email => 'joe@example.com')
22
+ client.expects(:post).with('/events', {'event_name' => 'sale of item', 'email' => 'joe@example.com'})
23
+ client.events.create(:event_name => "sale of item", :email => 'joe@example.com')
23
24
  end
24
-
25
+
25
26
  end
@@ -3,19 +3,20 @@ require 'spec_helper'
3
3
  describe "Intercom::Message" do
4
4
 
5
5
  let (:user) {Intercom::User.new("email" => "jim@example.com", :user_id => "12345", :created_at => Time.now, :name => "Jim Bob")}
6
+ let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
6
7
 
7
8
  it 'creates an user message with symbol keys' do
8
- Intercom.expects(:post).with('/messages', {'from' => { :type => 'user', :email => 'jim@example.com'}, 'body' => 'halp'}).returns(:status => 200)
9
- Intercom::Message.create(:from => { :type => "user", :email => "jim@example.com" }, :body => "halp")
9
+ client.expects(:post).with('/messages', {'from' => { :type => 'user', :email => 'jim@example.com'}, 'body' => 'halp'}).returns(:status => 200)
10
+ client.messages.create(:from => { :type => "user", :email => "jim@example.com" }, :body => "halp")
10
11
  end
11
-
12
+
12
13
  it "creates an user message with string keys" do
13
- Intercom.expects(:post).with('/messages', {'from' => { 'type' => 'user', 'email' => 'jim@example.com'}, 'body' => 'halp'}).returns(:status => 200)
14
- Intercom::Message.create('from' => { 'type' => "user", 'email' => "jim@example.com" }, 'body' => "halp")
14
+ client.expects(:post).with('/messages', {'from' => { 'type' => 'user', 'email' => 'jim@example.com'}, 'body' => 'halp'}).returns(:status => 200)
15
+ client.messages.create('from' => { 'type' => "user", 'email' => "jim@example.com" }, 'body' => "halp")
15
16
  end
16
-
17
+
17
18
  it "creates a admin message" do
18
- Intercom.expects(:post).with('/messages', {'from' => { 'type' => "admin", 'id' => "1234" }, 'to' => { 'type' => 'user', 'id' => '5678' }, 'body' => 'halp', 'message_type' => 'inapp'}).returns(:status => 200)
19
- Intercom::Message.create('from' => { 'type' => "admin", 'id' => "1234" }, :to => { 'type' => 'user', 'id' => '5678' }, 'body' => "halp", 'message_type' => 'inapp')
19
+ client.expects(:post).with('/messages', {'from' => { 'type' => "admin", 'id' => "1234" }, 'to' => { 'type' => 'user', 'id' => '5678' }, 'body' => 'halp', 'message_type' => 'inapp'}).returns(:status => 200)
20
+ client.messages.create('from' => { 'type' => "admin", 'id' => "1234" }, :to => { 'type' => 'user', 'id' => '5678' }, 'body' => "halp", 'message_type' => 'inapp')
20
21
  end
21
22
  end