unchained 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +20 -0
  3. data/README.md +38 -0
  4. data/lib/unchained/client.rb +4 -0
  5. data/lib/unchained/client/archetypes.rb +5 -6
  6. data/lib/unchained/client/attributes.rb +35 -0
  7. data/lib/unchained/client/factions.rb +5 -6
  8. data/lib/unchained/client/mixins/resource.rb +110 -16
  9. data/lib/unchained/client/motd.rb +19 -0
  10. data/lib/unchained/client/patcher.rb +11 -13
  11. data/lib/unchained/client/races.rb +5 -6
  12. data/lib/unchained/client/servers.rb +11 -10
  13. data/lib/unchained/error.rb +2 -0
  14. data/lib/unchained/request.rb +5 -1
  15. data/lib/unchained/version.rb +1 -1
  16. data/test/_fixtures/vcr_cassettes/archetypes.yml +51 -0
  17. data/test/_fixtures/vcr_cassettes/attribute_offsets.yml +36 -0
  18. data/test/_fixtures/vcr_cassettes/attributes.yml +100 -0
  19. data/test/_fixtures/vcr_cassettes/factions.yml +45 -0
  20. data/test/_fixtures/vcr_cassettes/motd.yml +36 -0
  21. data/test/_fixtures/vcr_cassettes/patcher_alerts.yml +36 -0
  22. data/test/_fixtures/vcr_cassettes/patcher_hero_contents.yml +104 -0
  23. data/test/_fixtures/vcr_cassettes/races.yml +38 -0
  24. data/test/_fixtures/vcr_cassettes/servers.yml +36 -0
  25. data/test/test_helper.rb +7 -0
  26. data/test/unchained/client/archetypes_test.rb +27 -0
  27. data/test/unchained/client/attributes_test.rb +29 -0
  28. data/test/unchained/client/factions_test.rb +20 -0
  29. data/test/unchained/client/mixins/resource_test.rb +38 -0
  30. data/test/unchained/client/motd_test.rb +23 -0
  31. data/test/unchained/client/patcher_test.rb +29 -0
  32. data/test/unchained/client/races_test.rb +20 -0
  33. data/test/unchained/client/servers_test.rb +20 -0
  34. data/test/unchained/client_test.rb +0 -5
  35. data/unchained.gemspec +4 -2
  36. metadata +66 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d440ea525f7c18793f4604906a9c1eb098e70b87
4
- data.tar.gz: 1d5c9f43267ca976c652ffb615a8d12d5e315a5b
3
+ metadata.gz: 8a4a2a58f8403c47f9d8f1f45cef8df674462f7f
4
+ data.tar.gz: eecb1fc22ce12a1150efdfb27f3ce83df34cfe18
5
5
  SHA512:
6
- metadata.gz: 1acaf1d610b637280ddf66f456b54e70acf1833307840c0f1142e3d83c6f1e97c8165fc3210841bcd22cf2b1308feed77eedb8d96ce1766de99a8d251a6cb002
7
- data.tar.gz: ef53adfdcc531a92902ac9913bcb24599edfdaaa8c245884226eb26dd0f146056777d068fb6f8b4bc160be49ec85d55df760004df4aeea067a45af26f9e85c10
6
+ metadata.gz: 4473fade28915ab541388879834f05d92c7bca0694d5eb3d6a12d9d0f74e2c98d0fd1b408a08ec56125b5ecdb667a4f2c3fc11e2145a9653c9cf6b1fee548d9f
7
+ data.tar.gz: 4889219e809cf2a40f5985c8e8fd7aa8f46d9476118ccf5f42d0624b2fe1f30fadeea69b8e1913aaa5bb2f636c1ff375bdcc815a9f1a4b69557536eeff52af4e
data/LICENSE.md CHANGED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Andrew Thorp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -0,0 +1,38 @@
1
+ ### Unchained
2
+
3
+ A ruby client library for the Camelot Unchained API.
4
+
5
+ You can view the API documentation at http://api.camelotunchained.com/
6
+
7
+ ### Usage
8
+
9
+ First, install the gem:
10
+
11
+ ```ruby
12
+ gem install unchained
13
+ ```
14
+
15
+ Alternatively, add the gem to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'unchained'
19
+ ```
20
+
21
+ Once you have the gem installed, you can use it to interact with the Camelot
22
+ Unchained API:
23
+
24
+ ```ruby
25
+ [1] pry(main)> client = Unchained::Client.new;
26
+ [2] pry(main)> client.servers.first
27
+ => #<Unchained::Client::Servers::Server:0x007fa0b94c7258 @access_level=6, @channel_id=4, @host="wyrmlingprep.camelotunchained.com", @name="WyrmlingPrep", @player_maximum=1000, @shard_id=1>
28
+ ```
29
+
30
+ ### TODO
31
+
32
+ - Tests
33
+ - Implement `loginToken` endpoints
34
+
35
+ ### License
36
+
37
+ View the [License](LICENSE.md)
38
+
@@ -4,7 +4,9 @@ require_relative 'request'
4
4
  # RESOURCES
5
5
  require_relative 'client/mixins/resource'
6
6
  require_relative 'client/archetypes'
7
+ require_relative 'client/attributes'
7
8
  require_relative 'client/factions'
9
+ require_relative 'client/motd'
8
10
  require_relative 'client/races'
9
11
  require_relative 'client/patcher'
10
12
  require_relative 'client/servers'
@@ -28,7 +30,9 @@ module Unchained
28
30
 
29
31
  # RESOURCES
30
32
  include Unchained::Client::Archetypes
33
+ include Unchained::Client::Attributes
31
34
  include Unchained::Client::Factions
35
+ include Unchained::Client::MOTD
32
36
  include Unchained::Client::Races
33
37
  include Unchained::Client::Patcher
34
38
  include Unchained::Client::Servers
@@ -4,12 +4,11 @@ module Unchained
4
4
 
5
5
  class Archetype
6
6
  include Unchained::Client::Mixins::Resource
7
- resource({
8
- :description => 'description',
9
- :faction => 'faction',
10
- :id => 'id',
11
- :name => 'name',
12
- })
7
+
8
+ attribute :description, String
9
+ attribute :faction, Integer
10
+ attribute :id, Integer
11
+ attribute :name, String
13
12
  end
14
13
 
15
14
  def archetypes(opts={})
@@ -0,0 +1,35 @@
1
+ module Unchained
2
+ class Client
3
+ module Attributes
4
+
5
+ class AttributeInfo
6
+ include Unchained::Client::Mixins::Resource
7
+
8
+ attribute :base_value, Float, json: 'baseValue'
9
+ attribute :derived_from, String, json: 'derivedFrom'
10
+ attribute :description, String
11
+ attribute :max_or_multipler, Float, json: 'maxOrMultipler'
12
+ attribute :name, String
13
+ attribute :type, Integer
14
+ attribute :units, String
15
+ end
16
+
17
+ class AttributeOffset
18
+ include Unchained::Client::Mixins::Resource
19
+
20
+ attribute :race, Integer
21
+ attribute :gender, Integer
22
+ attribute :offsets, Hash, json: 'attributeOffsets'
23
+ end
24
+
25
+ def attributes(shard, opts={})
26
+ get_resources("#{base_url}/gamedata/attributes/#{shard}", AttributeInfo, opts)
27
+ end
28
+
29
+ def attribute_offsets(shard, opts={})
30
+ get_resources("#{base_url}/gamedata/attributeoffsets/#{shard}", AttributeOffset, opts)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -4,12 +4,11 @@ module Unchained
4
4
 
5
5
  class Faction
6
6
  include Unchained::Client::Mixins::Resource
7
- resource({
8
- :description => 'description',
9
- :id => 'id',
10
- :name => 'name',
11
- :short_name => 'shortName',
12
- })
7
+
8
+ attribute :description, String
9
+ attribute :id, Integer
10
+ attribute :name, String
11
+ attribute :short_name, String, json: 'shortName'
13
12
  end
14
13
 
15
14
  def factions(opts={})
@@ -1,41 +1,135 @@
1
1
  module Unchained
2
2
  class Client
3
3
  module Mixins
4
+
5
+ # The Resource mixin gives you access to a DSL that lets you define what
6
+ # the class should look like in the JSON response.
7
+ #
8
+ # Usage:
9
+ #
10
+ # class Guild
11
+ # include Unchained::Client::Mixins::Resource
12
+ #
13
+ # attribute :id, String
14
+ # attribute :name, String
15
+ # attribute :members, Integer, json: 'numberOfMembers'
16
+ # end
17
+ #
18
+
4
19
  module Resource
20
+
21
+ # An `Attribute` is what `attribute` creates.
22
+ class Attribute
23
+ attr_reader :name
24
+ attr_reader :type
25
+ attr_reader :opts
26
+
27
+ def initialize(name, type, opts)
28
+ @name = name
29
+ @type = type
30
+ @opts = opts
31
+ end
32
+
33
+ # The field that maps to this attribute in the JSON.
34
+ #
35
+ # Usage:
36
+ #
37
+ # # json_field will return 'FOO'
38
+ # attribute :foo, json: 'FOO'
39
+ #
40
+ # # json_field will return 'foo_bar'
41
+ # attribute :foo_bar
42
+ #
43
+ def json_field
44
+ @opts.fetch(:json, @name.to_s)
45
+ end
46
+
47
+ # Whether or not to allow nil, defaults to false.
48
+ def allow_nil?
49
+ @opts.fetch(:allow_nil, false)
50
+ end
51
+ end
52
+
5
53
  def self.included(base)
6
54
  base.extend ClassMethods
7
55
  end
8
56
 
57
+ # Mostly just a helper method that you can override if you
58
+ # want to.
9
59
  def to_s
10
- attributes = self.class::JSON_MAP.map{|k,_| "#{k}=#{self.send(k)}"}.join(" ")
11
- "[#{self.class_name}] #{attributes}"
12
- end
60
+ attrs = self.class.attributes.map do |attr|
61
+ "#{attr.name}=#{self.send(attr.name)}"
62
+ end.join(', ')
13
63
 
14
- # Lurk, is there not a better way to do this?
15
- def class_name
16
- self.class.name.split('::').last
64
+ "[#{self.class.name.split('::').last}] #{attrs}"
17
65
  end
18
66
 
19
67
  module ClassMethods
20
- JSON_MAP = {}
68
+ attr_reader :attributes
21
69
 
22
- def resource(json_map)
70
+ def attribute(name, type, opts={})
23
71
  instance_eval do
24
- const_set("JSON_MAP", json_map)
72
+ attr_accessor name
25
73
  end
26
74
 
27
- class_eval do
28
- attr_accessor(*json_map.keys)
75
+ @attributes ||= []
76
+ @attributes << Attribute.new(name, type, opts)
77
+ end
78
+
79
+ # This is a pretty naive implementation of parsing JSON. It will
80
+ # loop through all of `@attributes` to find the right one, then do
81
+ # some very minimal validation, before setting the attribute on the
82
+ # instance.
83
+ #
84
+ # Returns an instance of the class that uses this mixin.
85
+ def from_json(json)
86
+ res = self.new
87
+
88
+ json.each do |k, v|
89
+ # TODO: Better way to do this?
90
+ attr = @attributes.find{|a| a.json_field == k.to_s}
91
+ raise InvalidAttribute.new(
92
+ "`#{self.name.split('::').last}` did not define a `#{k}`."
93
+ ) if attr.nil?
94
+
95
+ # TODO: Better way to do this?
96
+ case attr.type.to_s
97
+ when Integer.to_s
98
+ maybe_raise_invalid_value(attr, k, v) unless v.is_a?(Fixnum)
99
+ value = v.to_i
100
+ when Float.to_s
101
+ maybe_raise_invalid_value(attr, k, v) unless v.is_a?(Float)
102
+ value = v.to_f
103
+ when String.to_s
104
+ maybe_raise_invalid_value(attr, k, v) unless v.is_a?(String)
105
+ value = v
106
+ when Hash.to_s
107
+ maybe_raise_invalid_value(attr, k, v) unless v.is_a?(Hash)
108
+ value = v
109
+ end
110
+
111
+ res.send("#{attr.name}=", value)
29
112
  end
113
+
114
+ res
30
115
  end
31
116
 
32
- def decode_result(json)
33
- instance = self.new
34
- self::JSON_MAP.each do |k,v|
35
- instance.send("#{k}=", json[v])
117
+ private
118
+
119
+ def maybe_raise_invalid_value(attribute, key, value)
120
+ if value.nil?
121
+ return if attribute.allow_nil?
122
+
123
+ raise InvalidValue.new(
124
+ "`#{attribute.name}` is not allowed to be nil.",
125
+ )
36
126
  end
37
- instance
127
+
128
+ raise InvalidValue.new(
129
+ "Expected #{attribute.type}, got #{value.class}. `#{key}` (#{value})."
130
+ )
38
131
  end
132
+
39
133
  end
40
134
  end
41
135
  end
@@ -0,0 +1,19 @@
1
+ module Unchained
2
+ class Client
3
+ module MOTD
4
+
5
+ class Message
6
+ include Unchained::Client::Mixins::Resource
7
+
8
+ attribute :id, String
9
+ attribute :message, String
10
+ attribute :duration, Integer
11
+ end
12
+
13
+ def motd(opts={})
14
+ get_resource("#{base_url}/messageoftheday", Message, opts)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -4,23 +4,21 @@ module Unchained
4
4
 
5
5
  class HeroContent
6
6
  include Unchained::Client::Mixins::Resource
7
- resource({
8
- :id => 'id',
9
- :content => 'content',
10
- :priority => 'priority',
11
- :start => 'utcDateStart',
12
- :end => 'utcDateEnd',
13
- })
7
+
8
+ attribute :id, String
9
+ attribute :content, String
10
+ attribute :priority, Integer
11
+ attribute :start, String, json: 'utcDateStart'
12
+ attribute :end, String, json: 'utcDateEnd'
14
13
  end
15
14
 
16
15
  class Alert
17
16
  include Unchained::Client::Mixins::Resource
18
- resource({
19
- :id => 'id',
20
- :message => 'message',
21
- :start => 'utcDateStart',
22
- :end => 'utcDateEnd',
23
- })
17
+
18
+ attribute :id, String
19
+ attribute :message, String
20
+ attribute :start, String, json: 'utcDateStart'
21
+ attribute :end, String, json: 'utcDateEnd'
24
22
  end
25
23
 
26
24
  def patcher_hero_contents(opts={})
@@ -4,12 +4,11 @@ module Unchained
4
4
 
5
5
  class Race
6
6
  include Unchained::Client::Mixins::Resource
7
- resource({
8
- :description => 'description',
9
- :faction => 'faction',
10
- :id => 'id',
11
- :name => 'name',
12
- })
7
+
8
+ attribute :description, String
9
+ attribute :faction, Integer
10
+ attribute :id, Integer
11
+ attribute :name, String
13
12
  end
14
13
 
15
14
  def races(opts={})
@@ -4,18 +4,19 @@ module Unchained
4
4
 
5
5
  class Server
6
6
  include Unchained::Client::Mixins::Resource
7
- resource({
8
- :access_level => 'accessLevel',
9
- :channel_id => 'channelID',
10
- :host => 'host',
11
- :name => 'name',
12
- :player_maximum => 'playerMaximum',
13
- :shard_id => 'shardID',
14
- })
7
+
8
+ attribute :access_level, Integer, json: 'accessLevel'
9
+ attribute :channel_id, Integer, json: 'channelID'
10
+ attribute :host, String
11
+ attribute :name, String
12
+ attribute :player_maximum, Integer, json: 'playerMaximum'
13
+ attribute :shard_id, Integer, json: 'shardID'
15
14
  end
16
15
 
17
- def servers(opts={})
18
- get_resources("#{base_url}/servers", Server, opts)
16
+ def servers(opts={}, channel: nil)
17
+ url = "#{base_url}/servers"
18
+ url += "/#{channel}" unless channel.nil?
19
+ get_resources(url, Server, opts)
19
20
  end
20
21
 
21
22
  end
@@ -3,5 +3,7 @@ module Unchained
3
3
  # TODO: Support more errors.
4
4
  class Error < StandardError; end
5
5
  class NotFound < Error; end
6
+ class InvalidValue < Error; end
7
+ class InvalidAttribute < Error; end
6
8
 
7
9
  end
@@ -16,9 +16,13 @@ module Unchained
16
16
  end
17
17
  end
18
18
 
19
+ def get_resource(url, resource_class, params={})
20
+ resource_class.from_json(get(url, params))
21
+ end
22
+
19
23
  def get_resources(url, resource_class, params={})
20
24
  get(url, params).map do |result|
21
- resource_class.decode_result(result)
25
+ resource_class.from_json(result)
22
26
  end
23
27
  end
24
28