sailplay 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +11 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +7 -0
- data/lib/sailplay.rb +109 -0
- data/lib/sailplay/api/base.rb +4 -0
- data/lib/sailplay/api/gift.rb +34 -0
- data/lib/sailplay/api/purchase.rb +40 -0
- data/lib/sailplay/api/user.rb +52 -0
- data/lib/sailplay/client.rb +255 -0
- data/lib/sailplay/configuration.rb +115 -0
- data/lib/sailplay/error.rb +27 -0
- data/lib/sailplay/rails.rb +24 -0
- data/lib/sailplay/rails/client.rb +118 -0
- data/lib/sailplay/response.rb +25 -0
- data/lib/sailplay/version.rb +3 -0
- data/lib/templates/sailplay_client +19 -0
- data/rails/init.rb +1 -0
- data/sailplay.gemspec +34 -0
- data/spec/sailplay/client_spec.rb +123 -0
- data/spec/sailplay/configuration_spec.rb +80 -0
- data/spec/sailplay_spec.rb +5 -0
- data/spec/spec_helper.rb +21 -0
- metadata +197 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Sailplay
|
4
|
+
class Configuration
|
5
|
+
OPTIONS = [:host, :port, :secure, :endpoint, :connection_options, :store_id, :store_key, :store_pin, :logger]
|
6
|
+
|
7
|
+
|
8
|
+
# The host to connect to (defaults to sailplay.ru).
|
9
|
+
attr_accessor :host
|
10
|
+
|
11
|
+
# The port on which Sailplay API server runs (defaults to 443 for secure
|
12
|
+
# connections, 80 for insecure connections).
|
13
|
+
attr_accessor :port
|
14
|
+
|
15
|
+
# Url prefix for API (defaults to /api/v1)
|
16
|
+
attr_accessor :endpoint
|
17
|
+
|
18
|
+
# +true+ for https connections, +false+ for http connections.
|
19
|
+
attr_accessor :secure
|
20
|
+
|
21
|
+
attr_accessor :connection_options
|
22
|
+
attr_accessor :store_id
|
23
|
+
attr_accessor :store_key
|
24
|
+
attr_accessor :store_pin
|
25
|
+
|
26
|
+
attr_accessor :logger
|
27
|
+
|
28
|
+
# JS client configuration
|
29
|
+
attr_accessor :js_api_path
|
30
|
+
|
31
|
+
# one of :top_left, :top_right, :center_left, :center_right, :bottom_left, :bottom_right
|
32
|
+
attr_accessor :js_position
|
33
|
+
|
34
|
+
# {
|
35
|
+
# :buttonText => 'Text', :buttonBgGradient => ["#78bb44", "#367300"], :buttonFontSize => '9px',
|
36
|
+
# :picUrl => "http://some.url", :bgColor => '#ffffff', :borderColor => '#ffffff', :textColor => '#300c2f',
|
37
|
+
# :pointsColor => '#c81750', :buttonTextColor => '#ffffff'
|
38
|
+
# }
|
39
|
+
attr_accessor :skin
|
40
|
+
|
41
|
+
|
42
|
+
DEFAULT_CONNECTION_OPTIONS = {
|
43
|
+
:headers => {
|
44
|
+
:accept => 'application/json',
|
45
|
+
:user_agent => "Sailplay Ruby Gem (#{Sailplay::VERSION})"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
alias_method :secure?, :secure
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@host = 'sailplay.ru'
|
53
|
+
@secure = true
|
54
|
+
@endpoint = '/api/v1'
|
55
|
+
|
56
|
+
@js_api_path = 'static/js/sailplay.js'
|
57
|
+
@js_position = :top_right
|
58
|
+
|
59
|
+
@skin = {}
|
60
|
+
|
61
|
+
@connection_options = DEFAULT_CONNECTION_OPTIONS.dup
|
62
|
+
end
|
63
|
+
|
64
|
+
# Allows config options to be read like a hash
|
65
|
+
#
|
66
|
+
# @param [Symbol] option Key for a given attribute
|
67
|
+
def [](option)
|
68
|
+
send(option)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a hash of all configurable options
|
72
|
+
def to_hash
|
73
|
+
OPTIONS.inject({}) do |hash, option|
|
74
|
+
hash[option.to_sym] = self.send(option)
|
75
|
+
hash
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def port
|
80
|
+
@port || default_port
|
81
|
+
end
|
82
|
+
|
83
|
+
# Determines whether protocol should be "http" or "https".
|
84
|
+
# @return [String] Returns +"http"+ if you've set secure to +false+ in
|
85
|
+
# configuration, and +"https"+ otherwise.
|
86
|
+
def protocol
|
87
|
+
if secure?
|
88
|
+
'https'
|
89
|
+
else
|
90
|
+
'http'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def logger
|
95
|
+
@logger ||= begin
|
96
|
+
log = Logger.new($stdout)
|
97
|
+
log.level = Logger::INFO
|
98
|
+
log
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# Determines what port should we use for sending notices.
|
105
|
+
# @return [Fixnum] Returns 443 if you've set secure to true in your
|
106
|
+
# configuration, and 80 otherwise.
|
107
|
+
def default_port
|
108
|
+
if secure?
|
109
|
+
443
|
110
|
+
else
|
111
|
+
80
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Sailplay
|
2
|
+
class APIConnectionError < StandardError; end
|
3
|
+
class ConfigurationError < StandardError; end
|
4
|
+
|
5
|
+
class Error < StandardError
|
6
|
+
attr_reader :message
|
7
|
+
attr_reader :http_status
|
8
|
+
attr_reader :http_body
|
9
|
+
attr_reader :json_body
|
10
|
+
|
11
|
+
def initialize(message = nil, http_status = nil, http_body = nil, json_body = nil)
|
12
|
+
@message = message
|
13
|
+
@http_status = http_status
|
14
|
+
@http_body = http_body
|
15
|
+
@json_body = json_body
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
status_string = @http_status.nil? ? '' : "(Status #{@http_status}) "
|
20
|
+
"#{status_string}#{@message}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class APIError < Error; end
|
25
|
+
class AuthenticationError < Error; end
|
26
|
+
class InvalidRequestError < Error; end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'sailplay'
|
2
|
+
require 'sailplay/rails/client'
|
3
|
+
|
4
|
+
module Sailplay
|
5
|
+
module Rails
|
6
|
+
def self.initialize
|
7
|
+
if defined?(::ActionController::Base)
|
8
|
+
::ActionController::Base.send(:include, Sailplay::Rails::Client)
|
9
|
+
end
|
10
|
+
|
11
|
+
rails_logger = if defined?(::Rails.logger)
|
12
|
+
::Rails.logger
|
13
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
14
|
+
RAILS_DEFAULT_LOGGER
|
15
|
+
end
|
16
|
+
|
17
|
+
Sailplay.configure do |config|
|
18
|
+
config.logger = rails_logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Sailplay::Rails.initialize
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Sailplay
|
2
|
+
module Rails
|
3
|
+
module Client
|
4
|
+
def self.included(base)
|
5
|
+
base.send :around_filter, :prepare_sailplay_options
|
6
|
+
|
7
|
+
base.send :helper_method, :render_sailplay_client
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def assign_sailplay_user(user)
|
13
|
+
sailplay_options(:origin_user_id => user.sailplay_user_id) if user.respond_to?(:sailplay_user_id)
|
14
|
+
sailplay_options(:probable_user_phone => user.sailplay_phone) if user.respond_to?(:sailplay_user_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_sailplay_client(options = {})
|
18
|
+
@_sailplay_client_fired = true
|
19
|
+
result = sailplay_compile_template(sailplay_options.merge(options))
|
20
|
+
|
21
|
+
if result.respond_to?(:html_safe)
|
22
|
+
result.html_safe
|
23
|
+
else
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def authenticate_sailplay_user(phone, force_reload = false)
|
29
|
+
return if phone.nil?
|
30
|
+
|
31
|
+
if force_reload || (session[:sailplay] && session[:sailplay][:auth_expires] < Time.now)
|
32
|
+
session[:sailplay] = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
unless session[:sailplay]
|
36
|
+
user = begin
|
37
|
+
Sailplay.find_user(phone, :auth => true)
|
38
|
+
rescue Sailplay::APIError
|
39
|
+
Sailplay.create_user(phone, :auth => true) rescue nil
|
40
|
+
end
|
41
|
+
|
42
|
+
if user
|
43
|
+
sailplay_options :auth_hash => user.auth_hash, :auth_expires => 3.days.from_now
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def report_sailplay_purchase(user_id, order_id, price)
|
49
|
+
purchase = Sailplay.create_purchase(user_id, price, :order_id => order_id)
|
50
|
+
sailplay_options :transaction_key => purchase.public_key
|
51
|
+
purchase
|
52
|
+
rescue Sailplay::Error => e
|
53
|
+
logger.error "Error reporting purchase to Sailplay: #{e.message}"
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def sailplay_options(options = {})
|
58
|
+
(@_sailplay_options ||= {}).merge! options
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def prepare_sailplay_options
|
64
|
+
load_sailplay_options
|
65
|
+
yield
|
66
|
+
if @_sailplay_client_fired
|
67
|
+
reset_sailplay_options
|
68
|
+
end
|
69
|
+
save_sailplay_options
|
70
|
+
end
|
71
|
+
|
72
|
+
def save_sailplay_options
|
73
|
+
(session[:sailplay] ||= {}).merge! @_sailplay_options if @_sailplay_options
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_sailplay_options
|
77
|
+
@_sailplay_options = session.delete :sailplay
|
78
|
+
end
|
79
|
+
|
80
|
+
def reset_sailplay_options
|
81
|
+
@_sailplay_options = nil
|
82
|
+
session[:sailplay] = nil
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def sailplay_compile_template(options)
|
87
|
+
template_options = {
|
88
|
+
:file => File.join(File.dirname(__FILE__), '..', '..', 'templates', 'sailplay_client'),
|
89
|
+
:layout => false,
|
90
|
+
:use_full_path => false,
|
91
|
+
:handlers => [:erb],
|
92
|
+
:locals => {
|
93
|
+
:host => Sailplay.configuration.host,
|
94
|
+
:api_path => Sailplay.configuration.js_api_path,
|
95
|
+
:store_id => Sailplay.configuration.store_id,
|
96
|
+
:position => Sailplay.configuration.js_position.to_s.split('_'),
|
97
|
+
:skin => Sailplay.configuration.skin,
|
98
|
+
:origin_user_id => '',
|
99
|
+
:user_phone => '',
|
100
|
+
:auth_hash => '',
|
101
|
+
:public_key => 'none',
|
102
|
+
:link => '',
|
103
|
+
:pic => ''
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
template_options[:locals].merge!(options)
|
108
|
+
|
109
|
+
case @template
|
110
|
+
when ActionView::Template
|
111
|
+
@template.render template_options
|
112
|
+
else
|
113
|
+
render_to_string template_options
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sailplay
|
2
|
+
class Response
|
3
|
+
attr_reader :raw_data, :data, :error_message
|
4
|
+
|
5
|
+
def initialize(json_body)
|
6
|
+
@raw_data = json_body
|
7
|
+
|
8
|
+
if @raw_data && @raw_data[:status] == 'ok'
|
9
|
+
@success = true
|
10
|
+
@data = @raw_data.reject {|k, v| k == :status}
|
11
|
+
else
|
12
|
+
@success = false
|
13
|
+
@error_message = @raw_data[:message]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
@success
|
19
|
+
end
|
20
|
+
|
21
|
+
def error?
|
22
|
+
!success?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<%= javascript_tag %Q{
|
2
|
+
var _sp_options = {
|
3
|
+
depId: #{store_id.to_json},
|
4
|
+
position: #{position.to_json},
|
5
|
+
authHash: #{auth_hash.to_json},
|
6
|
+
publicKey: #{public_key.to_json},
|
7
|
+
originUserId: #{origin_user_id.to_json},
|
8
|
+
probableUserPhone: #{user_phone.to_json},
|
9
|
+
pic: #{pic.to_json},
|
10
|
+
link: #{link.to_json},
|
11
|
+
skin: #{skin.to_json}
|
12
|
+
};
|
13
|
+
|
14
|
+
(function() {
|
15
|
+
var sp = document.createElement('script'); sp.type = 'text/javascript'; sp.async = false; sp.charset = 'utf-8';
|
16
|
+
sp.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + '#{host}/#{api_path}';
|
17
|
+
var scr = document.getElementsByTagName('script')[0]; scr.parentNode.insertBefore(sp, scr);
|
18
|
+
})();
|
19
|
+
}%>
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'sailplay/rails'
|
data/sailplay.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sailplay/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'sailplay'
|
8
|
+
spec.version = Sailplay::VERSION
|
9
|
+
|
10
|
+
spec.authors = ['Sergey Nebolsin']
|
11
|
+
spec.email = ['nebolsin@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = %q{Sailplay API client}
|
14
|
+
spec.description = %q{Wrapper for sailplay.ru REST api}
|
15
|
+
spec.homepage = 'https://github.com/nebolsin/sailplay'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.required_ruby_version = '>= 1.8.7'
|
24
|
+
|
25
|
+
spec.add_runtime_dependency 'multi_json'
|
26
|
+
spec.add_runtime_dependency 'rest-client'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
29
|
+
spec.add_development_dependency 'rake'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 2.0'
|
31
|
+
spec.add_development_dependency 'webmock'
|
32
|
+
spec.add_development_dependency 'coveralls'
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sailplay::Client do
|
4
|
+
|
5
|
+
describe 'configuration' do
|
6
|
+
it 'should set the defaults' do
|
7
|
+
Sailplay.reset!
|
8
|
+
Sailplay.client.host.should eq('sailplay.ru')
|
9
|
+
Sailplay.client.port.should eq(443)
|
10
|
+
Sailplay.client.secure.should be_true
|
11
|
+
Sailplay.client.endpoint.should eq('/api/v1')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should set the options' do
|
15
|
+
Sailplay.configure do |c|
|
16
|
+
c.host = 'test.me'
|
17
|
+
c.endpoint = 'megaapi'
|
18
|
+
c.store_id = 'test_id'
|
19
|
+
c.store_key = 'test_key'
|
20
|
+
c.store_pin = 'test_pin'
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
Sailplay.client.host.should eq('test.me')
|
25
|
+
Sailplay.client.endpoint.should eq('megaapi')
|
26
|
+
Sailplay.client.store_id.should eq('test_id')
|
27
|
+
Sailplay.client.store_key.should eq('test_key')
|
28
|
+
Sailplay.client.store_pin.should eq('test_pin')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '.request' do
|
33
|
+
let(:config) do
|
34
|
+
Sailplay::Configuration.new.tap do |c|
|
35
|
+
c.store_id = 'id'
|
36
|
+
c.store_key = 'key'
|
37
|
+
c.store_pin = '1111'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with invalid client configuration' do
|
42
|
+
it 'should raise if store_id is not configured' do
|
43
|
+
config.store_id = nil
|
44
|
+
client = Sailplay::Client.new(config)
|
45
|
+
lambda { client.request(:get, 'call') }.should raise_error(Sailplay::ConfigurationError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should raise if store_key is not configured' do
|
49
|
+
config.store_key = nil
|
50
|
+
client = Sailplay::Client.new(config)
|
51
|
+
lambda { client.request(:get, 'call') }.should raise_error(Sailplay::ConfigurationError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should raise if store_pin is not configured' do
|
55
|
+
config.store_pin = nil
|
56
|
+
client = Sailplay::Client.new(config)
|
57
|
+
lambda { client.request(:get, 'call') }.should raise_error(Sailplay::ConfigurationError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when authentication is required' do
|
62
|
+
before do
|
63
|
+
@client = Sailplay::Client.new(config)
|
64
|
+
stub_http_request(:get, %r{/action\d?}).to_return(:body => '{"status":"ok"}')
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:login_url) {
|
68
|
+
stub_http_request(:get, 'https://sailplay.ru/api/v1/login').
|
69
|
+
with(:query => {:store_department_id => 'id', :store_department_key => 'key', :pin_code => '1111'})
|
70
|
+
}
|
71
|
+
|
72
|
+
it 'should request the token' do
|
73
|
+
login_url.to_return(:body => '{"status":"ok","token":"some_new_token"}')
|
74
|
+
@client.request(:get, 'action')
|
75
|
+
login_url.should have_been_requested
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should cache the recieved token' do
|
79
|
+
login_url.to_return(:body => '{"status":"ok","token":"some_new_token"}')
|
80
|
+
@client.request(:get, 'action')
|
81
|
+
@client.request(:get, 'action1')
|
82
|
+
login_url.should have_been_requested.once
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
context 'error handling' do
|
88
|
+
let(:login) do
|
89
|
+
stub_http_request(:any, %r{https://sailplay.ru/api/v1/login})
|
90
|
+
end
|
91
|
+
|
92
|
+
before do
|
93
|
+
@client = Sailplay::Client.new(config)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should raise when cannot connect to server' do
|
97
|
+
login.to_raise(SocketError)
|
98
|
+
|
99
|
+
lambda { @client.request(:get, 'call') }.should raise_error(Sailplay::APIConnectionError) do |e|
|
100
|
+
e.message.should match("Unexpected error communicating when trying to connect to Sailplay. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host sailplay.ru' from the command line.")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should raise when cannot connect to server due timeout' do
|
105
|
+
login.to_timeout
|
106
|
+
|
107
|
+
lambda { @client.request(:get, 'call') }.should raise_error(Sailplay::APIConnectionError) do |e|
|
108
|
+
e.message.should match("Unexpected error communicating when trying to connect to Sailplay. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host sailplay.ru' from the command line.")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should raise when response is not a valid JSON' do
|
113
|
+
login.to_return(:body => '{123')
|
114
|
+
|
115
|
+
lambda { @client.request(:get, 'call') }.should raise_error(Sailplay::APIError) do |e|
|
116
|
+
e.http_status.should eq(200)
|
117
|
+
e.http_body.should eq('{123')
|
118
|
+
e.message.should match('Invalid response object from API')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|