sailplay 0.1.1

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.
@@ -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,3 @@
1
+ module Sailplay
2
+ VERSION = '0.1.1'
3
+ 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