linkedin 0.4.6 → 0.4.7

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/.yardopts +7 -0
  4. data/{changelog.markdown → CHANGELOG.md} +5 -1
  5. data/EXAMPLES.md +199 -0
  6. data/Gemfile +4 -0
  7. data/README.md +43 -0
  8. data/Rakefile +2 -7
  9. data/lib/linked_in/api.rb +34 -2
  10. data/lib/linked_in/api/communications.rb +44 -0
  11. data/lib/linked_in/api/companies.rb +128 -0
  12. data/lib/linked_in/api/groups.rb +115 -0
  13. data/lib/linked_in/api/jobs.rb +64 -0
  14. data/lib/linked_in/api/people.rb +72 -0
  15. data/lib/linked_in/api/query_helpers.rb +86 -0
  16. data/lib/linked_in/api/share_and_social_stream.rb +133 -0
  17. data/lib/linked_in/client.rb +7 -2
  18. data/lib/linked_in/errors.rb +12 -2
  19. data/lib/linked_in/mash.rb +31 -4
  20. data/lib/linked_in/search.rb +16 -1
  21. data/lib/linked_in/version.rb +1 -1
  22. data/lib/linkedin.rb +4 -1
  23. data/linkedin.gemspec +5 -3
  24. data/spec/cases/api_spec.rb +15 -5
  25. data/spec/cases/mash_spec.rb +30 -2
  26. data/spec/cases/oauth_spec.rb +5 -6
  27. data/spec/cases/search_spec.rb +53 -23
  28. data/spec/fixtures/cassette_library/LinkedIn_Api/Company_API.yml +3 -3
  29. data/spec/fixtures/cassette_library/LinkedIn_Api/Company_API/should_load_correct_company_data.yml +81 -0
  30. data/spec/fixtures/cassette_library/LinkedIn_Client/{_authorize_from_request.yml → _authorize_from_request/should_return_a_valid_access_token.yml} +0 -0
  31. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/{with_a_callback_url.yml → with_a_callback_url/should_return_a_valid_access_token.yml} +0 -0
  32. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/{with_default_options.yml → with_default_options/should_return_a_valid_request_token.yml} +0 -0
  33. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/{by_company_name_option.yml → by_company_name_option/should_perform_a_search.yml} +8 -9
  34. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_email_address/should_perform_a_people_search.yml +57 -0
  35. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/{by_first_name_and_last_name_options.yml → by_first_name_and_last_name_options/should_perform_a_search.yml} +15 -15
  36. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options_with_fields/should_perform_a_search.yml +114 -0
  37. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/{by_keywords_string_parameter.yml → by_keywords_string_parameter/should_perform_a_search.yml} +8 -9
  38. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_multiple_email_address/should_perform_a_multi-email_search.yml +59 -0
  39. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/{by_single_keywords_option.yml → by_single_keywords_option/should_perform_a_search.yml} +8 -9
  40. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/{by_single_keywords_option_with_pagination.yml → by_single_keywords_option_with_pagination/should_perform_a_search.yml} +1 -3
  41. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/email_search_returns_unauthorized/should_raise_an_unauthorized_error.yml +59 -0
  42. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_options_with_fields/should_perform_a_search.yml +43 -0
  43. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/{by_keywords_string_parameter.yml → by_keywords_string_parameter/should_perform_a_company_search.yml} +19 -19
  44. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/{by_single_keywords_option.yml → by_single_keywords_option/should_perform_a_company_search.yml} +19 -19
  45. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/{by_single_keywords_option_with_facets_to_return.yml → by_single_keywords_option_with_facets_to_return/should_return_a_facet.yml} +14 -14
  46. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/{by_single_keywords_option_with_pagination.yml → by_single_keywords_option_with_pagination/should_perform_a_search.yml} +15 -15
  47. data/spec/helper.rb +9 -5
  48. metadata +76 -61
  49. data/.document +0 -5
  50. data/README.markdown +0 -84
  51. data/examples/authenticate.rb +0 -26
  52. data/examples/communication.rb +0 -7
  53. data/examples/network.rb +0 -12
  54. data/examples/profile.rb +0 -18
  55. data/examples/sinatra.rb +0 -77
  56. data/examples/status.rb +0 -6
  57. data/lib/linked_in/api/query_methods.rb +0 -176
  58. data/lib/linked_in/api/update_methods.rb +0 -85
  59. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token.yml +0 -37
  60. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options_with_fields.yml +0 -112
  61. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_options_with_fields.yml +0 -232
@@ -5,8 +5,13 @@ module LinkedIn
5
5
  class Client
6
6
  include Helpers::Request
7
7
  include Helpers::Authorization
8
- include Api::QueryMethods
9
- include Api::UpdateMethods
8
+ include Api::QueryHelpers
9
+ include Api::People
10
+ include Api::Groups
11
+ include Api::Companies
12
+ include Api::Jobs
13
+ include Api::ShareAndSocialStream
14
+ include Api::Communications
10
15
  include Search
11
16
 
12
17
  attr_reader :consumer_token, :consumer_secret, :consumer_options
@@ -8,12 +8,22 @@ module LinkedIn
8
8
  end
9
9
  end
10
10
 
11
+ # Raised when a 401 response status code is received
11
12
  class UnauthorizedError < LinkedInError; end
13
+
14
+ # Raised when a 400 response status code is received
12
15
  class GeneralError < LinkedInError; end
16
+
17
+ # Raised when a 403 response status code is received
13
18
  class AccessDeniedError < LinkedInError; end
14
19
 
15
- class UnavailableError < StandardError; end
16
- class InformLinkedInError < StandardError; end
20
+ # Raised when a 404 response status code is received
17
21
  class NotFoundError < StandardError; end
22
+
23
+ # Raised when a 500 response status code is received
24
+ class InformLinkedInError < StandardError; end
25
+
26
+ # Raised when a 502 or 503 response status code is received
27
+ class UnavailableError < StandardError; end
18
28
  end
19
29
  end
@@ -2,15 +2,23 @@ require 'hashie'
2
2
  require 'multi_json'
3
3
 
4
4
  module LinkedIn
5
+
6
+ # The generalized pseudo-object that is returned for all query
7
+ # requests.
5
8
  class Mash < ::Hashie::Mash
6
9
 
7
- # a simple helper to convert a json string to a Mash
10
+ # Convert a json string to a Mash
11
+ #
12
+ # @param [String] json_string
13
+ # @return [LinkedIn::Mash]
8
14
  def self.from_json(json_string)
9
15
  result_hash = ::MultiJson.decode(json_string)
10
16
  new(result_hash)
11
17
  end
12
18
 
13
- # returns a Date if we have year, month and day, and no conflicting key
19
+ # Returns a Date if we have year, month and day, and no conflicting key
20
+ #
21
+ # @return [Date]
14
22
  def to_date
15
23
  if !self.has_key?('to_date') && contains_date_fields?
16
24
  Date.civil(self.year, self.month, self.day)
@@ -19,6 +27,20 @@ module LinkedIn
19
27
  end
20
28
  end
21
29
 
30
+ # Returns the id of the object from LinkedIn
31
+ #
32
+ # @return [String]
33
+ def id
34
+ if self['id']
35
+ self['id']
36
+ else
37
+ self['_key']
38
+ end
39
+ end
40
+
41
+ # Convert the 'timestamp' field from a string to a Time object
42
+ #
43
+ # @return [Time]
22
44
  def timestamp
23
45
  value = self['timestamp']
24
46
  if value.kind_of? Integer
@@ -29,6 +51,13 @@ module LinkedIn
29
51
  end
30
52
  end
31
53
 
54
+ # Return the results array from the query
55
+ #
56
+ # @return [Array]
57
+ def all
58
+ super || []
59
+ end
60
+
32
61
  protected
33
62
 
34
63
  def contains_date_fields?
@@ -39,8 +68,6 @@ module LinkedIn
39
68
  # keys are made a little more ruby-ish
40
69
  def convert_key(key)
41
70
  case key.to_s
42
- when '_key'
43
- 'id'
44
71
  when '_total'
45
72
  'total'
46
73
  when 'values'
@@ -1,6 +1,21 @@
1
1
  module LinkedIn
2
2
 
3
3
  module Search
4
+
5
+ # Retrieve search results of the given object type
6
+ #
7
+ # Permissions: (for people search only) r_network
8
+ #
9
+ # @note People Search API is a part of the Vetted API Access Program. You
10
+ # must apply and get approval before using this API
11
+ #
12
+ # @see http://developer.linkedin.com/documents/people-search-api People Search
13
+ # @see http://developer.linkedin.com/documents/job-search-api Job Search
14
+ # @see http://developer.linkedin.com/documents/company-search Company Search
15
+ #
16
+ # @param [Hash] options search input fields
17
+ # @param [String] type type of object to return ('people', 'job' or 'company')
18
+ # @return [LinkedIn::Mash]
4
19
  def search(options={}, type='people')
5
20
 
6
21
  path = "/#{type.to_s}-search"
@@ -53,4 +68,4 @@ module LinkedIn
53
68
  end
54
69
  end
55
70
 
56
- end
71
+ end
@@ -3,7 +3,7 @@ module LinkedIn
3
3
  module VERSION #:nodoc:
4
4
  MAJOR = 0
5
5
  MINOR = 4
6
- PATCH = 6
6
+ PATCH = 7
7
7
  PRE = nil
8
8
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
9
9
  end
@@ -7,15 +7,18 @@ module LinkedIn
7
7
 
8
8
  # config/initializers/linkedin.rb (for instance)
9
9
  #
10
+ # ```ruby
10
11
  # LinkedIn.configure do |config|
11
12
  # config.token = 'consumer_token'
12
13
  # config.secret = 'consumer_secret'
13
14
  # config.default_profile_fields = ['educations', 'positions']
14
15
  # end
15
- #
16
+ # ```
16
17
  # elsewhere
17
18
  #
19
+ # ```ruby
18
20
  # client = LinkedIn::Client.new
21
+ # ```
19
22
  def configure
20
23
  yield self
21
24
  true
@@ -2,12 +2,13 @@
2
2
  require File.expand_path('../lib/linked_in/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
- gem.add_dependency 'hashie', ['>= 1.2', '< 2.1']
5
+ gem.add_dependency 'hashie', '~> 2.0'
6
6
  gem.add_dependency 'multi_json', '~> 1.0'
7
7
  gem.add_dependency 'oauth', '~> 0.4'
8
8
  # gem.add_development_dependency 'json', '~> 1.6'
9
9
  gem.add_development_dependency 'rake', '~> 10'
10
- gem.add_development_dependency 'rdoc', '~> 4.0'
10
+ gem.add_development_dependency 'yard'
11
+ gem.add_development_dependency 'kramdown'
11
12
  gem.add_development_dependency 'rspec', '~> 2.13'
12
13
  gem.add_development_dependency 'simplecov', '~> 0.7'
13
14
  gem.add_development_dependency 'vcr', '~> 2.5'
@@ -18,8 +19,9 @@ Gem::Specification.new do |gem|
18
19
  gem.files = `git ls-files`.split("\n")
19
20
  gem.homepage = 'http://github.com/hexgnu/linkedin'
20
21
  gem.name = 'linkedin'
22
+ gem.licenses = %w[MIT]
21
23
  gem.require_paths = ['lib']
22
- gem.summary = gem.description
24
+ gem.summary = 'This gem interfaces with the Linkedin XML and JSON APis'
23
25
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
26
  gem.version = LinkedIn::VERSION::STRING
25
27
  end
@@ -30,6 +30,12 @@ describe LinkedIn::Api do
30
30
  client.connections.should be_an_instance_of(LinkedIn::Mash)
31
31
  end
32
32
 
33
+ it "should be able to view new connections" do
34
+ modified_since = Time.now.to_i * 1000
35
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/connections?modified=new&modified-since=#{modified_since}").to_return(:body => "{}")
36
+ client.new_connections(modified_since).should be_an_instance_of(LinkedIn::Mash)
37
+ end
38
+
33
39
  it "should be able to view network_updates" do
34
40
  stub_request(:get, "https://api.linkedin.com/v1/people/~/network/updates").to_return(:body => "{}")
35
41
  client.network_updates.should be_an_instance_of(LinkedIn::Mash)
@@ -116,8 +122,7 @@ describe LinkedIn::Api do
116
122
  request_token.should == "request_token"
117
123
  end
118
124
 
119
- context "Company API" do
120
- use_vcr_cassette
125
+ context "Company API", :vcr do
121
126
 
122
127
  it "should be able to view a company profile" do
123
128
  stub_request(:get, "https://api.linkedin.com/v1/companies/id=1586").to_return(:body => "{}")
@@ -194,8 +199,7 @@ describe LinkedIn::Api do
194
199
 
195
200
  end
196
201
 
197
- context "Job API" do
198
- use_vcr_cassette
202
+ context "Job API", :vcr do
199
203
 
200
204
  it "should be able to view a job listing" do
201
205
  stub_request(:get, "https://api.linkedin.com/v1/jobs/id=1586").to_return(:body => "{}")
@@ -227,9 +231,15 @@ describe LinkedIn::Api do
227
231
  client.group_memberships.should be_an_instance_of(LinkedIn::Mash)
228
232
  end
229
233
 
234
+ it "should be able to list suggested groups for a profile" do
235
+ stub_request(:get, "https://api.linkedin.com/v1/people/~/suggestions/groups").to_return(:body => '{"id": "123"}')
236
+ response = client.group_suggestions
237
+ response.id.should == '123'
238
+ end
239
+
230
240
  it "should be able to parse nested fields" do
231
241
  stub_request(:get, "https://api.linkedin.com/v1/people/~/group-memberships:(group:(id,name,small-logo-url,short-description))").to_return(:body => "{}")
232
- client.group_memberships(fields: [{group: ['id', 'name', 'small-logo-url', 'short-description']}]).should be_an_instance_of(LinkedIn::Mash)
242
+ client.group_memberships(:fields => [{:group => ['id', 'name', 'small-logo-url', 'short-description']}]).should be_an_instance_of(LinkedIn::Mash)
233
243
  end
234
244
 
235
245
  it "should be able to join a group" do
@@ -18,6 +18,7 @@ describe LinkedIn::Mash do
18
18
  'firstName' => 'Josh',
19
19
  'LastName' => 'Kalderimis',
20
20
  '_key' => 1234,
21
+ 'id' => 1345,
21
22
  '_total' => 1234,
22
23
  'values' => {},
23
24
  'numResults' => 'total_results'
@@ -29,8 +30,23 @@ describe LinkedIn::Mash do
29
30
  mash.should have_key('last_name')
30
31
  end
31
32
 
32
- it "should convert the key _key to id" do
33
- mash.should have_key('id')
33
+ # this breaks data coming back from linkedIn
34
+ it "converts _key to id if there is an id column" do
35
+ mash._key.should == 1234
36
+ mash.id.should == 1345
37
+ end
38
+
39
+ context 'no collision' do
40
+ let(:mash) {
41
+ LinkedIn::Mash.new({
42
+ '_key' => 1234
43
+ })
44
+
45
+ }
46
+ it 'converts _key to id if there is no collision' do
47
+ mash.id.should == 1234
48
+ mash._key.should == 1234
49
+ end
34
50
  end
35
51
 
36
52
  it "should convert the key _total to total" do
@@ -82,4 +98,16 @@ describe LinkedIn::Mash do
82
98
  end
83
99
  end
84
100
 
101
+ describe "#all" do
102
+ let(:all_mash) do
103
+ LinkedIn::Mash.new({
104
+ :values => nil
105
+ })
106
+ end
107
+
108
+ it "should return an empty array if values is nil due to no results being found for a query" do
109
+ all_mash.all.should == []
110
+ end
111
+ end
112
+
85
113
  end
@@ -100,8 +100,8 @@ describe "LinkedIn::Client" do
100
100
  end
101
101
 
102
102
  describe "#request_token" do
103
- describe "with default options" do
104
- use_vcr_cassette :record => :new_episodes
103
+ vcr_options = { :record => :new_episodes}
104
+ describe "with default options", vcr: vcr_options do
105
105
 
106
106
  it "should return a valid request token" do
107
107
  request_token = client.request_token
@@ -113,8 +113,7 @@ describe "LinkedIn::Client" do
113
113
  end
114
114
  end
115
115
 
116
- describe "with a callback url" do
117
- use_vcr_cassette :record => :new_episodes
116
+ describe "with a callback url", vcr: vcr_options do
118
117
 
119
118
  it "should return a valid access token" do
120
119
  request_token = client.request_token(:oauth_callback => 'http://www.josh.com')
@@ -141,9 +140,9 @@ describe "LinkedIn::Client" do
141
140
  client.authorize_from_request('dummy-token', 'dummy-secret', 'dummy-pin')
142
141
  end
143
142
 
144
- use_vcr_cassette :record => :new_episodes, :match_requests_on => [:uri, :method]
143
+ vcr_options = { :record => :new_episodes, :match_requests_on => [ :uri, :method] }
145
144
 
146
- it "should return a valid access token" do
145
+ it "should return a valid access token", vcr: vcr_options do
147
146
  access_token.should be_a_kind_of Array
148
147
  access_token[0].should be_a_kind_of String
149
148
  access_token[1].should be_a_kind_of String
@@ -16,10 +16,11 @@ describe LinkedIn::Search do
16
16
  client
17
17
  end
18
18
 
19
+ vcr_options = { :record => :new_episodes}
20
+
19
21
  describe "#search_company" do
20
22
 
21
- describe "by keywords string parameter" do
22
- use_vcr_cassette :record => :new_episodes
23
+ describe "by keywords string parameter", vcr: vcr_options do
23
24
 
24
25
  let(:results) do
25
26
  client.search('apple', :company)
@@ -32,8 +33,7 @@ describe LinkedIn::Search do
32
33
  end
33
34
  end
34
35
 
35
- describe "by single keywords option" do
36
- use_vcr_cassette :record => :new_episodes
36
+ describe "by single keywords option", vcr: vcr_options do
37
37
 
38
38
  let(:results) do
39
39
  options = {:keywords => 'apple'}
@@ -47,8 +47,7 @@ describe LinkedIn::Search do
47
47
  end
48
48
  end
49
49
 
50
- describe "by single keywords option with facets to return" do
51
- use_vcr_cassette :record => :new_episodes
50
+ describe "by single keywords option with facets to return", vcr: vcr_options do
52
51
 
53
52
  let(:results) do
54
53
  options = {:keywords => 'apple', :facets => [:industry]}
@@ -60,8 +59,7 @@ describe LinkedIn::Search do
60
59
  end
61
60
  end
62
61
 
63
- describe "by single keywords option with pagination" do
64
- use_vcr_cassette :record => :new_episodes
62
+ describe "by single keywords option with pagination", vcr: vcr_options do
65
63
 
66
64
  let(:results) do
67
65
  options = {:keywords => 'apple', :start => 5, :count => 5}
@@ -77,8 +75,7 @@ describe LinkedIn::Search do
77
75
  end
78
76
  end
79
77
 
80
- describe "by keywords options with fields" do
81
- use_vcr_cassette :record => :new_episodes
78
+ describe "by keywords options with fields", vcr: vcr_options do
82
79
 
83
80
  let(:results) do
84
81
  fields = [{:companies => [:id, :name, :industries, :description, :specialties]}, :num_results]
@@ -96,8 +93,7 @@ describe LinkedIn::Search do
96
93
 
97
94
  describe "#search" do
98
95
 
99
- describe "by keywords string parameter" do
100
- use_vcr_cassette :record => :new_episodes
96
+ describe "by keywords string parameter", vcr: vcr_options do
101
97
 
102
98
  let(:results) do
103
99
  client.search('github')
@@ -111,8 +107,7 @@ describe LinkedIn::Search do
111
107
  end
112
108
  end
113
109
 
114
- describe "by single keywords option" do
115
- use_vcr_cassette :record => :new_episodes
110
+ describe "by single keywords option", vcr: vcr_options do
116
111
 
117
112
  let(:results) do
118
113
  client.search(:keywords => 'github')
@@ -126,8 +121,7 @@ describe LinkedIn::Search do
126
121
  end
127
122
  end
128
123
 
129
- describe "by single keywords option with pagination" do
130
- use_vcr_cassette :record => :new_episodes
124
+ describe "by single keywords option with pagination", vcr: vcr_options do
131
125
 
132
126
  let(:results) do
133
127
  client.search(:keywords => 'github', :start => 5, :count => 5)
@@ -141,8 +135,7 @@ describe LinkedIn::Search do
141
135
  end
142
136
  end
143
137
 
144
- describe "by first_name and last_name options" do
145
- use_vcr_cassette :record => :new_episodes
138
+ describe "by first_name and last_name options", vcr: vcr_options do
146
139
 
147
140
  let(:results) do
148
141
  client.search(:first_name => 'Charles', :last_name => 'Garcia')
@@ -156,8 +149,46 @@ describe LinkedIn::Search do
156
149
  end
157
150
  end
158
151
 
159
- describe "by first_name and last_name options with fields" do
160
- use_vcr_cassette :record => :new_episodes
152
+ describe "by email address", vcr: vcr_options do
153
+
154
+ let(:results) do
155
+ fields = ['id']
156
+ client.profile(:email => 'email=yy@zz.com', :fields => fields)
157
+ end
158
+
159
+ it "should perform a people search" do
160
+ results._total.should == 1
161
+ output = results["values"]
162
+ output.each do |record|
163
+ record.id.should == '96GVfLeWjU'
164
+ record._key.should == 'email=yy@zz.com'
165
+ end
166
+ end
167
+ end
168
+
169
+ describe "by multiple email address", vcr: vcr_options do
170
+
171
+ let(:results) do
172
+ fields = ['id']
173
+ client.profile(:email => 'email=yy@zz.com,email=xx@yy.com', :fields => fields)
174
+ end
175
+
176
+ it "should perform a multi-email search" do
177
+ results._total.should == 2
178
+ output = results["values"]
179
+ output.count.should == 2
180
+ end
181
+ end
182
+
183
+ describe "email search returns unauthorized", vcr: vcr_options do
184
+
185
+ it "should raise an unauthorized error" do
186
+ fields = ['id']
187
+ expect {client.profile(:email => 'email=aa@bb.com', :fields => fields)}.to raise_error(LinkedIn::Errors::UnauthorizedError)
188
+ end
189
+ end
190
+
191
+ describe "by first_name and last_name options with fields", vcr: vcr_options do
161
192
 
162
193
  let(:results) do
163
194
  fields = [{:people => [:id, :first_name, :last_name, :public_profile_url, :picture_url]}, :num_results]
@@ -175,8 +206,7 @@ describe LinkedIn::Search do
175
206
  end
176
207
  end
177
208
 
178
- describe "by company_name option" do
179
- use_vcr_cassette :record => :new_episodes
209
+ describe "by company_name option", vcr: vcr_options do
180
210
 
181
211
  let(:results) do
182
212
  client.search(:company_name => 'IBM')
@@ -201,4 +231,4 @@ describe LinkedIn::Search do
201
231
 
202
232
  end
203
233
 
204
- end
234
+ end