desmos 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +45 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +3 -0
- data/desmos.gemspec +91 -0
- data/lib/desmos.rb +19 -0
- data/lib/desmos/configuration.rb +66 -0
- data/lib/desmos/debug_mode.rb +9 -0
- data/lib/desmos/errors.rb +10 -0
- data/lib/desmos/request_support.rb +44 -0
- data/lib/desmos/student.rb +10 -0
- data/lib/desmos/tutor.rb +10 -0
- data/lib/desmos/user.rb +34 -0
- data/lib/desmos/utils.rb +64 -0
- data/lib/desmos/whiteboard.rb +96 -0
- data/spec/desmos/configuration_spec.rb +105 -0
- data/spec/desmos/request_support_spec.rb +99 -0
- data/spec/desmos/student_spec.rb +38 -0
- data/spec/desmos/tutor_spec.rb +38 -0
- data/spec/desmos/user_spec.rb +38 -0
- data/spec/desmos/utils_spec.rb +20 -0
- data/spec/desmos/whiteboard_spec.rb +203 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/configure.rb +9 -0
- metadata +226 -0
data/lib/desmos/utils.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Desmos
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
# Taken from Rack::Utils, 1.2.1 to remove Rack dependency.
|
5
|
+
def escape(s)
|
6
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/u) {
|
7
|
+
'%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
8
|
+
}.tr(' ', '+')
|
9
|
+
end
|
10
|
+
module_function :escape
|
11
|
+
|
12
|
+
# Params are NOT escaped.
|
13
|
+
def traverse_params_hash(hash, result = nil, current_key = nil)
|
14
|
+
result ||= { :files => [], :params => [] }
|
15
|
+
|
16
|
+
hash.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |key|
|
17
|
+
new_key = (current_key ? "#{current_key}[#{key}]" : key).to_s
|
18
|
+
case hash[key]
|
19
|
+
when Hash
|
20
|
+
traverse_params_hash(hash[key], result, new_key)
|
21
|
+
when Array
|
22
|
+
array_key = "#{new_key}[]"
|
23
|
+
hash[key].each do |v|
|
24
|
+
result[:params] << [array_key, v.to_s]
|
25
|
+
end
|
26
|
+
when File
|
27
|
+
filename = File.basename(hash[key].path)
|
28
|
+
types = MIME::Types.type_for(filename)
|
29
|
+
result[:files] << [
|
30
|
+
new_key,
|
31
|
+
filename,
|
32
|
+
types.empty? ? 'application/octet-stream' : types[0].to_s,
|
33
|
+
File.expand_path(hash[key].path)
|
34
|
+
]
|
35
|
+
else
|
36
|
+
result[:params] << [new_key, hash[key].to_s]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
module_function :traverse_params_hash
|
42
|
+
|
43
|
+
def traversal_to_param_string(traversal, escape = true)
|
44
|
+
traversal[:params].collect { |param|
|
45
|
+
"#{Desmos::Utils.escape(param[0])}=#{Desmos::Utils.escape(param[1])}"
|
46
|
+
}.join('&')
|
47
|
+
end
|
48
|
+
module_function :traversal_to_param_string
|
49
|
+
|
50
|
+
# Return the bytesize of String; uses String#size under Ruby 1.8 and
|
51
|
+
# String#bytesize under 1.9.
|
52
|
+
if ''.respond_to?(:bytesize)
|
53
|
+
def bytesize(string)
|
54
|
+
string.bytesize
|
55
|
+
end
|
56
|
+
else
|
57
|
+
def bytesize(string)
|
58
|
+
string.size
|
59
|
+
end
|
60
|
+
end
|
61
|
+
module_function :bytesize
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Desmos
|
2
|
+
class Whiteboard
|
3
|
+
include RequestSupport
|
4
|
+
attr_accessor :hash, :title, :tutor, :students
|
5
|
+
|
6
|
+
def self.find(hash)
|
7
|
+
new(:hash => hash).get
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.create(options = {})
|
11
|
+
new(options).save
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@hash = options[:hash] # optional: The API will generate this if not provided. Must be unique if provided.
|
16
|
+
@title = options[:title] # optional
|
17
|
+
|
18
|
+
# optional: Should be a Desmos::Tutor object
|
19
|
+
if options[:tutor] && !(Tutor === options[:tutor])
|
20
|
+
raise ArgumentError, ':tutor option must be either of type Desmos::Tutor or NilClass'
|
21
|
+
end
|
22
|
+
@tutor = options[:tutor]
|
23
|
+
|
24
|
+
# optional: Should be an Array of Desmos::Student objects
|
25
|
+
if options[:students] && (!options[:students].respond_to?(:all?) || !options[:students].all? { |option| Desmos::Student === option })
|
26
|
+
raise ArgumentError, ':students option must be either an Array containing object of type Desmos::Student or NilClass'
|
27
|
+
end
|
28
|
+
@students = options[:students] || []
|
29
|
+
end
|
30
|
+
|
31
|
+
def save
|
32
|
+
parsed_response = request!(:whiteboard, :create, request_options)
|
33
|
+
if parsed_response.fetch(:success) == 'false'
|
34
|
+
raise RequestError, parsed_response[:error_message] unless parsed_response[:error_message] == 'whiteboard already exists'
|
35
|
+
else
|
36
|
+
self.hash = parsed_response.fetch(:hash)
|
37
|
+
end
|
38
|
+
add_tutor
|
39
|
+
add_students
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_tutor
|
44
|
+
if tutor
|
45
|
+
parsed_response = request!(:whiteboard, :add_user, tutor.request_options.merge(:whiteboard_hash => hash))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_students
|
50
|
+
unless students.empty?
|
51
|
+
students.each do |student|
|
52
|
+
add_student(student)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_student(student)
|
58
|
+
parsed_response = request!(:whiteboard, :add_user, student.request_options.merge(:whiteboard_hash => hash))
|
59
|
+
end
|
60
|
+
|
61
|
+
def get
|
62
|
+
parsed_response = request!(:whiteboard, :read, :whiteboard_hash => hash)
|
63
|
+
if parsed_response[:success] == 'false'
|
64
|
+
case parsed_response[:error_message]
|
65
|
+
when 'Whiteboard not found'
|
66
|
+
raise WhiteboardNotFound, "Whiteboard with HASH=#{hash} could not be found"
|
67
|
+
else
|
68
|
+
raise RequestError, parsed_response[:error_message]
|
69
|
+
end
|
70
|
+
else
|
71
|
+
build_from_hash(parsed_response)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_options
|
76
|
+
options = {}
|
77
|
+
options.merge!(:hash => hash) if hash
|
78
|
+
options.merge!(:title => title) if title
|
79
|
+
if tutor
|
80
|
+
options.merge!(:tutor_name => tutor.name) if tutor.name
|
81
|
+
options.merge!(:tutor_id => tutor.id) if tutor.id
|
82
|
+
options.merge!(:tutor_hash => tutor.hash) if tutor.hash
|
83
|
+
end
|
84
|
+
options
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_from_hash(options)
|
88
|
+
self.hash = options[:hash]
|
89
|
+
self.title = options[:title]
|
90
|
+
self.tutor = Tutor.build_from_hash(options[:tutor])
|
91
|
+
self.students = options[:students].collect { |student_attributes| Student.new(student_attributes) }
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Desmos::Configuration, '.domain' do
|
4
|
+
|
5
|
+
it 'has a domain attribute, that when set will return the domain' do
|
6
|
+
Desmos::Configuration.should respond_to(:domain)
|
7
|
+
Desmos::Configuration.domain = 'https://example.com'
|
8
|
+
Desmos::Configuration.domain.should eql('https://example.com')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'raises a ConfigurationError if domain is called without having been set' do
|
12
|
+
Desmos::Configuration.domain = nil
|
13
|
+
lambda { Desmos::Configuration.domain }.should raise_error(Desmos::ConfigurationError, 'Desmos::Configuration.domain is a required configuration value.')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'adds https:// scheme if none is provided' do
|
17
|
+
Desmos::Configuration.domain = 'something.com'
|
18
|
+
Desmos::Configuration.domain.should eql('https://something.com')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'converts a regular http:// scheme to https:// if needed' do
|
22
|
+
Desmos::Configuration.domain = 'http://something.com'
|
23
|
+
Desmos::Configuration.domain.should eql('https://something.com')
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe Desmos::Configuration, '.version' do
|
29
|
+
|
30
|
+
it 'has a version attribute, that when set will return the version' do
|
31
|
+
Desmos::Configuration.should respond_to(:version)
|
32
|
+
Desmos::Configuration.version = 'api_v1'
|
33
|
+
Desmos::Configuration.version.should eql('api_v1')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises a ConfigurationError if version is called without having been set' do
|
37
|
+
Desmos::Configuration.version = nil
|
38
|
+
lambda { Desmos::Configuration.version }.should raise_error(Desmos::ConfigurationError, 'Desmos::Configuration.version is a required configuration value.')
|
39
|
+
end
|
40
|
+
|
41
|
+
it "prepends the version with 'api_v' if that part of the string does not exist" do
|
42
|
+
Desmos::Configuration.version = 1
|
43
|
+
Desmos::Configuration.version.should eql('api_v1')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "prepends the version with 'api_v' if that part of the string does not exist" do
|
47
|
+
Desmos::Configuration.version = '1'
|
48
|
+
Desmos::Configuration.version.should eql('api_v1')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "parses out a number if there is one to use as the version" do
|
52
|
+
Desmos::Configuration.version = 'this really 4 should not work'
|
53
|
+
Desmos::Configuration.version.should eql('api_v4')
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
describe Desmos::Configuration, '.key' do
|
59
|
+
|
60
|
+
it 'has a key attributes, that when set will return the key' do
|
61
|
+
Desmos::Configuration.should respond_to(:key)
|
62
|
+
Desmos::Configuration.key = '133'
|
63
|
+
Desmos::Configuration.key.should eql('133')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'raises a ConfigurationError if key is called without having been set' do
|
67
|
+
Desmos::Configuration.key = nil
|
68
|
+
lambda { Desmos::Configuration.key }.should raise_error(Desmos::ConfigurationError, 'Desmos::Configuration.key is a required configuration value.')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'converts whatever is passed in into a String' do
|
72
|
+
Desmos::Configuration.key = 133
|
73
|
+
Desmos::Configuration.key.should eql('133')
|
74
|
+
|
75
|
+
Desmos::Configuration.key = true
|
76
|
+
Desmos::Configuration.key.should eql('true')
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe Desmos::Configuration, '.secret' do
|
82
|
+
|
83
|
+
it 'has a secret attribute, that when set will return the secret' do
|
84
|
+
Desmos::Configuration.should respond_to(:secret)
|
85
|
+
Desmos::Configuration.secret = 'abcde'
|
86
|
+
Desmos::Configuration.secret.should eql('abcde')
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'raises a ConfigurationError is secret is called without having been set' do
|
90
|
+
Desmos::Configuration.secret = nil
|
91
|
+
lambda { Desmos::Configuration.secret }.should raise_error(Desmos::ConfigurationError, 'Desmos::Configuration.secret is a required configuration value.')
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
describe Desmos::Configuration, '.debug_mode' do
|
97
|
+
|
98
|
+
it 'has a debug_mode attriubute that defaults to false' do
|
99
|
+
Desmos::Configuration.should respond_to(:debug_mode)
|
100
|
+
Desmos::Configuration.debug_mode.should eql(false)
|
101
|
+
Desmos::Configuration.debug_mode = true
|
102
|
+
Desmos::Configuration.debug_mode.should eql(true)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Desmos::RequestSupport, '#request!' do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
class_instance = Class.new
|
7
|
+
class_instance.send(:include, Desmos::RequestSupport)
|
8
|
+
@instance = class_instance.new
|
9
|
+
|
10
|
+
stub_request(:get, /https\:\/\/api\.tutortrove\.com\/api_v1\/whiteboard\/create/).
|
11
|
+
to_return(:status => 200, :body => "{\"success\": \"true\", \"title\": \"Test Session\", \"hash\": \"abcde\"}")
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'makes a request to the Desmos API with the given object, method, and options and returns a response parsed from JSON' do
|
15
|
+
response = @instance.request!(:whiteboard, :create, { :title => 'Test Session' })
|
16
|
+
response.should eql({
|
17
|
+
:success => 'true',
|
18
|
+
:hash => 'abcde',
|
19
|
+
:title => 'Test Session'
|
20
|
+
})
|
21
|
+
assert_requested :get, /https\:\/\/api\.tutortrove\.com\/api_v1\/whiteboard\/create/, :times => 1
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Desmos::RequestSupport, '#make_request' do
|
27
|
+
|
28
|
+
before(:each) do
|
29
|
+
class_instance = Class.new
|
30
|
+
class_instance.send(:include, Desmos::RequestSupport)
|
31
|
+
@instance = class_instance.new
|
32
|
+
|
33
|
+
stub_request(:get, /https\:\/\/api\.tutortrove\.com\/api_v1\/whiteboard\/create/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'makes a request to the Desmos API with the given object, method, and options and returns a response from Net::HTTP' do
|
37
|
+
response = @instance.make_request(:whiteboard, :create, { :title => 'Test Session' })
|
38
|
+
response.should be_instance_of(Net::HTTPOK)
|
39
|
+
assert_requested :get, /https\:\/\/api\.tutortrove\.com\/api_v1\/whiteboard\/create/, :times => 1
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe Desmos::RequestSupport, '#build_uri' do
|
45
|
+
|
46
|
+
before(:each) do
|
47
|
+
class_instance = Class.new
|
48
|
+
class_instance.send(:include, Desmos::RequestSupport)
|
49
|
+
@instance = class_instance.new
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'builds a uri for the Desmos API from the given object, method, and options and returns the URI object' do
|
53
|
+
uri = @instance.build_uri(:whiteboard, :create, { :title => 'Test Session' })
|
54
|
+
uri.should be_instance_of(URI::HTTPS)
|
55
|
+
uri.path.should eql('/api_v1/whiteboard/create')
|
56
|
+
uri.query.should eql('title=Test+Session')
|
57
|
+
uri.host.should eql(Desmos::Configuration.domain.split('://')[1])
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
describe Desmos::RequestSupport, '#build_http' do
|
63
|
+
|
64
|
+
before(:each) do
|
65
|
+
class_instance = Class.new
|
66
|
+
class_instance.send(:include, Desmos::RequestSupport)
|
67
|
+
@instance = class_instance.new
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'builds and returns a Net::HTTP instance setup for SSL' do
|
71
|
+
http = @instance.build_http(@instance.build_uri(:whiteboard, :create, { :title => 'Test Session' }))
|
72
|
+
http.should be_instance_of(Net::HTTP)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Desmos::RequestSupport, '#parse_response' do
|
78
|
+
|
79
|
+
before(:each) do
|
80
|
+
class_instance = Class.new
|
81
|
+
class_instance.send(:include, Desmos::RequestSupport)
|
82
|
+
@instance = class_instance.new
|
83
|
+
|
84
|
+
stub_request(:get, /https\:\/\/api\.tutortrove\.com\/api_v1\/whiteboard\/create/).
|
85
|
+
to_return(:status => 200, :body => "{\"success\": \"true\", \"title\": \"Test Session\", \"hash\": \"abcde\"}")
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'parses the JSON response string into a Hash' do
|
89
|
+
response = @instance.make_request(:whiteboard, :create, { :title => 'Test Session', :hash => 'abcde' })
|
90
|
+
parsed_response = @instance.parse_response(response)
|
91
|
+
parsed_response.should be_instance_of(Hash)
|
92
|
+
parsed_response.should eql({
|
93
|
+
:success => 'true',
|
94
|
+
:title => 'Test Session',
|
95
|
+
:hash => 'abcde'
|
96
|
+
})
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Desmos::Student, '.new' do
|
4
|
+
|
5
|
+
it 'sets the instance variables for the student' do
|
6
|
+
student = Desmos::Student.new(
|
7
|
+
:id => 1,
|
8
|
+
:hash => 'abcde',
|
9
|
+
:name => 'Test',
|
10
|
+
:last_name => 'Student',
|
11
|
+
:family_name => 'StudentName',
|
12
|
+
:skype => 'test_student',
|
13
|
+
:email => 'test_student@example.com'
|
14
|
+
)
|
15
|
+
student.id.should eql(1)
|
16
|
+
student.hash.should eql('abcde')
|
17
|
+
student.type.should eql('student')
|
18
|
+
student.name.should eql('Test')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'requires a name attribute' do
|
22
|
+
lambda { Desmos::Student.new(:id => 1) }.should raise_error(ArgumentError, ':name is a required attribute')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'requires an id attribute' do
|
26
|
+
lambda { Desmos::Student.new(:name => 'test') }.should raise_error(ArgumentError, ':id is a required attribute')
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Desmos::Student, '.build_from_hash' do
|
32
|
+
|
33
|
+
it 'returns a Desmos::Student object' do
|
34
|
+
student = Desmos::Student.build_from_hash(:id => 1, :name => 'test')
|
35
|
+
student.should be_instance_of(Desmos::Student)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Desmos::Tutor, '.new' do
|
4
|
+
|
5
|
+
it 'sets the instance variables for the tutor' do
|
6
|
+
tutor = Desmos::Tutor.new(
|
7
|
+
:id => 1,
|
8
|
+
:hash => 'abcde',
|
9
|
+
:name => 'Test',
|
10
|
+
:last_name => 'Tutor',
|
11
|
+
:family_name => 'TutorName',
|
12
|
+
:skype => 'test_tutor',
|
13
|
+
:email => 'test_tutor@example.com'
|
14
|
+
)
|
15
|
+
tutor.id.should eql(1)
|
16
|
+
tutor.hash.should eql('abcde')
|
17
|
+
tutor.type.should eql('tutor')
|
18
|
+
tutor.name.should eql('Test')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'requires a name attribute' do
|
22
|
+
lambda { Desmos::Tutor.new(:id => 1) }.should raise_error(ArgumentError, ':name is a required attribute')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'requires an id attribute' do
|
26
|
+
lambda { Desmos::Tutor.new(:name => 'test') }.should raise_error(ArgumentError, ':id is a required attribute')
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Desmos::Tutor, '.build_from_hash' do
|
32
|
+
|
33
|
+
it 'returns a Desmos::Tutor object' do
|
34
|
+
tutor = Desmos::Tutor.build_from_hash(:id => 1, :name => 'test')
|
35
|
+
tutor.should be_instance_of(Desmos::Tutor)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Desmos::User, '.new' do
|
4
|
+
|
5
|
+
it 'sets the instance variables for the tutor' do
|
6
|
+
user = Desmos::User.new(
|
7
|
+
:id => 1,
|
8
|
+
:hash => 'abcde',
|
9
|
+
:name => 'Test',
|
10
|
+
:last_name => 'User',
|
11
|
+
:family_name => 'UserName',
|
12
|
+
:skype => 'test_user',
|
13
|
+
:email => 'test_user@example.com'
|
14
|
+
)
|
15
|
+
user.id.should eql(1)
|
16
|
+
user.hash.should eql('abcde')
|
17
|
+
user.type.should eql('user')
|
18
|
+
user.name.should eql('Test')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'requires a name attribute' do
|
22
|
+
lambda { Desmos::User.new(:id => 1) }.should raise_error(ArgumentError, ':name is a required attribute')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'requires an id attribute' do
|
26
|
+
lambda { Desmos::User.new(:name => 'test') }.should raise_error(ArgumentError, ':id is a required attribute')
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Desmos::User, '.build_from_hash' do
|
32
|
+
|
33
|
+
it 'returns a Desmos::User object' do
|
34
|
+
user = Desmos::User.build_from_hash(:id => 1, :name => 'test')
|
35
|
+
user.should be_instance_of(Desmos::User)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|