steam_mist 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +29 -0
- data/lib/steam_mist/connector.rb +167 -0
- data/lib/steam_mist/connectors/eager_connector.rb +24 -0
- data/lib/steam_mist/connectors/lazy_connector.rb +33 -0
- data/lib/steam_mist/connectors.rb +11 -0
- data/lib/steam_mist/pseudo_interface/pseudo_method.rb +187 -0
- data/lib/steam_mist/pseudo_interface.rb +75 -0
- data/lib/steam_mist/rcon/listener.rb +114 -0
- data/lib/steam_mist/rcon/packet.rb +146 -0
- data/lib/steam_mist/rcon/packet_factory.rb +45 -0
- data/lib/steam_mist/rcon/pass.rb +123 -0
- data/lib/steam_mist/rcon.rb +47 -0
- data/lib/steam_mist/request_uri.rb +77 -0
- data/lib/steam_mist/schema.rb +42 -0
- data/lib/steam_mist/session.rb +60 -0
- data/lib/steam_mist/version.rb +5 -0
- data/lib/steam_mist.rb +7 -0
- data/spec/cache_spec.rb +25 -0
- data/spec/packet_factory_spec.rb +8 -0
- data/spec/packet_spec.rb +26 -0
- data/spec/pseudo_interface_spec.rb +15 -0
- data/spec/pseudo_method_spec.rb +37 -0
- data/spec/request_uri_spec.rb +19 -0
- data/spec/schema_spec.rb +17 -0
- data/spec/session_spec.rb +9 -0
- metadata +97 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
module SteamMist
|
2
|
+
class Rcon
|
3
|
+
|
4
|
+
# Represents a packet either received from the server or sent by the
|
5
|
+
# client.
|
6
|
+
class Packet
|
7
|
+
|
8
|
+
# This is used as a {#type}. This is for the client, authenticating to
|
9
|
+
# the server.
|
10
|
+
SERVERDATA_AUTH = 3
|
11
|
+
|
12
|
+
# Used for {#type}. This is for the response from the server from the
|
13
|
+
# client.
|
14
|
+
SERVERDATA_AUTH_RESPONSE = 2
|
15
|
+
|
16
|
+
# Used for {#type}. This is for the client executing a response to the
|
17
|
+
# server.
|
18
|
+
SERVERDATA_EXECCOMMAND = 2
|
19
|
+
|
20
|
+
# Used for {#type}. This is for the server sending back response data
|
21
|
+
# for `EXECCOMMAND`.
|
22
|
+
SERVERDATA_RESPONSE_VALUE = 0
|
23
|
+
|
24
|
+
# This matches the requests with their responses.
|
25
|
+
RESPONSE_MATCH = { SERVERDATA_AUTH => SERVERDATA_AUTH_RESPONSE,
|
26
|
+
SERVERDATA_EXECCOMMAND => SERVERDATA_RESPONSE_VALUE }
|
27
|
+
|
28
|
+
# The id of the packet. This is mainly used to match request packets
|
29
|
+
# with their response.
|
30
|
+
#
|
31
|
+
# @return [Numeric]
|
32
|
+
attr_accessor :id
|
33
|
+
|
34
|
+
# The type of the packet.
|
35
|
+
#
|
36
|
+
# @return [Numeric]
|
37
|
+
attr_accessor :type
|
38
|
+
|
39
|
+
# The body of the packet.
|
40
|
+
#
|
41
|
+
# @return [Numeric]
|
42
|
+
attr_accessor :body
|
43
|
+
|
44
|
+
# Initialize the packet.
|
45
|
+
#
|
46
|
+
# @param id [Numeric] the id of the packet.
|
47
|
+
def initialize(id=nil)
|
48
|
+
@id = id || 0
|
49
|
+
@type = SERVERDATA_EXECCOMMAND
|
50
|
+
@body = ""
|
51
|
+
end
|
52
|
+
|
53
|
+
# Formats the packet for sending to the server. See
|
54
|
+
# [this](https://developer.valvesoftware.com/wiki/Source_RCON_Protocol)
|
55
|
+
# on how it's done.
|
56
|
+
#
|
57
|
+
# @return [String] a formatted string containing the data.
|
58
|
+
def format
|
59
|
+
[size, id, type, body].pack("l<l<l<a#{body.bytesize+1}x")
|
60
|
+
end
|
61
|
+
|
62
|
+
# This returns the size of the packet. This is the size of the body, in
|
63
|
+
# bytes. It also adds 10 bytes for the type (4 bytes), id (4 bytes), and
|
64
|
+
# the two nul-terminators (one for the body and one for the packet).
|
65
|
+
#
|
66
|
+
# @return [Numeric]
|
67
|
+
def size
|
68
|
+
body.bytesize + 10
|
69
|
+
end
|
70
|
+
|
71
|
+
# Compare this with another object. Calls {#to_i} on the other object
|
72
|
+
# and this one and delegates to that.
|
73
|
+
#
|
74
|
+
# @return [Numeric]
|
75
|
+
def <=>(other)
|
76
|
+
self.to_i <=> other.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
# Shows whether or not the packet is empty. The packet is not empty if
|
80
|
+
# the body contains more than "" and the type is not 2.
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
83
|
+
def empty?
|
84
|
+
body.empty? && (type != 2)
|
85
|
+
end
|
86
|
+
|
87
|
+
# This checks to see if it is SRCDS's weird packet response.
|
88
|
+
#
|
89
|
+
# @return [Boolean]
|
90
|
+
def weird?
|
91
|
+
(type == 0) and (body == "\x00\x01\x00\x00")
|
92
|
+
end
|
93
|
+
|
94
|
+
# This loads the packet data from a hash. Overwrites the contents of the
|
95
|
+
# packet.
|
96
|
+
#
|
97
|
+
# @param hash [Hash]
|
98
|
+
# @return [self]
|
99
|
+
def load!(hash)
|
100
|
+
self.id = hash[:id] || hash["id"] || id
|
101
|
+
self.type = hash[:type] || hash["type"] || type
|
102
|
+
self.body = hash[:body] || hash["body"] || body
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
alias :to_i :id
|
107
|
+
|
108
|
+
# This takes a formatted string and turns it into a packet instance.
|
109
|
+
# This is mainly used for responses from the server.
|
110
|
+
#
|
111
|
+
# @param raw [String] the raw data from the server.
|
112
|
+
# @return [Packet] the packet representing the data.
|
113
|
+
def self.from_raw(raw)
|
114
|
+
packet = Packet.new
|
115
|
+
size, = raw.unpack("l<")
|
116
|
+
_, packet.id, packet.type, packet.body =
|
117
|
+
raw.unpack("l<l<l<a#{size - 10}xx")
|
118
|
+
packet
|
119
|
+
end
|
120
|
+
|
121
|
+
# This reads from a stream and converts it into a packet.
|
122
|
+
#
|
123
|
+
# @param socket [#read] the stream to read from.
|
124
|
+
# @return [Packet] the packet representing the data.
|
125
|
+
def self.from_stream(socket)
|
126
|
+
packet = Packet.new
|
127
|
+
size, = socket.read(4).unpack("l<")
|
128
|
+
packet.id, packet.type, packet.body =
|
129
|
+
socket.read(size).unpack("l<l<a#{size - 10}xx")
|
130
|
+
packet
|
131
|
+
end
|
132
|
+
|
133
|
+
# This sets up the packet from a given hash.
|
134
|
+
#
|
135
|
+
# @param data [Hash] the data to map to the packet.
|
136
|
+
# @return [Packet]
|
137
|
+
def self.from_hash(data)
|
138
|
+
packet = Packet.new
|
139
|
+
packet.load! data
|
140
|
+
|
141
|
+
packet
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module SteamMist
|
4
|
+
class Rcon
|
5
|
+
|
6
|
+
# Creates packets for use.
|
7
|
+
class PacketFactory
|
8
|
+
|
9
|
+
# An {SortedSet} containing all of the packets that have been created.
|
10
|
+
#
|
11
|
+
# @return [SortedSet]
|
12
|
+
attr_accessor :packets
|
13
|
+
|
14
|
+
# The current packet number.
|
15
|
+
#
|
16
|
+
# @return [Numeric]
|
17
|
+
attr_accessor :number
|
18
|
+
|
19
|
+
# initializes.
|
20
|
+
def initialize
|
21
|
+
@number = 1
|
22
|
+
@packets = SortedSet.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates a packet. Automatically assigns it an id, based off of the
|
26
|
+
# current packet number.
|
27
|
+
#
|
28
|
+
# @return [Packet]
|
29
|
+
def create_packet
|
30
|
+
packet = Packet.new(@number)
|
31
|
+
@packets << packet
|
32
|
+
@number = @number + 1
|
33
|
+
packet
|
34
|
+
end
|
35
|
+
|
36
|
+
# Pretty inspect
|
37
|
+
#
|
38
|
+
# @return [String]
|
39
|
+
def inspect
|
40
|
+
"#<SteamMist::Rcon::PacketFactory #{@number}>"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module SteamMist
|
2
|
+
class Rcon
|
3
|
+
|
4
|
+
# This basically handles sending and receiving packets.
|
5
|
+
class Pass
|
6
|
+
|
7
|
+
# The packet factory that should be used when creating packets. This
|
8
|
+
# factory is used to increment the ID number on sequential packets.
|
9
|
+
#
|
10
|
+
# @return [PacketFactory]
|
11
|
+
attr_reader :packet_factory
|
12
|
+
|
13
|
+
# The listener for sending and receiving data from the server.
|
14
|
+
#
|
15
|
+
# @return [Listener]
|
16
|
+
attr_reader :listener
|
17
|
+
|
18
|
+
# Initialize.
|
19
|
+
#
|
20
|
+
# @param ip [String] the IP address of the Rcon server. Passed to
|
21
|
+
# Listener.
|
22
|
+
# @param port [Numeric] the port of the Rcon server. Passed to Listener.
|
23
|
+
def initialize(ip, port = 27015)
|
24
|
+
@packet_factory = PacketFactory.new
|
25
|
+
@listener = Listener.new ip, port
|
26
|
+
@empty_packet = Packet.from_hash :type => Packet::SERVERDATA_RESPONSE_VALUE
|
27
|
+
end
|
28
|
+
|
29
|
+
# Retrieves the next packet from the stream. If a block is given, it
|
30
|
+
# yields to that.
|
31
|
+
#
|
32
|
+
# @yieldparam packet [Packet]
|
33
|
+
# @return [Packet]
|
34
|
+
def on_packet(&block)
|
35
|
+
ensure_connected.on_data do |con|
|
36
|
+
packet = Packet.from_stream con
|
37
|
+
|
38
|
+
if block
|
39
|
+
block.call packet
|
40
|
+
end
|
41
|
+
|
42
|
+
packet
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sends the given packet to the server.
|
47
|
+
#
|
48
|
+
# @param packet [Packet]
|
49
|
+
# @return [void, Numeric]
|
50
|
+
def write(packet)
|
51
|
+
ensure_connected.write packet.format
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sends a packet along with an empty packet. This allows the client to
|
55
|
+
# figure out the server sent multiple packets to the client for the same
|
56
|
+
# request packet. Returns an array of packets if the server sent
|
57
|
+
# multiple packets.
|
58
|
+
#
|
59
|
+
# @param packet [Packet] the packet to send
|
60
|
+
# @return [Packet, Array<Packet>]
|
61
|
+
def send_packet(packet)
|
62
|
+
empty_packet = @empty_packet.dup
|
63
|
+
empty_packet.id = packet.id
|
64
|
+
write(packet) and write(empty_packet)
|
65
|
+
response_packets = []
|
66
|
+
|
67
|
+
begin
|
68
|
+
while response_packets.empty? || !response_packets.last.weird? do
|
69
|
+
response_packets << on_packet
|
70
|
+
end
|
71
|
+
|
72
|
+
rescue TimeoutError; end
|
73
|
+
|
74
|
+
response_packets.pop(2)
|
75
|
+
|
76
|
+
if response_packets.length == 1
|
77
|
+
response_packets[0]
|
78
|
+
else
|
79
|
+
response_packets
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# This authenticates the connection with the server. A password is
|
84
|
+
# required.
|
85
|
+
#
|
86
|
+
# @param password [String] the password to authenticate with.
|
87
|
+
# @return [Boolean]
|
88
|
+
def auth(password)
|
89
|
+
packet = packet_factory.create_packet
|
90
|
+
packet.type = Packet::SERVERDATA_AUTH
|
91
|
+
packet.body = password
|
92
|
+
|
93
|
+
#response = send_packet packet
|
94
|
+
write packet
|
95
|
+
|
96
|
+
# discard
|
97
|
+
on_packet
|
98
|
+
|
99
|
+
response = on_packet
|
100
|
+
|
101
|
+
response.id == packet.id
|
102
|
+
end
|
103
|
+
|
104
|
+
# This closes the connection by calling {Listener#close}.
|
105
|
+
#
|
106
|
+
# @return [void]
|
107
|
+
def close
|
108
|
+
listener.close
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# This makes sure the listener is connected before trying to perform an
|
114
|
+
# I/O operation.
|
115
|
+
#
|
116
|
+
# @return [Listener]
|
117
|
+
def ensure_connected
|
118
|
+
listener.bind! unless listener.connection
|
119
|
+
listener
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'steam_mist/rcon/pass'
|
3
|
+
require 'steam_mist/rcon/packet'
|
4
|
+
require 'steam_mist/rcon/listener'
|
5
|
+
require 'steam_mist/rcon/packet_factory'
|
6
|
+
|
7
|
+
module SteamMist
|
8
|
+
|
9
|
+
# This class provides RCON capabilities.
|
10
|
+
class Rcon
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
# This is the class that handles reading and writing packets from the
|
15
|
+
# listener.
|
16
|
+
#
|
17
|
+
# @return [Pass]
|
18
|
+
attr_reader :pass
|
19
|
+
|
20
|
+
# Initialize.
|
21
|
+
#
|
22
|
+
# @param ip [String] the IP to connect to.
|
23
|
+
# @param port [Numeric] the port to connect to.
|
24
|
+
def initialize(ip, port = 27015)
|
25
|
+
@pass = Pass.new(ip, port)
|
26
|
+
end
|
27
|
+
|
28
|
+
# This takes a hash, turns it into a packet, and sends it.
|
29
|
+
#
|
30
|
+
# @param hash [Hash] the data to turn into a packet.
|
31
|
+
# @return [Packet, Array<Packet>]
|
32
|
+
def send(hash)
|
33
|
+
send_packet packet_factory.create_packet.load!(hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Pretty inspect
|
37
|
+
#
|
38
|
+
# @return [String]
|
39
|
+
def inspect
|
40
|
+
"#<SteamMist::Rcon>"
|
41
|
+
end
|
42
|
+
|
43
|
+
def_delegators :@pass, :on_packet, :send_packet, :auth, :close,
|
44
|
+
:packet_factory
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module SteamMist
|
2
|
+
|
3
|
+
# This represents a request that may be made to the steam api. It is mainly
|
4
|
+
# used for obtaining paths to request to.
|
5
|
+
class RequestUri
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# This is the interface the request will be made to, like +ISteamUser+.
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
attr_accessor :interface
|
13
|
+
|
14
|
+
# This is the method of the interface the request will be made to.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
attr_accessor :method
|
18
|
+
|
19
|
+
# These are the arguments that will be passed for the request.
|
20
|
+
#
|
21
|
+
# @return [Enumerable]
|
22
|
+
attr_accessor :arguments
|
23
|
+
|
24
|
+
# The version of the method to request.
|
25
|
+
#
|
26
|
+
# @return [Numeric]
|
27
|
+
attr_accessor :version
|
28
|
+
|
29
|
+
# The domain to make the reuqest to.
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
attr_accessor :domain
|
33
|
+
|
34
|
+
# Initialize the request. Can take a hash. Options for the hash can be
|
35
|
+
# `:interface`, `:method`, `:version`, `:domain` and `:arguments`.
|
36
|
+
# Anything else will cause an argument error. See the attributes for
|
37
|
+
# each respectively on what they're for.
|
38
|
+
#
|
39
|
+
# @param options [Hash] the options to be used.
|
40
|
+
def initialize(options)
|
41
|
+
{
|
42
|
+
:interface => "",
|
43
|
+
:method => "",
|
44
|
+
:arguments => {},
|
45
|
+
:version => 0,
|
46
|
+
:domain => "api.steampowered.com"
|
47
|
+
}.merge(options).each do |k, v|
|
48
|
+
if respond_to? "#{k}="
|
49
|
+
send "#{k}=", v
|
50
|
+
else
|
51
|
+
raise ArgumentError, "don't know how to handle #{k}!"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Takes the request data and formats it into an URI.
|
57
|
+
#
|
58
|
+
# @return [URI] the URI of the request.
|
59
|
+
def format_uri
|
60
|
+
basic = "http://%s/%s/%s/v%04d" % [domain, interface, method, version]
|
61
|
+
|
62
|
+
uri = URI(basic)
|
63
|
+
uri.query = URI.encode_www_form(arguments)
|
64
|
+
|
65
|
+
uri
|
66
|
+
end
|
67
|
+
|
68
|
+
# Outputs a string version of the request.
|
69
|
+
#
|
70
|
+
# @return [String] the fully formated URL of the request.
|
71
|
+
def to_s
|
72
|
+
format_uri.to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
def_delegator :format_uri, :open
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'hashie/mash'
|
3
|
+
|
4
|
+
module SteamMist
|
5
|
+
|
6
|
+
# Handles the Schema for games.
|
7
|
+
#
|
8
|
+
# @todo More tests.
|
9
|
+
class Schema
|
10
|
+
|
11
|
+
# Initialize the schema. The first argument is the application id,
|
12
|
+
# which valve uses internally to distinguish games from each other.
|
13
|
+
#
|
14
|
+
# @param app_id [Numeric]
|
15
|
+
# @param lang [String] the language the schema should return its
|
16
|
+
# results in.
|
17
|
+
def initialize(key, app_id, lang = nil)
|
18
|
+
@app_id = app_id
|
19
|
+
@session = Session.new
|
20
|
+
@get_schema = @session.get_interface("IEconItems_#{app_id}").get_schema
|
21
|
+
|
22
|
+
if lang
|
23
|
+
@get_schema.with_arguments!(:language => lang)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a list of items in the schema.
|
28
|
+
#
|
29
|
+
# @return [SortedSet<Item>]
|
30
|
+
def items
|
31
|
+
@_items ||= data.items
|
32
|
+
end
|
33
|
+
|
34
|
+
# Retrieves the data from the request.
|
35
|
+
#
|
36
|
+
# @return [Hashie::Mash]
|
37
|
+
def data
|
38
|
+
@_data ||= Hashie::Mash.new(@get_schema.get.data["result"])
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# We're placing these here so that if someone wants to only use this part of
|
2
|
+
# the library, they don't have to require the entire thing.
|
3
|
+
require 'oj'
|
4
|
+
require 'uri'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'steam_mist/version'
|
7
|
+
require 'steam_mist/connector'
|
8
|
+
require 'steam_mist/connectors'
|
9
|
+
require 'steam_mist/request_uri'
|
10
|
+
require 'steam_mist/pseudo_interface'
|
11
|
+
require 'steam_mist/schema'
|
12
|
+
|
13
|
+
module SteamMist
|
14
|
+
|
15
|
+
# The session is used as a starting point for connecting to the Steam API.
|
16
|
+
class Session
|
17
|
+
|
18
|
+
# The connector that the session is going to use.
|
19
|
+
#
|
20
|
+
# @return [Class] the connector.
|
21
|
+
attr_accessor :connector
|
22
|
+
|
23
|
+
# The default arguments that the session will use.
|
24
|
+
#
|
25
|
+
# @return [Hash]
|
26
|
+
attr_accessor :default_arguments
|
27
|
+
|
28
|
+
# Initialize.
|
29
|
+
#
|
30
|
+
# @param connector [Class] the connector (should be subclass of {Connector}).
|
31
|
+
def initialize(connector = Connectors::LazyConnector)
|
32
|
+
@connector = connector
|
33
|
+
@_interfaces = {}
|
34
|
+
@default_arguments = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Grabs an interface for use. These are cached, so every call with the
|
38
|
+
# same argument returns the same object.
|
39
|
+
#
|
40
|
+
# @param interface [Symbol] the interface name.
|
41
|
+
# @return [PseudoInterface]
|
42
|
+
def get_interface(interface)
|
43
|
+
@_interfaces[interface] ||= PseudoInterface.new(self, interface)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Some {#method_missing} magic. Sorry @charliesome!
|
47
|
+
#
|
48
|
+
# @see {#get_interface}
|
49
|
+
def method_missing(method, *args)
|
50
|
+
super if args.length > 0 or block_given?
|
51
|
+
get_interface method
|
52
|
+
end
|
53
|
+
|
54
|
+
# pretty inspection
|
55
|
+
def inspect
|
56
|
+
"#<SteamMist::Session #{connector.inspect}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/lib/steam_mist.rb
ADDED
data/spec/cache_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
describe SteamMist::Connectors::LazyConnector do
|
2
|
+
|
3
|
+
subject { described_class.new(request_uri) }
|
4
|
+
let(:request_uri) { "https://api.twitter.com/1/statuses/oembed.json?id=133640144317198338" }
|
5
|
+
|
6
|
+
it "can cache" do
|
7
|
+
subject.enable_caching "tmp/example_cache.json"
|
8
|
+
expect(subject).to be_cache
|
9
|
+
end
|
10
|
+
|
11
|
+
it "does cache" do
|
12
|
+
subject.enable_caching "tmp/example_cache.json"
|
13
|
+
subject.data
|
14
|
+
expect { |p| subject.send(:with_cache, &p) }.to_not yield_control
|
15
|
+
end
|
16
|
+
|
17
|
+
it "loads from cache file" do
|
18
|
+
subject.enable_caching "tmp/example_cache.json"
|
19
|
+
expect { |p| subject.send(:with_cache, &p) }.to_not yield_control
|
20
|
+
end
|
21
|
+
|
22
|
+
after :all do
|
23
|
+
File.unlink "tmp/example_cache.json"
|
24
|
+
end
|
25
|
+
end
|
data/spec/packet_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
describe SteamMist::Rcon::Packet do
|
4
|
+
it "should format itself correctly" do
|
5
|
+
subject.id = 5
|
6
|
+
subject.type = 2
|
7
|
+
subject.body << "hello world"
|
8
|
+
|
9
|
+
subject.format.should eq "\x15\0\0\0\x05\0\0\0\x02\0\0\0hello world\0\0"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should properly deserialize data from raw" do
|
13
|
+
packet = described_class.from_raw "\x15\0\0\0\x05\0\0\0\x02\0\0\0hello world\0\0"
|
14
|
+
packet.id.should be 5
|
15
|
+
packet.type.should be 2
|
16
|
+
packet.body.should eq "hello world"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should properly deserialize data from a stream" do
|
20
|
+
s = StringIO.new "\x15\0\0\0\x05\0\0\0\x02\0\0\0hello world\0\0"
|
21
|
+
packet = described_class.from_stream s
|
22
|
+
packet.id.should be 5
|
23
|
+
packet.type.should be 2
|
24
|
+
packet.body.should eq "hello world"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
describe SteamMist::PseudoInterface do
|
2
|
+
|
3
|
+
subject(:interface) do
|
4
|
+
described_class.new(nil, :some_interface)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "turns the interface name into the correct api name" do
|
8
|
+
interface.api_name.should == "ISomeInterface"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "gives a pseudo method" do
|
12
|
+
interface.get_method(:some_method) \
|
13
|
+
.should be_instance_of SteamMist::PseudoInterface::PseudoMethod
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe SteamMist::PseudoInterface::PseudoMethod do
|
2
|
+
|
3
|
+
subject(:pseudo_method) do
|
4
|
+
session = SteamMist::Session.new(SteamMist::Connectors::LazyConnector)
|
5
|
+
session.default_arguments[:something] = "value"
|
6
|
+
interface = SteamMist::PseudoInterface.new(session, :some_interface)
|
7
|
+
described_class.new(interface, :some_method, 4)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should turn the method name into an api name" do
|
11
|
+
pseudo_method.api_name.should == "SomeMethod"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return a copy when adding arguments" do
|
15
|
+
pseudo_method.with_arguments(:hello => "world").should_not be pseudo_method
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return a copy when changing versions" do
|
19
|
+
pseudo_method.with_version(3).should_not be pseudo_method
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return a request uri" do
|
23
|
+
pseudo_method.request_uri.should be_instance_of SteamMist::RequestUri
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should give a connector instance" do
|
27
|
+
pseudo_method.get.should be_instance_of SteamMist::Connectors::LazyConnector
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should use default arguments" do
|
31
|
+
pseudo_method.request_uri.arguments.should include(:something => "value")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should give a connector with caching" do
|
35
|
+
expect(pseudo_method.with_caching("some_file").get).to be_cache
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe SteamMist::RequestUri do
|
2
|
+
|
3
|
+
subject(:request_uri) do
|
4
|
+
SteamMist::RequestUri.new(:interface => "ISomeInterface", :method => "SomeMethod",
|
5
|
+
:version => 1)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should raise an argument error" do
|
9
|
+
expect { described_class.new(:something => :else) }.to raise_error(ArgumentError)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return a uri" do
|
13
|
+
request_uri.format_uri.should be_instance_of(URI::HTTP)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should format the right string" do
|
17
|
+
request_uri.to_s.should eq "http://api.steampowered.com/ISomeInterface/SomeMethod/v0001?"
|
18
|
+
end
|
19
|
+
end
|