podio 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +9 -2
  4. data/README.md +1 -4
  5. data/examples/oauth_web_flow/Gemfile +4 -0
  6. data/examples/{sinatra.rb → oauth_web_flow/sinatra.rb} +7 -6
  7. data/lib/podio.rb +18 -3
  8. data/lib/podio/active_podio/base.rb +107 -62
  9. data/lib/podio/client.rb +30 -5
  10. data/lib/podio/error.rb +22 -10
  11. data/lib/podio/middleware/error_response.rb +23 -16
  12. data/lib/podio/middleware/json_response.rb +1 -1
  13. data/lib/podio/middleware/logger.rb +3 -0
  14. data/lib/podio/middleware/oauth2.rb +3 -0
  15. data/lib/podio/models/account_provider.rb +1 -0
  16. data/lib/podio/models/action.rb +19 -2
  17. data/lib/podio/models/activation_status.rb +8 -2
  18. data/lib/podio/models/app_store_category.rb +4 -1
  19. data/lib/podio/models/app_store_share.rb +29 -16
  20. data/lib/podio/models/application.rb +57 -2
  21. data/lib/podio/models/application_email.rb +5 -2
  22. data/lib/podio/models/application_field.rb +5 -2
  23. data/lib/podio/models/batch.rb +8 -0
  24. data/lib/podio/models/by_line.rb +2 -1
  25. data/lib/podio/models/calendar_event.rb +63 -3
  26. data/lib/podio/models/calendar_mute.rb +6 -1
  27. data/lib/podio/models/category.rb +1 -0
  28. data/lib/podio/models/comment.rb +29 -8
  29. data/lib/podio/models/condition.rb +8 -0
  30. data/lib/podio/models/condition_set.rb +40 -0
  31. data/lib/podio/models/contact.rb +13 -2
  32. data/lib/podio/models/contract.rb +85 -1
  33. data/lib/podio/models/contract_event.rb +17 -0
  34. data/lib/podio/models/contract_price.rb +3 -7
  35. data/lib/podio/models/contract_price_v2.rb +31 -0
  36. data/lib/podio/models/contract_user.rb +4 -0
  37. data/lib/podio/models/conversation.rb +114 -13
  38. data/lib/podio/models/conversation_event.rb +51 -0
  39. data/lib/podio/models/conversation_message.rb +2 -1
  40. data/lib/podio/models/conversation_participant.rb +3 -2
  41. data/lib/podio/models/date_election.rb +35 -0
  42. data/lib/podio/models/email_contact.rb +46 -0
  43. data/lib/podio/models/email_subscription_setting.rb +4 -23
  44. data/lib/podio/models/embed.rb +4 -4
  45. data/lib/podio/models/experiment.rb +71 -0
  46. data/lib/podio/models/external_file.rb +7 -2
  47. data/lib/podio/models/file_attachment.rb +23 -0
  48. data/lib/podio/models/form.rb +7 -0
  49. data/lib/podio/models/friend.rb +12 -0
  50. data/lib/podio/models/grant.rb +73 -0
  51. data/lib/podio/models/hook.rb +13 -6
  52. data/lib/podio/models/importer.rb +3 -0
  53. data/lib/podio/models/integration.rb +17 -6
  54. data/lib/podio/models/invoice.rb +30 -0
  55. data/lib/podio/models/item.rb +81 -14
  56. data/lib/podio/models/item_diff.rb +9 -5
  57. data/lib/podio/models/item_field.rb +11 -2
  58. data/lib/podio/models/item_revision.rb +5 -2
  59. data/lib/podio/models/linked_account.rb +1 -0
  60. data/lib/podio/models/live.rb +61 -0
  61. data/lib/podio/models/net_promoter_score.rb +28 -0
  62. data/lib/podio/models/notification.rb +9 -4
  63. data/lib/podio/models/notification_group.rb +5 -2
  64. data/lib/podio/models/o_auth.rb +4 -2
  65. data/lib/podio/models/o_auth_client.rb +1 -2
  66. data/lib/podio/models/organization.rb +22 -10
  67. data/lib/podio/models/organization_contact.rb +2 -1
  68. data/lib/podio/models/organization_member.rb +17 -3
  69. data/lib/podio/models/organization_profile.rb +8 -2
  70. data/lib/podio/models/pin.rb +27 -0
  71. data/lib/podio/models/profile.rb +19 -50
  72. data/lib/podio/models/promotion.rb +91 -0
  73. data/lib/podio/models/rating.rb +23 -5
  74. data/lib/podio/models/recurrence.rb +6 -3
  75. data/lib/podio/models/reference.rb +17 -1
  76. data/lib/podio/models/reminder.rb +8 -3
  77. data/lib/podio/models/search.rb +7 -1
  78. data/lib/podio/models/space.rb +41 -2
  79. data/lib/podio/models/space_contact.rb +1 -2
  80. data/lib/podio/models/space_invitation.rb +4 -5
  81. data/lib/podio/models/space_member.rb +37 -4
  82. data/lib/podio/models/status.rb +20 -8
  83. data/lib/podio/models/stream_activity_group.rb +40 -0
  84. data/lib/podio/models/stream_mute.rb +7 -8
  85. data/lib/podio/models/stream_object.rb +19 -11
  86. data/lib/podio/models/subscription.rb +7 -1
  87. data/lib/podio/models/tag.rb +6 -1
  88. data/lib/podio/models/tag_search.rb +2 -1
  89. data/lib/podio/models/task.rb +74 -18
  90. data/lib/podio/models/task_label.rb +10 -2
  91. data/lib/podio/models/user.rb +50 -6
  92. data/lib/podio/models/user_mail.rb +4 -0
  93. data/lib/podio/models/user_status.rb +7 -0
  94. data/lib/podio/models/view.rb +27 -7
  95. data/lib/podio/models/widget.rb +25 -3
  96. data/lib/podio/version.rb +1 -1
  97. data/podio.gemspec +13 -10
  98. data/script/config.example.rb +6 -0
  99. data/script/referenceGenerator.rb +87 -0
  100. data/test/active_podio_test.rb +44 -34
  101. data/test/client_test.rb +13 -2
  102. metadata +89 -36
  103. data/lib/podio/middleware/date_conversion.rb +0 -37
  104. data/lib/podio/models/bulletin.rb +0 -60
  105. data/lib/podio/models/connection.rb +0 -53
  106. data/lib/podio/models/news.rb +0 -85
@@ -0,0 +1,30 @@
1
+ class Podio::Invoice < ActivePodio::Base
2
+ property :invoice_id, :string
3
+ property :number, :string
4
+ property :date, :datetime
5
+ property :due_date, :datetime
6
+ property :amount, :float
7
+ property :amount_due, :float
8
+ property :amount_paid, :float
9
+ property :status, :string
10
+
11
+ alias_method :id, :invoice_id
12
+
13
+ class << self
14
+ def find(invoice_id)
15
+ member Podio.connection.get("/invoice/#{invoice_id}").body
16
+ end
17
+
18
+ def get_as_pdf(invoice_id)
19
+ Podio.client.connection.get("/invoice/#{invoice_id}/pdf").body
20
+ end
21
+
22
+ def find_by_contract(contract_id)
23
+ list Podio.connection.get("/invoice/contract/#{contract_id}/").body
24
+ end
25
+
26
+ def pay(invoice_id)
27
+ Podio.connection.post("/invoice/#{invoice_id}/pay").status
28
+ end
29
+ end
30
+ end
@@ -1,16 +1,22 @@
1
+ # @see https://developers.podio.com/doc/items
1
2
  class Podio::Item < ActivePodio::Base
2
3
  include ActivePodio::Updatable
3
4
 
4
5
  # Included Get Item basic
5
6
  property :item_id, :integer
7
+ property :revision, :integer
6
8
  property :app, :hash
9
+ property :app_item_id, :integer
10
+ property :app_item_id_formatted, :string
7
11
  property :external_id, :string
8
12
  property :title, :string
9
13
  property :fields, :array
10
14
  property :rights, :array
15
+ property :created_on, :datetime
11
16
 
12
- has_one :initial_revision, :class => 'ItemRevision'
13
- has_one :current_revision, :class => 'ItemRevision'
17
+ has_one :created_by, :class => 'ByLine'
18
+ has_one :created_via, :class => 'Via'
19
+ has_one :date_election, :class => 'DateElection'
14
20
 
15
21
  # Also included in the full Get item
16
22
  property :ratings, :hash
@@ -20,6 +26,7 @@ class Podio::Item < ActivePodio::Base
20
26
  property :refs, :array
21
27
  property :tags, :array
22
28
  property :subscribed, :boolean
29
+ property :pinned, :boolean
23
30
  property :user_ratings, :hash
24
31
  property :link, :string
25
32
  property :invite, :hash
@@ -27,10 +34,16 @@ class Podio::Item < ActivePodio::Base
27
34
  property :linked_account_id, :integer
28
35
  property :ref, :hash # linked items
29
36
  property :priority, :float
37
+ property :excerpt, :string
38
+ property :is_liked, :boolean
39
+ property :like_count, :integer
40
+ property :push, :hash
41
+ property :presence, :hash
30
42
 
31
43
  # Get items
32
44
  property :comment_count, :integer
33
45
  property :task_count, :integer
46
+ property :subscribed_count, :integer
34
47
 
35
48
  has_many :revisions, :class => 'ItemRevision'
36
49
  has_many :files, :class => 'FileAttachment'
@@ -39,6 +52,7 @@ class Podio::Item < ActivePodio::Base
39
52
  has_one :reminder, :class => 'Reminder'
40
53
  has_one :recurrence, :class => 'Recurrence'
41
54
  has_one :linked_account_data, :class => 'LinkedAccountData'
55
+ has_one :application, :class => 'Application', :property => :app
42
56
 
43
57
  # For inserting/updating
44
58
  property :file_ids, :array
@@ -46,20 +60,23 @@ class Podio::Item < ActivePodio::Base
46
60
  alias_method :id, :item_id
47
61
  delegate_to_hash :app, :app_id, :app_name, :item_name
48
62
 
63
+ # @see https://developers.podio.com/doc/items/add-new-item-22362
49
64
  def create
50
- self.item_id = self.class.create(self.app_id, prepare_item_values(self))
65
+ model = self.class.create(self.app_id, prepare_item_values(self))
66
+ self.attributes = model.attributes
67
+ self
51
68
  end
52
69
 
70
+ # @see https://developers.podio.com/doc/items/delete-item-22364
53
71
  def destroy
54
72
  self.class.delete(self.id)
55
73
  end
56
74
 
75
+ # @see https://developers.podio.com/doc/items/update-item-22363
57
76
  def update
58
77
  self.class.update(self.id, prepare_item_values(self))
59
78
  end
60
79
 
61
- handle_api_errors_for :create, :update
62
-
63
80
  protected
64
81
  def prepare_item_values(item)
65
82
  fields = item.fields.collect { |field| field.values.nil? ? nil : { :external_id => field.external_id, :values => field.values } }.compact
@@ -70,16 +87,20 @@ class Podio::Item < ActivePodio::Base
70
87
  end
71
88
 
72
89
  class << self
90
+ # @see https://developers.podio.com/doc/items/get-item-22360
73
91
  def find(id)
74
92
  member Podio.connection.get("/item/#{id}").body
75
93
  end
76
94
 
77
- def find_basic(id)
78
- member Podio.connection.get("/item/#{id}/basic").body
95
+ def find_by_app_item_id(app_id, app_item_id)
96
+ member Podio.connection.get("/app/#{app_id}/item/#{app_item_id}").body
79
97
  end
80
98
 
81
- def find_basic_hash(id)
82
- Podio.connection.get("/item/#{id}/basic").body
99
+ # @see https://developers.podio.com/doc/items/get-item-basic-61768
100
+ def find_basic(id, options={})
101
+ member Podio.connection.get { |req|
102
+ req.url("/item/#{id}/basic", options)
103
+ }.body
83
104
  end
84
105
 
85
106
  def find_basic_by_field(item_id, field_id)
@@ -90,16 +111,22 @@ class Podio::Item < ActivePodio::Base
90
111
  Podio.connection.get("/item/#{item_id}/reference/#{field_id}/preview").body
91
112
  end
92
113
 
114
+ def find_by_external_id(app_id, external_id)
115
+ member Podio.connection.get("/item/app/#{app_id}/external_id/#{external_id}").body
116
+ end
117
+
93
118
  def find_all_by_external_id(app_id, external_id)
94
119
  collection Podio.connection.get("/item/app/#{app_id}/v2/?external_id=#{external_id}").body
95
120
  end
96
121
 
122
+ # @see https://developers.podio.com/doc/items/get-items-27803
97
123
  def find_all(app_id, options={})
98
124
  collection Podio.connection.get { |req|
99
125
  req.url("/item/app/#{app_id}/", options)
100
126
  }.body
101
127
  end
102
128
 
129
+ # @see https://developers.podio.com/doc/items/filter-items-by-view-4540284
103
130
  def find_by_filter_id(app_id, filter_id, attributes)
104
131
  collection Podio.connection.post { |req|
105
132
  req.url "/item/app/#{app_id}/filter/#{filter_id}/"
@@ -107,6 +134,7 @@ class Podio::Item < ActivePodio::Base
107
134
  }.body
108
135
  end
109
136
 
137
+ # @see https://developers.podio.com/doc/items/filter-items-4496747
110
138
  def find_by_filter_values(app_id, filter_values, attributes={})
111
139
  attributes[:filters] = filter_values
112
140
  collection Podio.connection.post { |req|
@@ -115,6 +143,7 @@ class Podio::Item < ActivePodio::Base
115
143
  }.body
116
144
  end
117
145
 
146
+ # @see https://developers.podio.com/doc/items/get-app-values-22455
118
147
  def find_app_values(app_id)
119
148
  response = Podio.connection.get { |req|
120
149
  req.url("/item/app/#{app_id}/values")
@@ -122,12 +151,22 @@ class Podio::Item < ActivePodio::Base
122
151
  response.body
123
152
  end
124
153
 
154
+ # @see https://developers.podio.com/doc/items/get-item-references-22439
155
+ def find_references(item_id)
156
+ response = Podio.connection.get { |req|
157
+ req.url("/item/#{item_id}/reference/")
158
+ }
159
+ response.body
160
+ end
161
+
162
+ # @see https://developers.podio.com/doc/items/get-references-to-item-by-field-7403920
125
163
  def find_references_by_field(item_id, field_id, options = {})
126
164
  list Podio.connection.get { |req|
127
165
  req.url("/item/#{item_id}/reference/field/#{field_id}", options)
128
166
  }.body
129
167
  end
130
168
 
169
+ # @see https://developers.podio.com/doc/items/calculate-67633
131
170
  def calculate(app_id, config)
132
171
  response = Podio.connection.post do |req|
133
172
  req.url "/item/app/#{app_id}/calculate"
@@ -143,6 +182,7 @@ class Podio::Item < ActivePodio::Base
143
182
  }.body
144
183
  end
145
184
 
185
+ # @see https://developers.podio.com/doc/items/get-items-as-xlsx-63233
146
186
  def xlsx(app_id, options={})
147
187
  response = Podio.connection.get { |req|
148
188
  req.url("/item/app/#{app_id}/xlsx/", options)
@@ -150,6 +190,7 @@ class Podio::Item < ActivePodio::Base
150
190
  response.body
151
191
  end
152
192
 
193
+ # @see https://developers.podio.com/doc/items/export-items-4235696
153
194
  def export(app_id, exporter, options={})
154
195
  response = Podio.connection.post { |req|
155
196
  req.url "/item/app/#{app_id}/export/#{exporter}"
@@ -157,37 +198,62 @@ class Podio::Item < ActivePodio::Base
157
198
  }.body
158
199
  end
159
200
 
201
+ # @see https://developers.podio.com/doc/items/find-items-by-field-and-title-22485
160
202
  def search_field(field_id, options={})
161
203
  list Podio.connection.get { |req|
162
204
  req.url("/item/field/#{field_id}/find", options)
163
205
  }.body
164
206
  end
165
207
 
166
- def create(app_id, attributes)
208
+ # @see https://developers.podio.com/doc/items/add-new-item-22362
209
+ def create(app_id, attributes, options={})
167
210
  response = Podio.connection.post do |req|
168
- req.url "/item/app/#{app_id}/"
211
+ req.url("/item/app/#{app_id}/", options)
169
212
  req.body = attributes
170
213
  end
171
214
 
172
- response.body['item_id']
215
+ member response.body
173
216
  end
174
217
 
175
- def update(id, attributes)
218
+ # @see https://developers.podio.com/doc/items/update-item-22363
219
+ def update(id, attributes, options={})
176
220
  response = Podio.connection.put do |req|
177
- req.url "/item/#{id}"
221
+ req.url("/item/#{id}", options)
178
222
  req.body = attributes
179
223
  end
180
224
  response.status
181
225
  end
182
226
 
227
+ # @see https://developers.podio.com/doc/items/bulk-delete-items-19406111
228
+ def bulk_delete(app_id, attributes)
229
+ response = Podio.connection.post do |req|
230
+ req.url("/item/app/#{app_id}/delete")
231
+ req.body = attributes
232
+ end
233
+
234
+ response.body
235
+ end
236
+
237
+ # @see https://developers.podio.com/doc/items/delete-item-22364
183
238
  def delete(id)
184
239
  Podio.connection.delete("/item/#{id}").body
185
240
  end
186
241
 
242
+ # @see https://developers.podio.com/doc/items/delete-item-reference-7302326
187
243
  def delete_ref(id)
188
244
  Podio.connection.delete("/item/#{id}/ref").body
189
245
  end
190
246
 
247
+ # @see https://developers.podio.com/doc/items/clone-item-37722742
248
+ def clone(item_id, options={})
249
+ response = Podio.connection.post do |req|
250
+ req.url("/item/#{item_id}/clone", options)
251
+ end
252
+
253
+ response.body['item_id']
254
+ end
255
+
256
+ # @see https://developers.podio.com/doc/items/set-participation-7156154
191
257
  def set_participation(id, status)
192
258
  response = Podio.connection.put do |req|
193
259
  req.url "/item/#{id}/participation"
@@ -209,6 +275,7 @@ class Podio::Item < ActivePodio::Base
209
275
  member response.body
210
276
  end
211
277
 
278
+ # @see https://developers.podio.com/doc/items/get-meeting-url-14763260
212
279
  def find_meeting_url(id)
213
280
  response = Podio.connection.get { |req|
214
281
  req.url("/item/#{id}/meeting/url")
@@ -1,20 +1,24 @@
1
+ # @see https://developers.podio.com/doc/items
1
2
  class Podio::ItemDiff < ActivePodio::Base
2
3
  property :field_id, :integer
3
4
  property :type, :string
4
- property :external_id, :integer
5
+ property :external_id, :string
5
6
  property :label, :string
7
+ property :config, :hash
6
8
  property :from, :array
7
9
  property :to, :array
8
-
10
+
9
11
  alias_method :id, :field_id
10
-
12
+
11
13
  class << self
14
+ # @see https://developers.podio.com/doc/items/get-item-revision-difference-22374
12
15
  def find_by_item_and_revisions(item_id, revision_from_id, revision_to_id)
13
16
  list Podio.connection.get("/item/#{item_id}/revision/#{revision_from_id}/#{revision_to_id}").body
14
17
  end
15
-
18
+
19
+ # @see https://developers.podio.com/doc/items/revert-item-revision-953195
16
20
  def revert(item_id, revision_id)
17
21
  Podio.connection.delete("/item/#{item_id}/revision/#{revision_id}").body
18
22
  end
19
23
  end
20
- end
24
+ end
@@ -1,3 +1,4 @@
1
+ # @see https://developers.podio.com/doc/items
1
2
  class Podio::ItemField < ActivePodio::Base
2
3
  property :field_id, :integer
3
4
  property :type, :string
@@ -9,16 +10,24 @@ class Podio::ItemField < ActivePodio::Base
9
10
  alias_method :id, :field_id
10
11
 
11
12
  class << self
12
- def update(item_id, field_id, values)
13
+ # @see https://developers.podio.com/doc/items/update-item-field-values-22367
14
+ def update(item_id, field_id, values, options = {})
13
15
  response = Podio.connection.put do |req|
14
- req.url "/item/#{item_id}/value/#{field_id}"
16
+ req.url("/item/#{item_id}/value/#{field_id}", options)
15
17
  req.body = values
16
18
  end
17
19
  response.body
18
20
  end
19
21
 
22
+ # @see https://developers.podio.com/doc/calendar/get-item-field-calendar-as-ical-10195681
20
23
  def ical_entry(item_id, field_id)
21
24
  Podio.connection.get("/calendar/item/#{item_id}/field/#{field_id}/ics/").body
22
25
  end
26
+
27
+ # @see https://developers.podio.com/doc/items/get-field-ranges-24242866
28
+ def get_field_ranges(field_id)
29
+ Podio.connection.get("/item/field/#{field_id}/range").body
30
+ end
31
+
23
32
  end
24
33
  end
@@ -1,3 +1,4 @@
1
+ # @see https://developers.podio.com/doc/items
1
2
  class Podio::ItemRevision < ActivePodio::Base
2
3
  property :revision, :integer
3
4
  property :app_revision, :integer
@@ -5,14 +6,16 @@ class Podio::ItemRevision < ActivePodio::Base
5
6
 
6
7
  has_one :created_by, :class => 'ByLine'
7
8
  has_one :created_via, :class => 'Via'
8
-
9
+
9
10
  class << self
11
+ # @see https://developers.podio.com/doc/items/get-item-revision-22373
10
12
  def find(item_id, revision_id)
11
13
  member Podio.connection.get("/item/#{item_id}/revision/#{revision_id}").body
12
14
  end
13
15
 
16
+ # @see https://developers.podio.com/doc/items/get-item-revisions-22372
14
17
  def find_all_by_item_id(item_id)
15
18
  list Podio.connection.get("/item/#{item_id}/revision/").body
16
19
  end
17
20
  end
18
- end
21
+ end
@@ -1,5 +1,6 @@
1
1
  class Podio::LinkedAccount < ActivePodio::Base
2
2
  property :linked_account_id, :integer
3
+ property :status, :string
3
4
  property :label, :string
4
5
  property :provider, :string
5
6
  property :provider_humanized_name, :string
@@ -0,0 +1,61 @@
1
+ class Podio::Live < ActivePodio::Base
2
+ property :status, :string
3
+ property :presence, :hash
4
+ property :provider, :string
5
+ property :push, :hash
6
+ property :live_id, :integer
7
+ property :settings, :hash
8
+ property :ref, :hash
9
+
10
+ class << self
11
+
12
+ # @see https://hoist.podio.com/api/item/45673217
13
+ def create(ref_type, ref_id, attributes)
14
+ response = Podio.connection.post do |req|
15
+ req.url "/live/#{ref_type}/#{ref_id}"
16
+ req.body = attributes
17
+ end
18
+
19
+ member response.body
20
+ end
21
+
22
+ def update(id, attributes)
23
+ response = Podio.connection.put do |req|
24
+ req.url "/live/#{id}/settings"
25
+ req.body = attributes
26
+ end
27
+ response.status
28
+ end
29
+
30
+ def delete(id)
31
+ Podio.connection.delete("/live/#{id}").body
32
+ end
33
+
34
+ def accept(id)
35
+ Podio.connection.post("/live/#{id}/accept").status
36
+ end
37
+
38
+ def decline(id)
39
+ Podio.connection.post("/live/#{id}/decline").status
40
+ end
41
+
42
+ def authorize(attributes)
43
+ response = Podio.connection.post do |req|
44
+ req.url "/live/omega/authorize"
45
+ req.body = attributes
46
+ end
47
+
48
+ response.body
49
+ end
50
+
51
+ def authorize_all
52
+ Podio.connection.post("/live/omega/authorize/all").body
53
+ end
54
+
55
+ def get_servers
56
+ Podio.connection.get("/live/servers").body
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,28 @@
1
+ class Podio::NetPromoterScore < ActivePodio::Base
2
+ property :nps_id, :integer
3
+ property :user_id, :integer
4
+ property :created_on, :datetime
5
+ property :created_via, :integer
6
+ property :score, :integer
7
+ property :feedback, :string
8
+ property :country_code, :string
9
+
10
+ alias_method :id, :nps_id
11
+
12
+ class << self
13
+ def create(attributes=[])
14
+ member Podio.connection.post { |req|
15
+ req.url("/nps/")
16
+ req.body = attributes
17
+ }.body
18
+ end
19
+
20
+ def update(nps_id, attributes)
21
+ member Podio.connection.put { |req|
22
+ req.url("/nps/#{nps_id}")
23
+ req.body = attributes
24
+ }.body
25
+ end
26
+ end
27
+
28
+ end