social_plus-web_api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 478a90847f6f39922aa12fc9f1712da864e5af9d
4
+ data.tar.gz: ccfa4efaede205efb439c1886773b1f956f167a1
5
+ SHA512:
6
+ metadata.gz: f0775913c356fc0cb0df82688633624e516d4efbf0f1cd2494ce06b56e02f84b5a986d0cec74116af1c24a762f7e2eccc38b2f0aed7a80fa48150cf15469b0b3
7
+ data.tar.gz: 0ce553e92923ed27fe2bec14264b7afa2297675419fee39ee95f09398d570e11d0b91b60122a124941c1b9acfe2835ad9df0c0889a26c911a3be4c26cbd470a4
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /vendor/bundle/
16
+ /bin/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
@@ -0,0 +1,75 @@
1
+ AllCops:
2
+ Exclude:
3
+ - bin/*
4
+ - db/schema.rb
5
+ - vendor/**/*
6
+
7
+ #Metrics/AbcSize:
8
+ # Max: 52
9
+
10
+ Metrics/LineLength:
11
+ Max: 192
12
+ AllowURI: true
13
+ URISchemes:
14
+ - http
15
+ - https
16
+
17
+ Style/MultilineOperationIndentation:
18
+ EnforcedStyle: indented
19
+
20
+ Style/NumericLiterals:
21
+ MinDigits: 6
22
+
23
+ Style/SpaceAroundEqualsInParameterDefault:
24
+ EnforcedStyle: no_space
25
+
26
+ Style/MethodCalledOnDoEndBlock:
27
+ Enabled: true
28
+
29
+ Style/SymbolArray:
30
+ Enabled: true
31
+
32
+ Style/ExtraSpacing:
33
+ Enabled: true
34
+
35
+ Style/BlockDelimiters:
36
+ EnforcedStyle: semantic
37
+
38
+ Style/FormatString:
39
+ Enabled: false
40
+
41
+ Style/PercentLiteralDelimiters:
42
+ Enabled: false
43
+
44
+ Style/RescueModifier:
45
+ Enabled: false
46
+
47
+ Style/SignalException:
48
+ Enabled: false
49
+
50
+ Style/SpaceInsideBrackets:
51
+ Enabled: false
52
+
53
+ Style/UnneededPercentQ:
54
+ Enabled: false
55
+
56
+ Style/CollectionMethods:
57
+ Enabled: false
58
+
59
+ Style/Encoding:
60
+ Enabled: false
61
+
62
+ Style/InlineComment:
63
+ Enabled: false
64
+
65
+ Style/BlockDelimiters:
66
+ Enabled: false
67
+
68
+ Style/StringLiterals:
69
+ Enabled: false
70
+
71
+ Style/ExtraSpacing:
72
+ Enabled: false
73
+
74
+ Style/GlobalVars:
75
+ Enabled: false
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.0
5
+ - 2.3.0
6
+ - 2.4.0
7
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in social_plus-web_api.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 feedforce Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ # SocialPlus::WebApi
2
+
3
+ [![Travis Status](https://img.shields.io/travis/feedforce/social_plus-web_api.svg?style=flat-square)][travisci]
4
+ [![License](https://img.shields.io/github/license/feedforce/social_plus-web_api.svg?style=flat-square)][license]
5
+ [![Gem](https://img.shields.io/gem/v/social_plus-web_api.svg?style=flat-square)][gem-link]
6
+
7
+ [travisci]: https://travis-ci.org/feedforce/social_plus-web_api
8
+ [license]: https://github.com/feedforce/social_plus-web_api/blob/master/LICENSE.txt
9
+ [gem-link]: http://badge.fury.io/rb/social_plus-web_api
10
+
11
+ This gem provides fundamental access to Social Plus's Web API.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'social_plus-web_api'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install social_plus-web_api
28
+
29
+ ## Usage
30
+
31
+ Instantiate an instance of {SocialPlus::WebApi::Client} with a valid API key.
32
+
33
+ ```ruby
34
+ client = SocialPlus::WebApi::Client.new(API_KEY)
35
+ ```
36
+
37
+ ### API access via GET method
38
+
39
+ ```ruby
40
+ begin
41
+ result = client.execute(:api_name, arguments_as_hash)
42
+ rescue SocialPlus::WebApi::ApiError => e
43
+ # handle exceptions
44
+ end
45
+ ```
46
+
47
+ ### API access via POST method
48
+
49
+ ```ruby
50
+ begin
51
+ result = client.execute(:api_name, arguments_as_hash, via: :post)
52
+ rescue SocialPlus::WebApi::ApiError => e
53
+ # handle exceptions
54
+ end
55
+ ```
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ Dir[File.join(__dir__, 'lib/tasks/*.rake')].each(&method(:load))
@@ -0,0 +1,12 @@
1
+ # :nodoc:
2
+ module SocialPlus
3
+ # :nodoc:
4
+ module WebApi
5
+ end
6
+ end
7
+
8
+ require 'social_plus/web_api/version'
9
+ require 'social_plus/web_api/api_error'
10
+ require 'social_plus/web_api/client'
11
+ require 'social_plus/web_api/user'
12
+ require 'social_plus/web_api/profile'
@@ -0,0 +1,50 @@
1
+ require 'net/http'
2
+
3
+ # :nodoc:
4
+ module SocialPlus
5
+ # :nodoc:
6
+ module WebApi
7
+ # An Exception class which wraps errors from SocialPlus Web API
8
+ class ApiError < StandardError
9
+ # @overload initialize(error)
10
+ # @param error [Hash] a Hash which represents an API error
11
+ # @option error [String] message the error message
12
+ # @option error [Integer] code the error code
13
+ def initialize(error)
14
+ super(error['message'])
15
+ @code = error['code']
16
+ end
17
+
18
+ # @return [Integer] the error code (API error code or HTTP status)
19
+ attr_reader :code
20
+ end
21
+
22
+ require 'social_plus/web_api/invalid_token'
23
+ require 'social_plus/web_api/http_response_error'
24
+
25
+ class ApiError < StandardError
26
+ EXCEPTION_CLASSES = Hash.new(ApiError).tap do |exception_classes|
27
+ exception_classes[4] = InvalidToken
28
+ end
29
+
30
+ def self.exception_from_api_result(response, result)
31
+ if social_plus_error?(response, result)
32
+ error = result['error']
33
+ raise EXCEPTION_CLASSES[error['code']], error
34
+ else
35
+ raise HttpResponseError, response
36
+ end
37
+ end
38
+
39
+ private
40
+ def self.social_plus_error?(response, result)
41
+ case response
42
+ when Net::HTTPServerError, Net::HTTPClientError
43
+ result.key?('error') && %w(message code).all? { |key| result['error'].key?(key) }
44
+ else
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,85 @@
1
+ require 'json'
2
+ require 'uri'
3
+ require 'net/https'
4
+
5
+ require 'active_support/core_ext/object/to_query'
6
+ require 'active_support/core_ext/hash/except'
7
+
8
+ require 'social_plus/web_api/api_error'
9
+ require 'social_plus/web_api/invalid_token'
10
+ require 'social_plus/web_api/version'
11
+
12
+ # :nodoc:
13
+ module SocialPlus
14
+ # :nodoc:
15
+ module WebApi
16
+ # A class which wraps calls to Social Plus Web API
17
+ class Client
18
+ API_KEY_RE = /\A[0-9a-f]{40}\z/
19
+ private_constant :API_KEY_RE
20
+
21
+ # @param [String] api_key A Social Plus API key
22
+ # @raise [ArgumentError] when `api_key` is invalid
23
+ def initialize(api_key)
24
+ raise ArgumentError, 'invalid API key' unless API_KEY_RE =~ api_key
25
+ @api_key = api_key.freeze
26
+ freeze
27
+ end
28
+
29
+ # Executes a Social Plus Web API
30
+ #
31
+ # @param [String, Symbol] method An API method name
32
+ # @param [Hash] parameters Parameters to API except `key`
33
+ # @option parameters [Symbol] :via HTTP method(default `:get`)
34
+ # @return [Hash] a Hash generated by parsing the JSON returned from the API call, except `status`,
35
+ # just `{}` on parsing failure
36
+ # @raise [ApiError] when the API returns a status other than 200 OK
37
+ def execute(method, parameters={})
38
+ parameters = parameters.with_indifferent_access
39
+ http_method = parameters.delete(:via) || :get
40
+ response = request(http_method, method, parameters.merge(key: @api_key))
41
+ result = parse_as_json(response.body)
42
+
43
+ ApiError.exception_from_api_result(response, result) unless response.is_a?(Net::HTTPOK)
44
+
45
+ result.except('status')
46
+ end
47
+
48
+ SOCIAL_PLUS_FQDN = URI('https://api.socialplus.jp/')
49
+ private_constant :SOCIAL_PLUS_FQDN
50
+
51
+ USER_AGENT = 'SocialPlus Web API gem/%s' % VERSION
52
+ private_constant :USER_AGENT
53
+
54
+ private
55
+ def request(http_method, api_method, parameters)
56
+ uri = request_uri(api_method)
57
+ request = send("create_#{http_method.downcase}_request", uri.path, parameters)
58
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
59
+ http.request(request)
60
+ end
61
+ end
62
+
63
+ def create_get_request(path, parameters)
64
+ Net::HTTP::Get.new(path + '?' + parameters.to_query, 'User-Agent' => USER_AGENT)
65
+ end
66
+
67
+ def create_post_request(path, parameters)
68
+ Net::HTTP::Post.new(path, 'User-Agent' => USER_AGENT).tap { |request| request.body = parameters.to_query }
69
+ end
70
+
71
+ def request_uri(method)
72
+ SOCIAL_PLUS_FQDN.dup.tap do |uri|
73
+ uri.path = '/api/%s' % method
74
+ end
75
+ end
76
+
77
+ def parse_as_json(json_text)
78
+ json_text ||= '{}'
79
+ JSON.parse(json_text)
80
+ rescue JSON::ParserError
81
+ {}
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,18 @@
1
+ # :nodoc:
2
+ module SocialPlus
3
+ # :nodoc:
4
+ module WebApi
5
+ # An Exception class raised when SocialPlus Web API reports http response error
6
+ #
7
+ class HttpResponseError < ApiError
8
+ # @overload initialize(response)
9
+ # @param response [Net::HTTPResponse] HTTP Response (except 200 OK)
10
+ def initialize(response)
11
+ super(
12
+ 'message' => response.message,
13
+ 'code' => response.code.to_i
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ # :nodoc:
2
+ module SocialPlus
3
+ # :nodoc:
4
+ module WebApi
5
+ # An Exception class raised when SocialPlus Web API reports invalid token
6
+ class InvalidToken < ApiError; end
7
+ end
8
+ end
@@ -0,0 +1,55 @@
1
+ # :nodoc:
2
+ module SocialPlus
3
+ # :nodoc:
4
+ module WebApi
5
+ PREFECTURES = {
6
+ 1 => '北海道',
7
+ 2 => '青森県',
8
+ 3 => '岩手県',
9
+ 4 => '宮城県',
10
+ 5 => '秋田県',
11
+ 6 => '山形県',
12
+ 7 => '福島県',
13
+ 8 => '茨城県',
14
+ 9 => '栃木県',
15
+ 10 => '群馬県',
16
+ 11 => '埼玉県',
17
+ 12 => '千葉県',
18
+ 13 => '東京都',
19
+ 14 => '神奈川県',
20
+ 15 => '新潟県',
21
+ 16 => '富山県',
22
+ 17 => '石川県',
23
+ 18 => '福井県',
24
+ 19 => '山梨県',
25
+ 20 => '長野県',
26
+ 21 => '岐阜県',
27
+ 22 => '静岡県',
28
+ 23 => '愛知県',
29
+ 24 => '三重県',
30
+ 25 => '滋賀県',
31
+ 26 => '京都府',
32
+ 27 => '大阪府',
33
+ 28 => '兵庫県',
34
+ 29 => '奈良県',
35
+ 30 => '和歌山県',
36
+ 31 => '鳥取県',
37
+ 32 => '島根県',
38
+ 33 => '岡山県',
39
+ 34 => '広島県',
40
+ 35 => '山口県',
41
+ 36 => '徳島県',
42
+ 37 => '香川県',
43
+ 38 => '愛媛県',
44
+ 39 => '高知県',
45
+ 40 => '福岡県',
46
+ 41 => '佐賀県',
47
+ 42 => '長崎県',
48
+ 43 => '熊本県',
49
+ 44 => '大分県',
50
+ 45 => '宮崎県',
51
+ 46 => '鹿児島県',
52
+ 47 => '沖縄県'
53
+ }
54
+ end
55
+ end
@@ -0,0 +1,196 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/object/inclusion'
4
+ require 'active_support/core_ext/object/try'
5
+ require 'active_support/core_ext/string/conversions'
6
+
7
+ require 'social_plus/web_api/prefectures'
8
+
9
+ # :nodoc:
10
+ module SocialPlus
11
+ # :nodoc:
12
+ module WebApi
13
+ # rubocop: disable Metrics/ClassLength, Metrics/AbcSize
14
+
15
+ # A class which represents authenticated user's information(profile and email) obtained from Social Plus
16
+ class Profile
17
+ # rubocop: disable Metrics/MethodLength
18
+
19
+ # @param [Hash<String,Object>] params a user's information obtained from Social Plus.
20
+ # @option params [Hash] "profile" profile of a user
21
+ # @option params [Array] "email" email addresses of a user
22
+
23
+ def initialize(params)
24
+ params = params.with_indifferent_access
25
+
26
+ profile = params[:profile]
27
+ @full_name, @given_name, @family_name, @full_name_kana, @given_name_kana, @family_name_kana = extract_names(profile).freeze
28
+ @zip_code = profile[:postal_code]
29
+ @prefecture, @prefecture_name, @city, @location = extract_location(profile).freeze
30
+ @gender = profile[:gender].in?([1, 2]) ? profile[:gender] : nil
31
+ @urls = profile[:uri].respond_to?(:map) ? filter_http_urls(profile[:uri]) : []
32
+ @birthday = /\A\d{4}-\d{2}-\d{2}\z/ =~ profile[:birthday] ? profile[:birthday].try(:to_date).freeze : nil
33
+
34
+ emails = params[:email]
35
+ @emails = emails && emails.respond_to?(:map) ? emails.map { |email| email[:email].freeze }.compact.freeze : []
36
+
37
+ freeze
38
+ end
39
+ # rubocop: enable Metrics/MethodLength
40
+
41
+ # @return [String] Returns the user's full name
42
+ attr_reader :full_name
43
+
44
+ # @return [String] Returns the user's family name
45
+ attr_reader :family_name
46
+
47
+ # @return [String] Returns the user's given name
48
+ attr_reader :given_name
49
+
50
+ # @return [String] Returns the user's full name in kana
51
+ attr_reader :full_name_kana
52
+
53
+ # @return [String] Returns the user's family name in kana
54
+ attr_reader :family_name_kana
55
+
56
+ # @return [String] Returns the user's given name in kana
57
+ attr_reader :given_name_kana
58
+
59
+ # @return [String] Returns the zip code of user's address
60
+ attr_reader :zip_code
61
+
62
+ # @return [Integer] Returns the prefecture code of user's address
63
+ attr_reader :prefecture
64
+
65
+ # @return [String] Returns the name of prefecture of user's address
66
+ attr_reader :prefecture_name
67
+
68
+ # @return [Integer] Returns the city code of user's address
69
+ attr_reader :city
70
+
71
+ # @return [String] Returns the rest of user's address
72
+ attr_reader :location
73
+
74
+ # @return [Date] Returns the user's birthday
75
+ attr_reader :birthday
76
+
77
+ # @return [Array<URI>] Returns the user's URLs
78
+ attr_reader :urls
79
+
80
+ # @return [Integer] Returns the user's gender
81
+ attr_reader :gender
82
+
83
+ # @return [Array<String>] Returns the user's E-Mail addresses
84
+ attr_reader :emails
85
+
86
+ private
87
+
88
+ def filter_http_urls(uris)
89
+ uris.map { |uri_string| URI(uri_string).freeze rescue nil }.select { |url| url.try(:scheme).in?(%w(http https)) }.freeze
90
+ end
91
+
92
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
93
+
94
+ # @return [Array] Returns Array of full name, given name, family name, full name in kana, given name in kana and family name in kana
95
+ def extract_names(profile)
96
+ full_name_kanji = profile[:full_name_kanji] || ''
97
+ last_name_kanji = profile[:last_name_kanji] || ''
98
+ first_name_kanji = profile[:first_name_kanji] || ''
99
+
100
+ full_name_kana = profile[:full_name_kana] || ''
101
+ last_name_kana = profile[:last_name_kana] || ''
102
+ first_name_kana = profile[:first_name_kana] || ''
103
+
104
+ full_name = profile[:full_name] || ''
105
+ last_name = profile[:last_name] || ''
106
+ first_name = profile[:first_name] || ''
107
+ user_name = profile[:user_name] || ''
108
+
109
+ # NOTE: "".present? #=> false
110
+
111
+ # *: set
112
+ # -: not set
113
+ # : no care
114
+ # full(K) | last(K) | first(K) | full | last | first | user |
115
+ # #1 ******* | ******* | ******** | | | | |
116
+ # #1 ******* | ******* | -------- | | | | |
117
+ # #1 ******* | ------- | ******** | | | | |
118
+ # #1' | | | **** | **** | ***** | |
119
+ # #1' | | | **** | **** | ----- | |
120
+ # #1' | | | **** | ---- | ***** | |
121
+ # #2 ------- | ******* | ******** | | | | |
122
+ # #2 ------- | ******* | -------- | | | | |
123
+ # #2 ------- | ------- | ******** | | | | |
124
+ # #2' | | | ---- | **** | ***** | |
125
+ # #2' | | | ---- | **** | ----- | |
126
+ # #2' | | | ---- | ---- | ***** | |
127
+ # #3 ******* | ------- | -------- | | | | |
128
+ # #3' | | | **** | ---- | ----- | |
129
+ # #4 ------- | ------- | -------- | ---- | ---- | ----- | **** |
130
+ # #5 ------- | ------- | -------- | ---- | ---- | ----- | ---- |
131
+
132
+ # #1
133
+ if full_name_kanji.present? && (last_name_kanji.present? || first_name_kanji.present?)
134
+ names = [ full_name_kanji, first_name_kanji, last_name_kanji ]
135
+ # #1'
136
+ elsif full_name.present? && (last_name.present? || first_name.present?)
137
+ names = [ full_name, first_name, last_name ]
138
+ # #2
139
+ elsif last_name_kanji.present? || first_name_kanji.present?
140
+ names = [ [last_name_kanji, first_name_kanji].join(' ').strip, first_name_kanji, last_name_kanji ]
141
+ # #2'
142
+ elsif last_name.present? || first_name.present?
143
+ names = [ [last_name, first_name].join(' ').strip, first_name, last_name ]
144
+ # #3
145
+ elsif full_name_kanji.present?
146
+ names = [ full_name_kanji, '', full_name_kanji ]
147
+ # #3'
148
+ elsif full_name.present?
149
+ names = [ full_name, '', full_name ]
150
+ # #4
151
+ elsif user_name.present?
152
+ names = [ user_name, '', '' ]
153
+ # #5
154
+ else
155
+ names = [ '', '', '' ]
156
+ end
157
+
158
+ if full_name_kana.present? && (last_name_kana.present? || first_name_kana.present?)
159
+ kana = [ full_name_kana, first_name_kana, last_name_kana ]
160
+ elsif last_name_kana.present? || first_name_kana.present?
161
+ kana = [ [last_name_kana, first_name_kana].join(' ').strip, first_name_kana, last_name_kana ]
162
+ elsif full_name_kana.present?
163
+ kana = [ full_name_kana, '', full_name_kana ]
164
+ else
165
+ kana = [ '', '', '' ]
166
+ end
167
+
168
+ names + kana
169
+ end
170
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
171
+
172
+ # @return [Array] Returns prefecture code, prefecture_name, city code and location(rest of the address)
173
+ def extract_location(profile)
174
+ location = profile[:location].presence || ''
175
+
176
+ city_code = city_code(profile)
177
+ prefecture_code = city_code ? city_code_to_prefecture_code(city_code) : nil
178
+ return [ nil, '', nil, location ] unless city_code && prefecture_code
179
+
180
+ prefecture_name = PREFECTURES[prefecture_code]
181
+ location_without_prefecture = location.sub(/\A#{prefecture_name}/, '')
182
+ [ prefecture_code, prefecture_name, city_code, location_without_prefecture ]
183
+ end
184
+
185
+ def city_code(profile)
186
+ profile[:location_jis_id].to_i.presence
187
+ end
188
+
189
+ def city_code_to_prefecture_code(city_code)
190
+ code = city_code / 1000
191
+ PREFECTURES.key?(code) ? code : nil
192
+ end
193
+ end
194
+ # rubocop: enable Metrics/ClassLength
195
+ end
196
+ end