koa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dc5945903a71ce2ab19ff84349e51f71b424168f
4
+ data.tar.gz: 0eb595a00c2ba2ea352c9d9e58efa822aa9b7005
5
+ SHA512:
6
+ metadata.gz: dd417b6f9690c978be64f172c73125cc81c682b557bcfa484285707f3426fe025d5c0e3276e90e0a047e3b03a542fbc5468d30d471b6b59f14480ffcdb7d2191
7
+ data.tar.gz: 1273a8f1a20bc5748ef0caf33e4510272fcaecc81afc2b3fafaedebbace0657c70d11b7ad096757da84bc6f0b85222403a0c475e056bcf12c983d569346efe8f
data/lib/koa.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Koa
2
+ end
3
+
4
+ require 'koa/conf'
5
+ require 'koa/health-check'
6
+ require 'koa/logger'
7
+ require 'koa/measurement'
8
+ require 'koa/kik'
9
+ require 'koa/rack-request-timer'
10
+ require 'koa/request'
11
+ require 'koa/sequel-logger'
12
+ require 'koa/query-time'
data/lib/koa/conf.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Koa
2
+ module Conf
3
+
4
+ def self.env(k)
5
+ ENV[k]
6
+ end
7
+
8
+ def self.env!(k)
9
+ env(k) || raise("Must set #{k}")
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'json'
2
+ require 'timeout'
3
+
4
+ class Koa::HealthCheck
5
+ def self.call(env)
6
+ begin
7
+ Timeout::timeout(2) do
8
+ db_check
9
+ end
10
+ rescue => e
11
+ return [500, {"Content-Type" => "text/json"},
12
+ [JSON.dump({error: e.message})]]
13
+ end
14
+ [200, {"Content-Type" => "text/json"}, [JSON.dump({info: Time.now})]]
15
+ end
16
+
17
+ def self.db_check
18
+ query = "select * from information_schema.tables"
19
+ if defined?(ActiveRecord)
20
+ ActiveRecord::Base.connection.execute(query)
21
+ elsif defined?(Sequel)
22
+ c = Sequel.connect(ENV["DATABASE_URL"])
23
+ c.exec(query)
24
+ c.disconnect
25
+ end
26
+ end
27
+ end
data/lib/koa/kik.rb ADDED
@@ -0,0 +1,87 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'timeout'
4
+
5
+ module Koa::Kik
6
+ def self.purchase(signedData, username, host)
7
+ Koa::Logger.measure_block("kikpurchase") do
8
+ url = "https://purchase.kik.com/verification/v1/check?u=#{username}&d=#{host}"
9
+ response = request_with_retry(url, signedData, 5)
10
+ if response.nil? or response.code.to_i != 200
11
+ code = (response and response.code)
12
+ code ||= "nil"
13
+ Koa::Logger.count("kikpurchase.fail", 1,
14
+ {source: code, username: username, signed_data: signedData})
15
+ return false
16
+ end
17
+ JSON.parse(response.body)
18
+ end
19
+ end
20
+
21
+ def self.push(token, message, data)
22
+ Koa::Logger.measure_block("kikpush") do
23
+ url = "https://api.kik.com/push/v1/send"
24
+ body = {
25
+ token: token,
26
+ ticker: message,
27
+ data: data
28
+ }
29
+ response = request_with_retry(url, body.to_json, 1, 1)
30
+ response_code = (response && response.code)
31
+ response_code ||= "nil"
32
+ PushResponse.new(response).tap do |pr|
33
+ if pr.success?
34
+ Koa::Logger.count("notification_sent", 1)
35
+ else
36
+ Koa::Logger.count("notification_fail", 1, {source: response_code})
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def self.verify(signedData, username, host)
43
+ Koa::Logger.measure_block("kikverify") do
44
+ url = "https://auth.kik.com/verification/v1/check?u=#{username}&d=#{host}"
45
+ request_with_retry(url, signedData, 5).body
46
+ end
47
+ end
48
+
49
+ def self.request_with_retry(url, body, tries, seconds = 2)
50
+ tries.times do
51
+ begin
52
+ Timeout::timeout(seconds) do
53
+ return request(url, body)
54
+ end
55
+ rescue Timeout::Error
56
+ Koa::Logger.count("kiktimeout", 1)
57
+ nil
58
+ end
59
+ end
60
+ end
61
+
62
+ def self.request(url, body)
63
+ uri = URI.parse(url)
64
+ http = Net::HTTP.new(uri.host, uri.port)
65
+ http.use_ssl = true
66
+ request = Net::HTTP::Post.new(uri.request_uri)
67
+ request.add_field('Content-Type', 'application/json')
68
+ request.add_field('Content-Length', body.size)
69
+
70
+ request.body = body
71
+ http.request(request)
72
+ end
73
+
74
+ class PushResponse
75
+ def initialize(response)
76
+ @response = response
77
+ end
78
+
79
+ def success?
80
+ @response and @response.code.to_i == 200
81
+ end
82
+
83
+ def bad_token?
84
+ @response and @response.code.to_i == 403
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,55 @@
1
+ require 'json'
2
+ require 'securerandom'
3
+
4
+ module Koa
5
+ module LeaderboardClient
6
+ URL = Conf.env!('LEADERBOARD_URL')
7
+ GAME_ID = Conf.env!('KOA_GAME_ID')
8
+
9
+ def self.get_leaderboards(limit)
10
+ Logger.measure_block("leaderboard-client.get-leaderboards") do
11
+ response = Koa::Request.make(
12
+ type: :get,
13
+ url: URL + "/boards",
14
+ data: {limit: limit, game_id: GAME_ID}
15
+ )
16
+ if response.successful?
17
+ JSON.parse(response.body)
18
+ else
19
+ {}
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.get_user_scores(user_id)
25
+ Logger.measure_block("leaderboard-client.get-user-scores") do
26
+ response = Koa::Request.make(
27
+ type: :get,
28
+ url: URL + "/user_scores",
29
+ data: {game_id: GAME_ID, user_id: user_id}
30
+ )
31
+ if response.successful?
32
+ user_scores = JSON.parse(response.body)
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.add_score(user_id, score)
38
+ Logger.measure_block("leaderboard-client.add-score") do
39
+ Koa::Request.make(
40
+ type: :put,
41
+ url: URL + "/score",
42
+ data: {
43
+ user_id: user_id,
44
+ score: score,
45
+ request_id: SecureRandom.uuid,
46
+ game_id: GAME_ID
47
+ },
48
+ tries: 3,
49
+ timeout: 5
50
+ )
51
+ end
52
+ end
53
+
54
+ end
55
+ end
data/lib/koa/logger.rb ADDED
@@ -0,0 +1,85 @@
1
+ $stdout.sync = true
2
+ module Koa
3
+ class StringLogger
4
+ def initialize
5
+ @msgs = []
6
+ end
7
+
8
+ def puts(msg)
9
+ @msgs << msg
10
+ end
11
+
12
+ def flush
13
+ @msgs.length.times.map {@msgs.pop}
14
+ end
15
+ end
16
+
17
+ module NullLogger
18
+ def self.puts(*args)
19
+
20
+ end
21
+
22
+ def self.flush(*args)
23
+
24
+ end
25
+ end
26
+
27
+ module Logger
28
+ def self.appname
29
+ @prefix ||= ENV['APP_NAME'].downcase if ENV['APP_NAME']
30
+ if defined? ::Rails
31
+ @prefix ||= Rails.application.class.parent_name.downcase+"."
32
+ end
33
+ @prefix ||= ""
34
+ end
35
+
36
+ def self.prefix(name)
37
+ appname+name
38
+ end
39
+
40
+ def self.librato_log(type, name, val, data)
41
+ name = prefix(name)
42
+ data["#{type}##{name}"] = val
43
+ log(data)
44
+ end
45
+
46
+ def self.out=(o)
47
+ @out = o
48
+ end
49
+
50
+ def self.out
51
+ @out || $stdout
52
+ end
53
+
54
+ def self.log(data)
55
+ out.puts(stringify(data))
56
+ end
57
+
58
+ def self.measure_block(name, data = {})
59
+ start = Time.now
60
+ result = yield
61
+ elapsed = (Time.now.to_f - start.to_f) * 1000
62
+ measure(name, elapsed.round.to_s+"ms")
63
+ result
64
+ end
65
+
66
+ def self.count(name, val, data = {})
67
+ librato_log("count", name, val, data)
68
+ end
69
+
70
+ def self.measure(name, val, data = {})
71
+ librato_log("measure", name, val, data)
72
+ end
73
+
74
+ def self.sample(name, val, data = {})
75
+ librato_log("sample", name, val, data)
76
+ end
77
+
78
+ def self.stringify(data)
79
+ data.reduce(out=String.new) do |s, tup|
80
+ s << [tup.first, tup.last].join("=") << " "
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,45 @@
1
+ module Koa
2
+ module Measurement
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def measure_methods(*args)
9
+ @methods_to_measure ||= []
10
+ @methods_to_measure += args
11
+ @methods_to_measure.map!(&:to_sym)
12
+ end
13
+
14
+ def method_added(name)
15
+ return if @adding_measurers
16
+ @adding_measurers = true
17
+ if @methods_to_measure.include? name.to_sym
18
+ unmeasured_name = "unmeasured_#{name}"
19
+ alias_method unmeasured_name, name
20
+ define_method name do |*args, &block|
21
+ Koa::Logger.measure_block(name.to_s.gsub(/_/,"-")) do
22
+ send unmeasured_name, *args, &block
23
+ end
24
+ end
25
+ end
26
+ @adding_measurers = nil
27
+ end
28
+
29
+ def singleton_method_added(name)
30
+ return if @adding_measurers
31
+ @adding_measurers = true
32
+ if @methods_to_measure.include? name.to_sym
33
+ unmeasured_name = "unmeasured_#{name}"
34
+ self.singleton_class.send(:alias_method, unmeasured_name, name)
35
+ define_singleton_method name do |*args, &block|
36
+ Koa::Logger.measure_block(name.to_s.gsub(/_/,"-")) do
37
+ send unmeasured_name, *args, &block
38
+ end
39
+ end
40
+ end
41
+ @adding_measurers = nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ module Koa
2
+ module QueryTime
3
+ def self.enable_log
4
+ if Rails.env.production?
5
+ ActiveSupport::Notifications.subscribe "sql.active_record" do |name, start, finish, id, payload|
6
+ if payload[:name] == "SQL"
7
+ duration = (finish - start) * 1000
8
+ Koa::Logger.measure("pg.query", duration.round.to_s+"ms")
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class Koa::RackRequestTimer
2
+ def initialize(app)
3
+ @app = app
4
+ end
5
+
6
+ def call(env)
7
+ start_request = Time.now
8
+ status, headers, body = @app.call(env)
9
+ elapsed = (Time.now - start_request) * 1000
10
+ $stdout.puts("request-id=#{env['HTTP_HEROKU_REQUEST_ID']} measure#rack-request=#{elapsed.round}ms")
11
+ [status, headers, body]
12
+ end
13
+ end
@@ -0,0 +1,106 @@
1
+ require 'cgi'
2
+ require 'delegate'
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'timeout'
6
+ require 'uri'
7
+
8
+ module Koa
9
+ class TimeoutResponse
10
+
11
+ def successful?
12
+ false
13
+ end
14
+
15
+ def code
16
+ '0'
17
+ end
18
+
19
+ def self.body_permitted?
20
+ false
21
+ end
22
+
23
+ def body
24
+ nil
25
+ end
26
+ end
27
+
28
+ class ResponseDecorator < SimpleDelegator
29
+ def successful?
30
+ code.to_i / 100 == 2
31
+ end
32
+ end
33
+
34
+ module Request
35
+ def self.make(opts)
36
+ opts[:tries] ||= 1
37
+ opts[:timeout] ||= 10
38
+
39
+ opts[:url] = build_url(opts)
40
+ opts[:body] = build_body(opts)
41
+
42
+ execute(opts)
43
+ end
44
+
45
+ private
46
+
47
+ def self.execute(opts)
48
+ opts[:tries].times do
49
+
50
+ begin
51
+ ::Timeout::timeout(opts[:timeout]) do
52
+ return ResponseDecorator.new(net_request(opts))
53
+ end
54
+ rescue ::Timeout::Error
55
+ end
56
+
57
+ end
58
+ Koa::TimeoutResponse.new
59
+ end
60
+
61
+ def self.net_request(opts)
62
+ uri = ::URI.parse(opts[:url])
63
+ http = ::Net::HTTP.new(uri.host, uri.port)
64
+ http.use_ssl = uri.scheme == "https"
65
+ request = request_factory(opts[:type]).new(uri.request_uri)
66
+
67
+ request.basic_auth uri.user, uri.password if uri.user
68
+ request.basic_auth opts[:auth][:user], opts[:auth][:pass] if opts[:auth]
69
+
70
+ if opts[:type] == :post || opts[:type] == :put
71
+ request.add_field('Content-Type', 'application/json')
72
+ request.add_field('Content-Length', opts[:body].size)
73
+ request.body = opts[:body]
74
+ end
75
+
76
+ http.request(request)
77
+ end
78
+
79
+ def self.request_factory(type)
80
+ return ::Net::HTTP::Post if type == :post
81
+ return ::Net::HTTP::Put if type == :put
82
+ return ::Net::HTTP::Delete if type == :delete
83
+ return ::Net::HTTP::Get if type == :get
84
+ end
85
+
86
+ def self.build_body(opts)
87
+ body = ""
88
+ if opts[:type] == :post || opts[:type] == :put
89
+ body = ::JSON.dump(opts[:data]) if opts[:data]
90
+ end
91
+ body
92
+ end
93
+
94
+ def self.build_url(opts)
95
+ url = opts[:url]
96
+ if opts[:type] == :get || opts[:type] == :delete
97
+ url += "?"+hash_to_query(opts[:data]) if opts[:data]
98
+ end
99
+ url
100
+ end
101
+
102
+ def self.hash_to_query(hash)
103
+ hash.map{|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"}.join("&")
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,33 @@
1
+ module Koa
2
+ if defined?(::Sequel)
3
+ module ::Sequel
4
+ class Database
5
+ def log_yield(sql, args=nil)
6
+ sql = "#{sql}; #{args.inspect}" if args
7
+ t0 = Time.now
8
+ begin
9
+ yield
10
+ rescue => e
11
+ log_exception(e, sql)
12
+ raise
13
+ ensure
14
+ t1 = Time.now
15
+ log_duration(Integer((t1-t0)*1000), sql) unless e
16
+ end
17
+ end
18
+
19
+ def log_duration(t, sql)
20
+ Logger.measure("sequel-latency", t, source: action(sql))
21
+ end
22
+
23
+ def log_exception(e, sql)
24
+ Logger.log(error: "sequel-exception", class: e.class)
25
+ end
26
+
27
+ def action(sql)
28
+ sql[/(\w+){1}/].downcase
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: koa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Eric Rykwalder
8
+ - Ryan Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-02 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Gem of tools for Koala
15
+ email: eric@koa.la
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/koa/conf.rb
21
+ - lib/koa/health-check.rb
22
+ - lib/koa/kik.rb
23
+ - lib/koa/leaderboard-client.rb
24
+ - lib/koa/logger.rb
25
+ - lib/koa/measurement.rb
26
+ - lib/koa/query-time.rb
27
+ - lib/koa/rack-request-timer.rb
28
+ - lib/koa/request.rb
29
+ - lib/koa/sequel-logger.rb
30
+ - lib/koa.rb
31
+ homepage: http://koa.la
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.0.6
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: KOA tools
55
+ test_files: []