flox 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: 219bdba8d257f7a35c1b9174877fc74775ce0a89
4
+ data.tar.gz: cc1abbb765aa82c4b6c3d650e308869b9594361e
5
+ SHA512:
6
+ metadata.gz: 77c3fe30874b4bd0531108be4965dfa5178813712f319346cd167fa9b41985737904e095375b7b64792a0135275d1231d1817ba16e023d20b9c4906a5c4d4c86
7
+ data.tar.gz: 01509872cb9f839e79bd2941894d01eee92f5b238404e75aff60f1dba2a4099a0e05e96936840e617a070172ea5e2f0adc993986c95bb03540af14426bc3c9cf
data/bin/flox ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## Author: Daniel Sperl
4
+ ## Copyright: Copyright 2014 Gamua
5
+ ## License: Simplified BSD
6
+
7
+ $LOAD_PATH << '../lib'
8
+
9
+ require 'flox'
10
+ require 'fileutils'
11
+ require 'trollop'
12
+
13
+ class Worker
14
+
15
+ attr_reader :flox
16
+
17
+ def initialize(game_id, game_key, base_url)
18
+ @flox = Flox.new(game_id, game_key, base_url)
19
+ end
20
+
21
+ def login_with_key(key)
22
+ flox.login_with_key(key)
23
+ end
24
+
25
+ def execute(command_name, args)
26
+ begin
27
+ method_name = command_name.downcase.gsub("-", "_")
28
+ self.send(method_name, **args)
29
+ rescue Exception => e
30
+ log "Error: " + e.to_s
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def download_logs(args={})
37
+ query = args[:query]
38
+ limit = args[:limit]
39
+ destination = args[:destination] || Dir.pwd
40
+ FileUtils.mkdir_p(destination)
41
+
42
+ log "Fetching Logs ..."
43
+ log_ids = flox.load_log_ids(query, limit)
44
+ log_ids.each do |log_id|
45
+ remote_path = "logs/#{log_id}"
46
+ local_path = File.join(destination, log_id) + ".json"
47
+ load_and_save_resource(remote_path, local_path)
48
+ end
49
+ end
50
+
51
+ def status(args={})
52
+ log "Fetching Server Status ..."
53
+ status = flox.status
54
+ log "Version: #{status['version']}, status: #{status['status']}"
55
+ end
56
+
57
+ def log(message)
58
+ puts message
59
+ end
60
+
61
+ def fail(message)
62
+ log message
63
+ exit
64
+ end
65
+
66
+ def load_and_save_resource(remote_path, local_path)
67
+ if (File.exists? local_path)
68
+ log("Skipped #{local_path} (file exists)")
69
+ else
70
+ resource = flox.load_resource(remote_path)
71
+ File.write(local_path, JSON.pretty_generate(resource))
72
+ log("Saved #{local_path}")
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ options = Trollop::options do
79
+ banner "Administrative utility for the Flox.cc Game Backend"
80
+ opt :key, "The 'Hero' key used for authentication", :type => :string, :required => true
81
+ opt :game_id, "The ID of the game", :type => :string, :required => true
82
+ opt :game_key, "The key of the game", :type => :string, :required => true
83
+ opt :base_url, "The URL of the Flox service", :type => :string
84
+ opt :destination, "The directory in which to store the logs", :type => :string
85
+ opt :query, "Narrows down the list of results", :type => :string
86
+ opt :limit, "Maximum number of logs to download", :type => :int
87
+ end
88
+
89
+ commands = ARGV.clone
90
+ # puts "Optons: #{options}"
91
+
92
+ hero_key = options[:key]
93
+ game_id = options[:game_id]
94
+ game_key = options[:game_key]
95
+ base_url = options[:base_url] || Flox::DEFAULT_URL
96
+
97
+ worker = Worker.new(game_id, game_key, base_url)
98
+ worker.login_with_key(hero_key)
99
+
100
+ commands.each do |command|
101
+ worker.execute(command, options)
102
+ end
data/lib/flox.rb ADDED
@@ -0,0 +1,203 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ # The main class used to interact with the Flox cloud service. Create an
6
+ # instance of Flox using the game ID and key acquired from the web interface,
7
+ # then login with a "Hero" key. That way, you will be able to access the data
8
+ # of all players.
9
+ class Flox
10
+
11
+ # The URL where the Flox servers are found.
12
+ DEFAULT_URL = "https://www.flox.cc"
13
+
14
+ # @private
15
+ attr_reader :service
16
+
17
+ # The player that is currently logged in.
18
+ attr_reader :current_player
19
+
20
+ # Creates a new instance with a certain game ID and key. Per default, a guest
21
+ # player will be logged in. You probably need to call 'login_with_key' with a
22
+ # Hero-key to access your data.
23
+ def initialize(game_id, game_key, base_url=Flox::DEFAULT_URL)
24
+ @service = RestService.new(game_id, game_key, base_url)
25
+ self.login_guest
26
+ end
27
+
28
+ # Makes a key-login on the server. It is recommended to create a 'hero'
29
+ # player in the web interface and use that for the login.
30
+ def login_with_key(key)
31
+ login(:key, key)
32
+ end
33
+
34
+ # Creates a new guest player and logs it in.
35
+ def login_guest()
36
+ login(:guest)
37
+ end
38
+
39
+ # Logging out the current player automatically logs in a new guest.
40
+ alias_method :logout, :login_guest
41
+
42
+ # @private
43
+ def login(auth_type, auth_id=nil, auth_token=nil)
44
+ data = service.login(auth_type, auth_id, auth_token)
45
+ @current_player = Player.new(data['id'], data['entity'])
46
+ end
47
+
48
+ # Loads an entity with a certain type and id from the server.
49
+ # Normally, the type is the class name you used for the entity in your game.
50
+ # Returns a Flox::Entity instance.
51
+ def load_entity(type, id)
52
+ path = entity_path(type, id)
53
+ data = service.get(path)
54
+ if (type == '.player')
55
+ Player.new(id, data)
56
+ else
57
+ Entity.new(type, id, data)
58
+ end
59
+ end
60
+
61
+ # Stores an entity on the server.
62
+ def save_entity(entity)
63
+ result = service.put(entity.path, entity)
64
+ entity['updatedAt'] = result['updatedAt']
65
+ entity['createdAt'] = result['createdAt']
66
+ entity
67
+ end
68
+
69
+ # :call-seq:
70
+ # delete_entity(entity)
71
+ # delete_entity(type, id)
72
+ #
73
+ # Deletes the given entity from the database.
74
+ def delete_entity(*entity)
75
+ if entity.length > 1
76
+ type, id = entity[0], entity[1]
77
+ else
78
+ type, id = entity[0].type, entity[0].id
79
+ end
80
+ service.delete(entity_path(type, id))
81
+ nil
82
+ end
83
+
84
+ # Posts a score to a certain leaderboard. Beware that only the top score of
85
+ # a player will appear on the leaderboard.
86
+ def post_score(leaderboard_id, score, player_name)
87
+ path = leaderboard_path(leaderboard_id)
88
+ data = { playerName: player_name, value: score }
89
+ service.post(path, data)
90
+ end
91
+
92
+ # Loads all scores of a leaderboard, sorted by rank. 'scope' can either be
93
+ # one of the symbols +:today, :this_week, :all_time+ or an array of player IDs.
94
+ def load_scores(leaderboard_id, scope)
95
+ path = leaderboard_path(leaderboard_id)
96
+ args = {}
97
+
98
+ if scope.is_a?(Array)
99
+ args['p'] = scope
100
+ else
101
+ args['t'] = scope.to_s.to_camelcase
102
+ end
103
+
104
+ raw_scores = service.get(path, args)
105
+ raw_scores.collect { |raw_score| Score.new(raw_score) }
106
+ end
107
+
108
+ # Loads a JSON object from the given path. This works with any resource
109
+ # e.g. entities, logs, etc. Always returns a Hash.
110
+ def load_resource(path, args=nil)
111
+ service.get(path, args)
112
+ end
113
+
114
+ # Loads a log with a certain ID. A log is a Hash instance.
115
+ def load_log(log_id)
116
+ log = service.get log_path(log_id)
117
+ log['id'] = log_id unless log['id']
118
+ log
119
+ end
120
+
121
+ # Loads logs, optionally restricted by a certain query.
122
+ # Here are some sample queries:
123
+ #
124
+ # * 'day:2014-02-20' -> all logs of a certain day
125
+ # * 'severity:warning' -> all logs of type warning & error
126
+ # * 'severity:error' -> all logs of type error
127
+ # * 'day:2014-02-20 severity:error' all error logs from February 20th.
128
+ #
129
+ # Returns a Flox::ResourceEnumerator you can use to iterate
130
+ # over the logs.
131
+ def load_logs(query=nil, limit=nil)
132
+ log_ids = load_log_ids(query, limit)
133
+ paths = log_ids.map { |log_id| log_path(log_id) }
134
+ ResourceEnumerator.new(service, paths)
135
+ end
136
+
137
+ # Loads just the IDs of the logs, restricted by a certain query.
138
+ def load_log_ids(query=nil, limit=nil)
139
+ log_ids = []
140
+ cursor = nil
141
+ begin
142
+ args = {}
143
+ args['q'] = query if query
144
+ args['l'] = limit if limit
145
+ args['c'] = cursor if cursor
146
+
147
+ result = service.get "logs", args
148
+ cursor = result["cursor"]
149
+ log_ids += result["ids"]
150
+ limit -= log_ids.length if limit
151
+ end while !cursor.nil? and (limit.nil? or limit > 0)
152
+ log_ids
153
+ end
154
+
155
+ # Loads the status of the Flox service, which is a Hash with the
156
+ # keys 'status' and 'version'.
157
+ def status
158
+ service.get("")
159
+ end
160
+
161
+ # The ID of the game you are accessing.
162
+ def game_id
163
+ service.game_id
164
+ end
165
+
166
+ # The key of the game you are accessing.
167
+ def game_key
168
+ service.game_key
169
+ end
170
+
171
+ # The base URL of the Flox service.
172
+ def base_url
173
+ service.base_url
174
+ end
175
+
176
+ # @return [String] a string representation of the object.
177
+ def inspect
178
+ "[Flox game_id: '#{game_id}', base_url: '#{base_url}']"
179
+ end
180
+
181
+ private
182
+
183
+ def entity_path(type, id)
184
+ "entities/#{type}/#{id}"
185
+ end
186
+
187
+ def leaderboard_path(leaderboard_id)
188
+ "leaderboards/#{leaderboard_id}"
189
+ end
190
+
191
+ def log_path(log_id)
192
+ "logs/#{log_id}"
193
+ end
194
+
195
+ end
196
+
197
+ require 'flox/version'
198
+ require 'flox/utils'
199
+ require 'flox/rest_service'
200
+ require 'flox/entity'
201
+ require 'flox/player'
202
+ require 'flox/score'
203
+ require 'flox/resource_enumerator'
@@ -0,0 +1,103 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'time'
6
+
7
+ # The base class of all objects that can be stored persistently on the Flox
8
+ # server.
9
+ #
10
+ # The class extends `Hash`. Thus, all properties of the Entity can be accessed
11
+ # as keys of the Entity instance.
12
+ #
13
+ # entity['name'] = 'Donald Duck'
14
+ #
15
+ # For convenience, the standard entity properties (e.g. `created_at` and
16
+ # `updated_at`)can be accessed via Ruby attributes.
17
+ #
18
+ # entity.public_access = 'rw'
19
+ #
20
+ # To load and save an entity, use the respective methods on the Flox class.
21
+ #
22
+ # my_entity = flox.load_entity('SaveGame', '12345') # => Flox::Entity
23
+ # flox.save_entity(my_entity)
24
+ #
25
+ class Flox::Entity < Hash
26
+
27
+ # @return [String] the primary identifier of the entity.
28
+ attr_accessor :id
29
+
30
+ # @return [String] the type of the entity. Types group entities together on the server.
31
+ attr_reader :type
32
+
33
+ # @param type [String] Typically the class name of the entity (as used in the other SDKs).
34
+ # @param id [String] The unique identifier of the entity.
35
+ # @param data [Hash] The initial contents of the entity.
36
+ def initialize(type, id=nil, data=nil)
37
+ @type = type
38
+ @id = id ? id : String.random_uid
39
+ self['createdAt'] = self['updatedAt'] = Time.now.utc.to_xs_datetime
40
+ self.public_access = ''
41
+ self.merge!(data) if data
42
+ end
43
+
44
+ def created_at
45
+ Time.parse self['createdAt']
46
+ end
47
+
48
+ def updated_at
49
+ Time.parse self['updatedAt']
50
+ end
51
+
52
+ def public_access
53
+ self["publicAccess"]
54
+ end
55
+
56
+ def public_access=(access)
57
+ self["publicAccess"] = access.to_s
58
+ end
59
+
60
+ def owner_id
61
+ self["ownerId"]
62
+ end
63
+
64
+ def owner_id=(value)
65
+ self["ownerId"] = value.to_s
66
+ end
67
+
68
+ def path
69
+ "entities/#{@type}/#{@id}"
70
+ end
71
+
72
+ # @return [String] provides a simple string representation of the Entity.
73
+ def inspect
74
+ description = "[#{self.class} #{@id} (#{@type})\n"
75
+ each_pair do |key, value|
76
+ description += " #{key}: #{value}\n"
77
+ end
78
+ description += "]"
79
+ end
80
+
81
+ #
82
+ # documentation hints
83
+ #
84
+
85
+ # @!attribute owner_id
86
+ # @return [String] the player ID of the owner of the entity
87
+ # (referencing a Player entitity).
88
+
89
+ # @!attribute created_at
90
+ # @return [Time] the time the entity was created.
91
+
92
+ # @!attribute updated_at
93
+ # @return [Time] the time the entity was last changed on the server.
94
+
95
+ # @!attribute public_access
96
+ # @return [String] the access rights of all players except the owner
97
+ # (the owner always has unlimited access). Possible values: '', 'r', 'rw'
98
+
99
+ # @!attribute [r] path
100
+ # @return [String] the path to the REST-resource of the entity, relative
101
+ # to the game's root.
102
+
103
+ end
@@ -0,0 +1,43 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ # An Entity that contains information about a Flox Player.
6
+ # Normally, you don't create instances of `Player` yourself. There's always
7
+ # a player logged in you can access with
8
+ #
9
+ # flox.current_player # => Flox::Player
10
+ #
11
+ # To log in as a different player, you'll probably want to use a `key`-login.
12
+ # When you use the Flox Gem as a maintenance tool, create a "Hero" in the
13
+ # online interface and use its key to login. That way, you have access to
14
+ # all entities, regardless of their `public_access` values.
15
+ #
16
+ # flox.login_with_key 'hero-key' # => Flox::Player
17
+ #
18
+ # The Player class itself is just an entity that adds an `auth_type`
19
+ # property for your convenience.
20
+ class Flox::Player < Flox::Entity
21
+
22
+ # Creates a player with a certain ID and data. The `type` of a player
23
+ # is always `.player` in Flox.
24
+ def initialize(id=nil, data=nil)
25
+ data ||= {}
26
+ data["authType"] ||= "guest"
27
+ data["publicAccess"] ||= "r"
28
+ super(".player", id, data)
29
+ self.owner_id ||= self.id
30
+ end
31
+
32
+ def auth_type
33
+ self["authType"].to_sym
34
+ end
35
+
36
+ #
37
+ # documentation hints
38
+ #
39
+
40
+ # @!attribute auth_type
41
+ # @return [String] the type of authentication the player used to log in.
42
+
43
+ end
@@ -0,0 +1,43 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'json'
6
+
7
+ # A helper class that stores the paths to a number of REST resources and
8
+ # supports iterating over those resources, downloading them lazily from the
9
+ # server.
10
+ class Flox::ResourceEnumerator
11
+
12
+ include Enumerable
13
+
14
+ # @param rest_service [RestService]
15
+ # the service instance used to download the resources.
16
+ # @param paths [Array<String>]
17
+ # the URLs to the resources that need to be accessed, relative to
18
+ # the game's root.
19
+ def initialize(rest_service, paths)
20
+ @service = rest_service
21
+ @paths = paths
22
+ end
23
+
24
+ # Iterates over the resources provided on intialization, loading them
25
+ # from the server one by one.
26
+ def each
27
+ @paths.each do |path|
28
+ yield @service.get(path)
29
+ end
30
+ end
31
+
32
+ def length
33
+ @paths.length
34
+ end
35
+
36
+ #
37
+ # documentation hints
38
+ #
39
+
40
+ # @!attribute length
41
+ # @return [Fixnum] the total number of objects being enumerated.
42
+
43
+ end
@@ -0,0 +1,128 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'json'
6
+ require 'yaml'
7
+ require 'net/http'
8
+
9
+ # A class that makes it easy to communicate with the Flox server via a REST protocol.
10
+ class Flox::RestService
11
+
12
+ # @return [String] the unique identifier of the game.
13
+ attr_reader :game_id
14
+
15
+ # @return [String] the key that identifies the game.
16
+ attr_reader :game_key
17
+
18
+ # @return [String] the URL pointing to the Flox REST API.
19
+ attr_reader :base_url
20
+
21
+ def initialize(game_id, game_key, base_url)
22
+ @game_id = game_id
23
+ @game_key = game_key
24
+ @base_url = base_url
25
+ @authentication = { "authType" => "guest" }
26
+ end
27
+
28
+ # Makes a `GET` request at the server. The given data-Hash is URI-encoded
29
+ # and added to the path.
30
+ # @return the server response.
31
+ def get(path, data=nil)
32
+ path = full_path(path)
33
+ path += "?" + URI.encode_www_form(data) if data
34
+ request = Net::HTTP::Get.new(path)
35
+ execute(request)
36
+ end
37
+
38
+ # Makes a `DELETE` request at the server.
39
+ # @return the server response.
40
+ def delete(path)
41
+ request = Net::HTTP::Delete.new(full_path(path))
42
+ execute(request)
43
+ end
44
+
45
+ # Makes a `POST` request at the server. The given data-Hash is transferred
46
+ # in the body of the request.
47
+ # @return the server response.
48
+ def post(path, data=nil)
49
+ request = Net::HTTP::Post.new(full_path(path))
50
+ execute(request, data)
51
+ end
52
+
53
+ # Makes a `PUT` request at the server. The given data-Hash is transferred
54
+ # in the body of the request.
55
+ # @return the server response.
56
+ def put(path, data=nil)
57
+ request = Net::HTTP::Put.new(full_path(path))
58
+ execute(request, data)
59
+ end
60
+
61
+ # Makes a login on the server with the given authentication data.
62
+ # @return the server response.
63
+ def login(auth_type, auth_id=nil, auth_token=nil)
64
+ auth_data = {
65
+ "authType" => auth_type,
66
+ "authId" => auth_id,
67
+ "authToken" => auth_token,
68
+ "id" => auth_id
69
+ }
70
+
71
+ if (auth_type.to_sym == :guest)
72
+ response = auth_data
73
+ else
74
+ response = post("authenticate", auth_data)
75
+ auth_data["id"] = response["id"]
76
+ end
77
+
78
+ @authentication = auth_data
79
+ response
80
+ end
81
+
82
+ # @return [String] provides a simple string representation of the service.
83
+ def inspect
84
+ "[RestService game_id: #{game_id}, base_url: #{base_url}]"
85
+ end
86
+
87
+ private
88
+
89
+ def execute(request, data=nil)
90
+ flox_header = {
91
+ "sdk" => { "type" => "ruby", "version" => Flox::VERSION },
92
+ "gameKey" => @game_key,
93
+ "dispatchTime" => Time.now.utc.to_xs_datetime,
94
+ "player" => @authentication
95
+ }
96
+
97
+ request["Content-Type"] = "application/json"
98
+ request["X-Flox"] = flox_header.to_json
99
+ request.body = data.to_json if data
100
+
101
+ uri = URI.parse(@base_url)
102
+ http = Net::HTTP::new(uri.host, uri.port)
103
+
104
+ if uri.scheme == "https" # enable SSL/TLS
105
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
106
+ http.use_ssl = true
107
+ end
108
+
109
+ http.start do |session|
110
+ response = session.request(request)
111
+ if (response.is_a? Net::HTTPSuccess)
112
+ return JSON.parse(response.body || '{}')
113
+ else
114
+ message = begin
115
+ JSON.parse(response.body)['message']
116
+ rescue
117
+ response.body
118
+ end
119
+ raise message
120
+ end
121
+ end
122
+ end
123
+
124
+ def full_path(path)
125
+ "/api/games/#{@game_id}/#{path}"
126
+ end
127
+
128
+ end
data/lib/flox/score.rb ADDED
@@ -0,0 +1,34 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ # Provides information about the value and origin of one posted score entry.
6
+ class Flox::Score
7
+
8
+ # @return [String] the ID of the player who posted the score.
9
+ # Note that this could be a guest player unknown to the server.
10
+ attr_reader :player_id
11
+
12
+ # @return [String] the name of the player who posted the score.
13
+ attr_reader :player_name
14
+
15
+ # @return [Fixnum] the actual score.
16
+ attr_reader :value
17
+
18
+ # @return [String] the country from which the score originated, in a
19
+ # two-letter country code.
20
+ attr_reader :country
21
+
22
+ # @return [Time] the date at which the score was posted.
23
+ attr_reader :created_at
24
+
25
+ # @param data [Hash] the contents of the score as given by the Flox server.
26
+ def initialize(data)
27
+ @player_id = data['playerId'].to_s
28
+ @player_name = data['playerName'].to_s
29
+ @value = data['value'].to_i
30
+ @country = data['country'].to_s
31
+ @created_at = Time.parse(data['createdAt'].to_s)
32
+ end
33
+
34
+ end
data/lib/flox/utils.rb ADDED
@@ -0,0 +1,38 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'securerandom'
6
+
7
+ # Flox-extensions to the standard Time class.
8
+ class Time
9
+
10
+ # @return [String] an XS-DateTime representation of the string, like this:
11
+ # `2014-02-20T20:15:00.123Z`
12
+ def to_xs_datetime
13
+ strftime("%Y-%m-%dT%H:%M:%S.%LZ")
14
+ end
15
+
16
+ end
17
+
18
+ # Flox-extensions to the standard Time class.
19
+ class String
20
+
21
+ # @return [String] creates a random alphanumeric string with a given length.
22
+ def self.random_uid(length=16)
23
+ SecureRandom.base64(length * 2).gsub(/[\+\/]/, '').slice(0, length)
24
+ end
25
+
26
+ # @return [String] converts a `camelCase` string to its `under_score` equivalent.
27
+ def to_underscore
28
+ gsub(/(.)([A-Z])/,'\1_\2').downcase
29
+ end
30
+
31
+ # @return [String] converts a string that separates its words with space,
32
+ # underscore or dash into its `camelCase` equivalent.
33
+ def to_camelcase
34
+ words = downcase.split(/[_\-\s]/)
35
+ words.shift + words.map(&:capitalize).join
36
+ end
37
+
38
+ end
@@ -0,0 +1,10 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ class Flox
6
+
7
+ # The current version of the Flox SDK.
8
+ VERSION = "0.0.1"
9
+
10
+ end
@@ -0,0 +1,52 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'flox'
6
+ require 'test/unit'
7
+
8
+ class EntityTest < Test::Unit::TestCase
9
+
10
+ def test_init
11
+ type = "type"
12
+ id = "id"
13
+ data = { value: true }
14
+ entity = Flox::Entity.new(type, id, data)
15
+ assert_equal(type, entity.type)
16
+ assert_equal(id, entity.id)
17
+ assert_equal(data[:value], entity[:value])
18
+ assert_not_nil(entity.created_at)
19
+ assert_not_nil(entity.updated_at)
20
+ assert_not_nil(entity.public_access)
21
+ end
22
+
23
+ def test_init_without_id
24
+ entity = Flox::Entity.new("type")
25
+ assert_kind_of(String, entity.id)
26
+ assert_not_empty(entity.id)
27
+ end
28
+
29
+ def test_created_at
30
+ created_at = Time.parse("2014-02-20T11:00:00Z")
31
+ created_at_string = created_at.to_xs_datetime
32
+ entity = Flox::Entity.new('type', 'id')
33
+ entity['createdAt'] = created_at_string
34
+ assert_equal(created_at, entity.created_at)
35
+ end
36
+
37
+ def test_updated_at
38
+ updated_at = Time.parse("2014-02-20T11:00:00Z")
39
+ updated_at_string = updated_at.to_xs_datetime
40
+ entity = Flox::Entity.new('type', 'id')
41
+ entity['updatedAt'] = updated_at_string
42
+ assert_equal(updated_at, entity.updated_at)
43
+ end
44
+
45
+ def test_public_access
46
+ entity = Flox::Entity.new('type', 'id')
47
+ assert_equal('', entity.public_access)
48
+ entity.public_access = 'rw'
49
+ assert_equal('rw', entity.public_access)
50
+ end
51
+
52
+ end
data/test/test_flox.rb ADDED
@@ -0,0 +1,152 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'flox'
6
+ require 'test/unit'
7
+ require 'mocha/test_unit'
8
+
9
+ class FloxTest < Test::Unit::TestCase
10
+
11
+ GAME_ID = "game_id"
12
+ GAME_KEY = "game_key"
13
+ BASE_URL = "http://url.com"
14
+
15
+ attr_reader :flox
16
+
17
+ def setup
18
+ @flox = Flox.new(GAME_ID, GAME_KEY, BASE_URL)
19
+ end
20
+
21
+ def test_init
22
+ assert_equal(GAME_ID, flox.game_id)
23
+ assert_equal(GAME_KEY, flox.game_key)
24
+ assert_equal(BASE_URL, flox.base_url)
25
+
26
+ assert_kind_of(Flox::Player, flox.current_player)
27
+ assert_equal(:guest, flox.current_player.auth_type)
28
+ end
29
+
30
+ def test_post_score
31
+ flox.service.expects(:post).once
32
+ flox.post_score('leaderboard_id', 123, 'player_name')
33
+ end
34
+
35
+ def test_load_scores
36
+ leaderboard_id = "dummy"
37
+ path = "leaderboards/#{leaderboard_id}"
38
+ raw_score = {
39
+ 'value' => 20,
40
+ 'playerName' => 'hugo',
41
+ 'playerId' => '123',
42
+ 'country' => 'at',
43
+ 'createdAt' => '2014-02-24T20:15:00.123Z'
44
+ }
45
+
46
+ # using time scope (t)
47
+ flox.service.expects(:get).once.with(path, has_key('t')).returns([])
48
+ scores = flox.load_scores(leaderboard_id, :today)
49
+ assert_kind_of(Array, scores)
50
+ assert_equal(0, scores.length)
51
+
52
+ # using player scope (p)
53
+ flox.service.expects(:get).once.with(path, has_key('p')).returns([raw_score])
54
+ scores = flox.load_scores(leaderboard_id, %w(1, 2, 3))
55
+ assert_kind_of(Array, scores)
56
+ assert_equal(1, scores.length)
57
+
58
+ highscore = scores.first
59
+ assert_kind_of(Flox::Score, highscore)
60
+ assert_equal(raw_score['value'], highscore.value)
61
+ assert_equal(raw_score['playerName'], highscore.player_name)
62
+ assert_equal(raw_score['playerId'], highscore.player_id)
63
+ assert_equal(raw_score['country'], highscore.country)
64
+ assert_equal(raw_score['createdAt'], highscore.created_at.to_xs_datetime)
65
+ end
66
+
67
+ def test_login_with_key
68
+ key = "key"
69
+ result = { 'id' => '123', 'entity' => { 'authType' => 'key' } }
70
+ flox.service.expects(:login).with(:key, key, nil).once.returns(result)
71
+ player = flox.login_with_key(key)
72
+ assert_not_nil(player)
73
+ assert_equal(:key, player.auth_type)
74
+ assert_equal(player, flox.current_player)
75
+ end
76
+
77
+ def test_login_guest
78
+ player = flox.login_guest
79
+ assert_not_nil(player)
80
+ assert_equal(:guest, player.auth_type)
81
+ assert_equal(player, flox.current_player)
82
+ end
83
+
84
+ def test_load_entity
85
+ type = "type"
86
+ id = "id"
87
+ path = "entities/#{type}/#{id}"
88
+ data = { "name" => "Jean-Luc" }
89
+
90
+ flox.service.expects(:get).with(path).once.returns(data)
91
+ entity = flox.load_entity(type, id)
92
+
93
+ assert_kind_of(Flox::Entity, entity)
94
+ assert_equal(id, entity.id)
95
+ assert_equal(type, entity.type)
96
+ assert_equal(path, entity.path)
97
+ assert_equal(data["name"], entity["name"])
98
+ end
99
+
100
+ def test_save_entity
101
+ data = { "name" => "Jean-Luc" }
102
+ entity = Flox::Entity.new("type", "id", data)
103
+ path = "entities/#{entity.type}/#{entity.id}"
104
+ result = { "createdAt" => "2014-01-01T12:00:00.000Z",
105
+ "updatedAt" => "2014-02-01T12:00:00.000Z" }
106
+
107
+ flox.service.expects(:put).with(path, entity).once.returns(result)
108
+ flox.save_entity(entity)
109
+
110
+ assert_equal(result["createdAt"], entity.created_at.to_xs_datetime)
111
+ assert_equal(result["updatedAt"], entity.updated_at.to_xs_datetime)
112
+ end
113
+
114
+ def test_delete_entity
115
+ entity = Flox::Entity.new("type", "id")
116
+ path = "entities/#{entity.type}/#{entity.id}"
117
+ flox.service.expects(:delete).with(path)
118
+ flox.delete_entity(entity)
119
+ end
120
+
121
+ def test_find_logs
122
+ log_ids = %w{ 0 1 2 }
123
+ result = { 'ids' => log_ids, 'cursor' => nil }
124
+ flox.service.expects(:get).at_most_once.returns(result)
125
+ logs = flox.load_logs(':warning', 50)
126
+ assert_kind_of(Flox::ResourceEnumerator, logs)
127
+ assert_equal(log_ids.length, logs.length)
128
+ flox.service.expects(:get).times(log_ids.length).returns({})
129
+ logs.each { |log| assert_kind_of(Hash, log) }
130
+ end
131
+
132
+ def test_find_log_ids
133
+ log_ids = %w{ 0 1 2 3 4 5 6 7 8 9 }
134
+ log_ids_a = log_ids.slice 0, 5
135
+ log_ids_b = log_ids.slice 5, 5
136
+ result_a = { 'ids' => log_ids_a, 'cursor' => 'a' }
137
+ result_b = { 'ids' => log_ids_b, 'cursor' => nil }
138
+
139
+ # without limit
140
+ flox.service.expects(:get).twice.returns(result_a, result_b)
141
+ out_log_ids = flox.load_log_ids
142
+ assert_equal(log_ids.length, out_log_ids.length)
143
+
144
+ # with limit
145
+ limit = 7
146
+ result_b['ids'] = %w{ 5 6 }
147
+ flox.service.expects(:get).twice.returns(result_a, result_b)
148
+ out_log_ids = flox.load_log_ids(nil, limit)
149
+ assert_equal(limit, out_log_ids.length)
150
+ end
151
+
152
+ end
@@ -0,0 +1,34 @@
1
+ ## Author: Daniel Sperl
2
+ ## Copyright: Copyright 2014 Gamua
3
+ ## License: Simplified BSD
4
+
5
+ require 'flox'
6
+ require 'test/unit'
7
+
8
+ class EntityTest < Test::Unit::TestCase
9
+
10
+ def test_random_uid
11
+ length = 16
12
+ uid = String.random_uid(length)
13
+ assert_equal(length, uid.length)
14
+ assert_match(/^[a-zA-Z0-9]+$/, uid)
15
+ end
16
+
17
+ def test_xs_datetime
18
+ time_xs = "2014-02-20T11:00:00.124Z"
19
+ time = Time.parse(time_xs)
20
+ assert_equal(time_xs, time.to_xs_datetime)
21
+ end
22
+
23
+ def test_to_camelcase
24
+ assert_equal("homeSweetHome", "home_sweet_home".to_camelcase)
25
+ assert_equal("homeSweetHome", "hOME-sWEET-hOME".to_camelcase)
26
+ assert_equal("homeSweetHome", "Home Sweet Home".to_camelcase)
27
+ end
28
+
29
+ def test_to_underscore
30
+ assert_equal("home_sweet_home", "HomeSweetHome".to_underscore)
31
+ assert_equal("home_sweet_home", "homeSweetHome".to_underscore)
32
+ end
33
+
34
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Sperl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mocha
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ description: |
56
+ Flox is the no-fuzz backend for game developers. The Ruby SDK allows direct
57
+ interaction with the Flox servers, e.g. to download log files or update
58
+ specific entities. It can be used from other Ruby scripts or directly with
59
+ its bundled command-line utility.
60
+ email: daniel@gamua.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - bin/flox
66
+ - test/test_entity.rb
67
+ - test/test_flox.rb
68
+ - test/test_utils.rb
69
+ - lib/flox/entity.rb
70
+ - lib/flox/player.rb
71
+ - lib/flox/resource_enumerator.rb
72
+ - lib/flox/rest_service.rb
73
+ - lib/flox/score.rb
74
+ - lib/flox/utils.rb
75
+ - lib/flox/version.rb
76
+ - lib/flox.rb
77
+ homepage: https://www.flox.cc
78
+ licenses:
79
+ - Simplified BSD
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.0.3
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Ruby SDK for the flox.cc game backend
101
+ test_files: []
102
+ has_rdoc: