snafu 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.textile +12 -1
  3. data/README.textile +68 -7
  4. data/lib/snafu.rb +9 -2
  5. data/lib/snafu/achievements.rb +17 -0
  6. data/lib/snafu/client.rb +33 -23
  7. data/lib/snafu/giants.rb +35 -0
  8. data/lib/snafu/models.rb +4 -1
  9. data/lib/snafu/models/achievement.rb +24 -0
  10. data/lib/snafu/models/giant.rb +21 -0
  11. data/lib/snafu/models/glitch_time.rb +116 -0
  12. data/lib/snafu/models/hub.rb +5 -2
  13. data/lib/snafu/models/location.rb +3 -3
  14. data/lib/snafu/models/street.rb +13 -5
  15. data/lib/snafu/util.rb +12 -0
  16. data/lib/snafu/version.rb +1 -1
  17. data/snafu.gemspec +2 -2
  18. data/spec/snafu/achievements_spec.rb +51 -0
  19. data/spec/snafu/client_spec.rb +37 -15
  20. data/spec/snafu/giants_spec.rb +60 -0
  21. data/spec/snafu/locations_spec.rb +33 -32
  22. data/spec/snafu/models/achievement_spec.rb +31 -0
  23. data/spec/snafu/models/giant_spec.rb +38 -0
  24. data/spec/snafu/models/glitch_image_spec.rb +7 -17
  25. data/spec/snafu/models/glitch_time_spec.rb +176 -0
  26. data/spec/snafu/models/hub_spec.rb +2 -2
  27. data/spec/snafu/models/street_spec.rb +1 -1
  28. data/spec/snafu/models/util_spec.rb +22 -0
  29. data/spec/snafu_spec.rb +4 -3
  30. data/spec/spec_helper.rb +17 -16
  31. metadata +46 -54
  32. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_accept_a_hash_of_POST_arguments.yml +0 -39
  33. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_automatically_pass_in_the_oauth_token_if.yml +0 -36
  34. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_automatically_pass_in_the_oauth_token_if_authenticate.yml +0 -36
  35. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_automatically_pass_in_the_oauth_token_if_authenticate_called_with_true.yml +0 -36
  36. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_automatically_pass_in_the_oauth_token_if_called_with_authenticate.yml +0 -36
  37. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_automatically_pass_in_the_oauth_token_if_called_with_authenticate_true.yml +0 -36
  38. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_automatically_pass_in_the_oauth_token_if_called_with_true.yml +0 -36
  39. data/spec/fixtures/vcr_cassettes/Snafu_Client/call_should_raise_a_GlitchAPIError_if_receiving_an_unsucessful_response_from_the_Glitch_API.yml +0 -36
  40. data/spec/fixtures/vcr_cassettes/Snafu_Locations/get_hub_should_return_a_valid_hub_if_given_a_valid_Hub_ID.yml +0 -39
  41. data/spec/fixtures/vcr_cassettes/Snafu_Locations/get_hub_should_return_an_array_of_valid_street_information_if_given_a_valid_Hub_ID.yml +0 -39
  42. data/spec/fixtures/vcr_cassettes/Snafu_Locations/get_hubs_should_populated_the_returned_Hub_objects_with_an_id_and_name.yml +0 -39
  43. data/spec/fixtures/vcr_cassettes/Snafu_Locations/get_hubs_should_return_an_array_of_Hub_objects.yml +0 -39
  44. data/spec/fixtures/vcr_cassettes/Snafu_Locations/get_hubs_should_return_an_array_of_all_hubs.yml +0 -39
  45. data/spec/fixtures/vcr_cassettes/Snafu_Locations/get_street_should_return_a_complete_street_if_given_a_valid_street_ID.yml +0 -40
  46. data/spec/fixtures/vcr_cassettes/Snafu_Models_Hub/initialize_should_populate_values_if_given_the_raw_JSON_response_from_HTTParty.yml +0 -39
  47. data/spec/fixtures/vcr_cassettes/Snafu_Models_Hub/initialize_should_populate_values_if_given_the_raw_response_from_Glitch.yml +0 -39
  48. data/spec/fixtures/vcr_cassettes/Snafu_Models_Street/initialize_should_populate_values_if_given_the_raw_JSON_response_from_HTTParty.yml +0 -40
  49. data/spec/fixtures/vcr_cassettes/Snafu_Models_Street_new/should_populate_values_if_given_the_raw_JSON_response_from_HTTParty.yml +0 -40
  50. data/spec/fixtures/vcr_cassettes/calendar_getHoldays/valid_calendar.yml +0 -39
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  api_key.rb
6
+ spec/fixtures/vcr_cassettes
@@ -1,8 +1,19 @@
1
+ h2. 0.2.0
2
+
3
+ * Hub#streets now contains an array of Street objects
4
+ * Support for in-game Date & Time
5
+ * Support for the "achievements.listAll" method
6
+ * Support for the "giants.list" method
7
+ * Support for the "giants.getFavor" method
8
+
1
9
  h2. 0.1.2
2
- * Glitch Locations (Hubs, Streets, etc.) may have strings or integers for their IDs. Street#id, and Hub#id now always return a String
10
+
11
+ * Glitch Locations (Hubs, Streets, etc.) may have strings or integers for their IDs. Street#id, and Hub#id now always returns a String
3
12
 
4
13
  h2. 0.1.1
14
+
5
15
  * Fixed an issue where Hub.new would not populate steet names when given the raw result from Glitch
6
16
 
7
17
  h2. 0.1.0
18
+
8
19
  * Initial release
@@ -1,20 +1,65 @@
1
1
  h1. Snafu
2
2
 
3
- Snafu is a Ruby gem that provides an interface to the "Glitch API":http://api.glitch.com. "Glitch":http://glitch.com is an online multi-player game created by Tiny Speck and provides a decent API for interacting with its world.
3
+ Snafu is a Ruby gem that provides an interface to the "API":http://api.glitch.com for "Glitch":http://glitch.com, a browser-based MMO created by "Tiny Speck":http://tinyspeck.com.
4
+
5
+ h2. API Support
6
+
7
+ h3. Currently Supported
8
+
9
+ * In-Game Time & Date (See the GlitchTime class for more info)
10
+ * Giants
11
+ ** giants.list (Snafu::Giants#get_giants)
12
+ * Locations
13
+ ** locations.getHubs (Snafu::Locations#get_hubs)
14
+ ** locations.getStreets (Snafu::Locations#get_hub)
15
+ ** locations.streetInfo (Snafu::Locations#get_street)
16
+
17
+ h3. Not Yet Supported
18
+
19
+ * Achievements
20
+ * Auctions
21
+ * Avatar
22
+ * Players
23
+ * Skills
4
24
 
5
25
  h2. Usage
6
26
 
7
- To get started, instantiate a client by passing in an optional oauth token of an authenticated user. Be sure to pass in an OAuth token with the appropriate scope level for API method you wish to call.
27
+ To get started, instantiate the client with either no options or with the OAuth token of an authenticated user if 'identity' scope or higher is required.
8
28
 
9
29
  <pre lang="ruby">
10
30
  snafu = Snafu.new(:oauth_token => "your-oauth-token")
11
31
  </pre>
12
32
 
13
- Snafu currently does not support Glitch's OAuth authentication. There are other gems that support authenticating with Glitch including the excellent "OmniAuth":https://github.com/intridea/omniauth.
33
+ Snafu currently does not support Glitch's OAuth authentication. There are other gems that support authenticating with Glitch such as "OmniAuth":https://github.com/intridea/omniauth.
34
+
35
+ h3. In-Game Time & Date
14
36
 
15
- h3. Supported Methods
37
+ Note that the hour, minute, day of week, day of year, and day of month are all zero-based.
16
38
 
17
- Currently, only the locations.* methods are supported via helper methods. Each method returns a Ruby object representing the returned data.
39
+ To get the current in-game date and time:
40
+
41
+ <pre lang="ruby">
42
+ current_time = snafu.glitch_time
43
+ current_time.year
44
+ => 20
45
+ current_time.month
46
+ => "Spork"
47
+ current_time.day
48
+ => "Hairday"
49
+ </pre>
50
+
51
+ h3. Giants
52
+
53
+ <pre lang="ruby">
54
+ # giants.list
55
+ giants = snafu.get_giants
56
+ giants.first.name
57
+ => "Alph"
58
+ </pre>
59
+
60
+ h3. Locations
61
+
62
+ All locations.* methods are supported. Each method returns a Ruby object representing the returned data.
18
63
 
19
64
  <pre lang="ruby">
20
65
  # locations.*
@@ -23,9 +68,21 @@ Currently, only the locations.* methods are supported via helper methods. Each m
23
68
  street = snafu.get_street(street_tsid) # "locations.streetInfo"
24
69
  </pre>
25
70
 
26
- The rest of the API will be supported in due time.
71
+ h3. Achievements
72
+
73
+ <pre lang="ruby">
74
+ achievements = snafu.get_achievements # "achievements.listAll"
75
+ achievements.first.name
76
+ => "1-Star Cuisinartist"
77
+ achievements.first.desc
78
+ => "Whipped up 11 meals with an Awesome Pot"
79
+ </pre>
80
+
81
+ h3. Manual Method Calls
27
82
 
28
- You can also receive raw data from the Glitch API by passing in any method as a string and supplying required parameters via an options hash. This returns a Hash representation of the JSON returned by Glitch:
83
+ You can also receive raw data from the Glitch API by passing any method name into #call and supplying any required parameters via an options hash. This returns a Hash representation of the JSON returned by Glitch.
84
+
85
+ For example, to manually call the Glitch *calendar.getholidays* method:
29
86
 
30
87
  <pre lang="ruby">
31
88
  snafu.call("calendar.getHolidays")
@@ -63,3 +120,7 @@ You can also receive raw data from the Glitch API by passing in any method as a
63
120
  => }
64
121
  </pre>
65
122
 
123
+ h2. Credits
124
+
125
+ "Jeff Browning":http://github.com/jbrowning - Original author
126
+ "Lee Jensen":https://github.com/outerim - Contributions to GlitchTime
@@ -1,6 +1,13 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
1
3
  require 'httparty'
2
4
 
3
5
  require "snafu/version"
4
6
  require "snafu/locations"
5
- require "snafu/client"
6
- require "snafu/models"
7
+ require "snafu/models"
8
+ require "snafu/achievements"
9
+ require "snafu/giants"
10
+ require "snafu/util"
11
+
12
+ # Must be required last
13
+ require "snafu/client"
@@ -0,0 +1,17 @@
1
+ module Snafu
2
+ module Achievements
3
+ def get_achievements
4
+ result = []
5
+ response = self.call("achievements.listAll", :per_page => self.achievement_count)
6
+ response["items"].each do |achievement|
7
+ result << Models::Achievement.new(achievement[1])
8
+ end
9
+ result
10
+ end
11
+
12
+ def achievement_count
13
+ response = self.call("achievements.listAll")
14
+ response["total"]
15
+ end
16
+ end
17
+ end
@@ -9,12 +9,15 @@ module Snafu
9
9
  # it's p-p-p-party time
10
10
  include HTTParty
11
11
 
12
- # include other classes & modules so they can be called on the returned client object
12
+ # include other classes & modules so they can be called on self
13
13
  include Snafu::Locations
14
-
14
+ include Snafu::Achievements
15
+ include Snafu::Giants
16
+ include Snafu::Util
17
+
15
18
  base_uri API_URL
16
19
 
17
- attr_accessor :oauth_token
20
+ attr_accessor :oauth_token, :last_request_result
18
21
 
19
22
  def initialize(options={})
20
23
  @oauth_token = options[:oauth_token]
@@ -30,39 +33,46 @@ module Snafu
30
33
  # snafu = snafu.call("players.stats", :authenticate => true)
31
34
  #
32
35
  # Invalid method calls will raise a <tt>GlitchAPIError</tt>
33
-
34
36
  def call(method, query_parameters={})
35
- if method.is_a? String
36
- options = { :format => :json }
37
- unless query_parameters.empty?
38
- options[:query] = query_parameters
39
- if options[:query].has_key?(:authenticate) && options[:query][:authenticate] == true
40
- if self.oauth_token.nil?
41
- raise GlitchAPIError.new("You cannot do an authenticated call without an oauth token")
42
- end
37
+ unless method.is_a? String
38
+ raise ArgumentError.new("Method argument must be a string")
39
+ end
43
40
 
44
- # Replace the authenticate
45
- options[:query].delete(:authenticate)
46
- options[:query].update(:oauth_token => GLITCH_OAUTH_TOKEN)
41
+ options = { :format => :json }
42
+ unless query_parameters.empty?
43
+ options[:query] = query_parameters
44
+ if options[:query].has_key?(:authenticate) && options[:query][:authenticate] == true
45
+ if self.oauth_token.nil?
46
+ raise GlitchAuthenticationError.new("You cannot perform an authenticated call without an oauth token")
47
47
  end
48
+
49
+ # Replace the authenticate key with the oauth token
50
+ options[:query].delete(:authenticate)
51
+ options[:query].update(:oauth_token => @oauth_token)
48
52
  end
49
- request_uri = "/#{method}"
50
- parse_response(self.class.get(request_uri, options))
51
- else
52
- raise ArgumentError.new("Method argument must be a string")
53
53
  end
54
+ request_uri = "/#{method}"
55
+ parse_response(self.class.get(request_uri, options))
54
56
  end
55
57
 
56
58
  def parse_response(response)
57
- if response["ok"] == 0
58
- raise GlitchAPIError.new(response["error"])
59
+ if response["error"] == "invalid_token"
60
+ raise GlitchAuthenticationError.new("Invalid Token")
61
+ elsif response["ok"] == 0
62
+ if response["error"] == "missing_scope"
63
+ raise GlitchAuthenticationError.new("The token supplied has insufficient scope for this API method")
64
+ elsif response["error"] == "not_authenticated"
65
+ raise GlitchAuthenticationError.new("This API method requires authentication and no OAuth token has been supplied")
66
+ else
67
+ raise GlitchAPIError.new(response["error"])
68
+ end
59
69
  else
70
+ @last_request_result = response
60
71
  response
61
72
  end
62
73
  end
63
74
  end
64
75
 
65
-
66
-
67
76
  class GlitchAPIError < StandardError;end
77
+ class GlitchAuthenticationError < StandardError;end
68
78
  end
@@ -0,0 +1,35 @@
1
+ module Snafu
2
+ module Giants
3
+
4
+ # Accesses the "giants.list" method from the Glitch API and returns an array of the
5
+ # Giant information
6
+ def get_giants
7
+ giants = []
8
+ response = self.call("giants.list")
9
+ response["giants"].each do |short_name, long_name|
10
+ giants << Models::Giant.new(:name => long_name)
11
+ end
12
+ giants
13
+ end
14
+
15
+ # Accesses the "giants.getFavor" method from the Glitch API and returns an array of
16
+ # giants with their respective favor for the authenticated users
17
+ #
18
+ # Requires an OAuth token with *read* scope
19
+ #
20
+ def get_giants_favor
21
+ giants = []
22
+ reponse = self.call("giants.getFavor", :authenticate => true)
23
+ reponse["giants"].each do |giant_short_name, giant_values|
24
+ giants << Models::Giant.new(
25
+ :name => giant_values["name"],
26
+ :cur_favor => giant_values["cur_favor"],
27
+ :max_favor => giant_values["max_favor"],
28
+ :cur_daily_favor => giant_values["cur_daily_favor"],
29
+ :max_daily_favor => giant_values["max_daily_favor"]
30
+ )
31
+ end
32
+ giants
33
+ end
34
+ end
35
+ end
@@ -1,4 +1,7 @@
1
1
  require 'snafu/models/glitch_image'
2
2
  require 'snafu/models/location'
3
3
  require 'snafu/models/hub'
4
- require 'snafu/models/street'
4
+ require 'snafu/models/street'
5
+ require 'snafu/models/glitch_time'
6
+ require 'snafu/models/achievement'
7
+ require 'snafu/models/giant'
@@ -0,0 +1,24 @@
1
+ module Snafu
2
+ module Models
3
+ class Achievement
4
+ attr_reader :name, :description, :url, :image_60, :image_180
5
+ alias_method :desc, :description
6
+ def initialize(options = {})
7
+ @name = options["name"]
8
+ @description = options["description"] || options["desc"]
9
+ @url = options["url"]
10
+ @image_60 = parse_image options["image_60"]
11
+ @image_180 = parse_image options["image_180"]
12
+ end
13
+
14
+ def parse_image(image)
15
+ if image.is_a? String
16
+ return GlitchImage.new(url: image)
17
+ elsif image.is_a? GlitchImage
18
+ return image
19
+ end
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Snafu
2
+ module Models
3
+ # Defines a class for Glitch Giants
4
+ #
5
+ class Giant
6
+ attr_reader :name, :cur_favor, :max_favor, :cur_daily_favor, :max_daily_favor
7
+
8
+ alias_method :current_favor, :cur_favor
9
+ alias_method :current_daily_favor, :cur_daily_favor
10
+
11
+ def initialize(options={})
12
+ @name = options[:name] || options["name"]
13
+ @cur_favor = (options[:cur_favor] || options["cur_favor"] || 0).to_i
14
+ @max_favor = (options[:max_favor] || options["max_favor"] || 0).to_i
15
+ @cur_daily_favor = (options[:cur_daily_favor] || options["cur_daily_favor"] || 0).to_i
16
+ @max_daily_favor = (options[:max_daily_favor] || options["max_daily_favor"] || 0).to_i
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,116 @@
1
+ module Snafu
2
+ module Models
3
+ # Class representing a Glitch date and time
4
+ #
5
+ # there are 4435200 real seconds in a game year
6
+ # there are 115200 real seconds in a game week
7
+ # there are 14400 real seconds in a game day
8
+ # there are 600 real seconds in a game hour
9
+ # there are 10 real seconds in a game minute
10
+ #
11
+ class GlitchTime
12
+ class InvalidTimestampError < Exception; end
13
+
14
+ GLITCH_EPOCH = 1238562000
15
+ Y_SECS = 4435200
16
+ D_SECS = 14400
17
+ H_SECS = 600
18
+ M_SECS = 10
19
+ DAYS_IN_MONTH = [29, 3, 53, 17, 73, 19, 13, 37, 5, 47, 11, 1]
20
+ MONTH_NAMES = %w(Primuary Spork Bruise Candy Fever Junuary Septa Remember Doom Widdershins Eleventy Recurse)
21
+ DAY_NAMES = %w(Hairday Moonday Twoday Weddingday Theday Fryday Standday Fabday Recurse)
22
+
23
+ attr_reader :timestamp, :seconds_since_epoch
24
+
25
+ def initialize(new_timestamp = Time.now)
26
+ @timestamp = new_timestamp.to_i
27
+ raise InvalidTimestampError if @timestamp < GLITCH_EPOCH
28
+ @timestamp = timestamp
29
+ @seconds_since_epoch = @timestamp - GLITCH_EPOCH
30
+ end
31
+
32
+ # Returns the number of years since the Glitch epoch
33
+ def year
34
+ (seconds_since_epoch / Y_SECS).to_i
35
+ end
36
+
37
+ # Returns the 0-based day of the current Glitch year
38
+ def day_of_year
39
+ (seconds_since_start_of_year / D_SECS).to_i
40
+ end
41
+
42
+ # Returns the 0-based month of the year
43
+ def month
44
+ running_days = -1
45
+ DAYS_IN_MONTH.each_with_index do |days, idx|
46
+ running_days += days
47
+ return idx if day_of_year <= running_days
48
+ end
49
+ end
50
+
51
+ # Returns the name of the month
52
+ def name_of_month
53
+ MONTH_NAMES[self.month]
54
+ end
55
+
56
+ # Returns the 0-based day of the month
57
+ def day_of_month
58
+ return self.day_of_year if self.month == 0
59
+ self.day_of_year - DAYS_IN_MONTH.slice(0, (month)).inject(:+)
60
+ end
61
+
62
+ # Returns the 0-based day of the week
63
+ def day_of_week
64
+ return -1 if day_of_year == 307
65
+ days_since_epoch % 8
66
+ end
67
+
68
+ # Returns the name of the day
69
+ def name_of_day
70
+ DAY_NAMES[day_of_week]
71
+ end
72
+
73
+ # Returns the number of game days since epoch
74
+ def days_since_epoch
75
+ self.day_of_year + (307 * year)
76
+ end
77
+
78
+ # Returns the hour of the day
79
+ def hour
80
+ # seconds_since_start_of_day / H_SECS
81
+ (seconds_since_start_of_day / H_SECS).to_i
82
+ end
83
+
84
+ # Returns the minute of the hour
85
+ #
86
+ # == Options
87
+ #
88
+ # +:padded+ - If true, will return a zero-padded string if the minute value is less than 10
89
+ def minute(padded = false)
90
+ min = (seconds_since_start_of_hour / M_SECS).to_i
91
+ if padded && min < 10
92
+ min = "0#{min}"
93
+ end
94
+ min
95
+ end
96
+
97
+ def to_s
98
+ "#{self.hour}:#{self.minute(padded: true)}, #{self.name_of_day} #{self.day_of_month + 1} of #{self.name_of_month}, year #{self.year}"
99
+ end
100
+
101
+ private
102
+ def seconds_since_start_of_year
103
+ seconds_since_epoch - (Y_SECS * self.year)
104
+ end
105
+
106
+ def seconds_since_start_of_day
107
+ seconds_since_start_of_year - (D_SECS * self.day_of_year)
108
+ end
109
+
110
+ def seconds_since_start_of_hour
111
+ seconds_since_start_of_day - (H_SECS * self.hour)
112
+ end
113
+
114
+ end
115
+ end
116
+ end