sailplay 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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