slothbear-redcap 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +621 -0
- data/README.rdoc +70 -0
- data/Rakefile +60 -0
- data/VERSION.yml +4 -0
- data/bin/redcap +83 -0
- data/doc/placeholder +0 -0
- data/lib/redcap/uploader.rb +208 -0
- data/redcap.gemspec +61 -0
- data/test/redcap_image.jpc +0 -0
- data/test/redcap_test.rb +7 -0
- data/test/synapse.jpg +0 -0
- data/test/test_helper.rb +9 -0
- data/test/test_llsd.rb +62 -0
- data/test/test_nextpowerof2.rb +39 -0
- data/test/test_parsecap.rb +31 -0
- data/test/test_ticket.rb +26 -0
- metadata +76 -0
data/README.rdoc
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
= redcap
|
2
|
+
|
3
|
+
redcap is a library for uploading images
|
4
|
+
to Second Life. It costs L$10 per image uploaded.
|
5
|
+
You can save some money during testing by using the
|
6
|
+
Preview Grid (if available).
|
7
|
+
|
8
|
+
== installation
|
9
|
+
|
10
|
+
To install redcap as a gem:
|
11
|
+
gem install slothbear-redcap --source http://gems.github.com
|
12
|
+
|
13
|
+
If you'd rather grab a couple files and give it a try, download
|
14
|
+
the following files from http://github.com/slothbear/redcap/tree/master
|
15
|
+
bin/redcap
|
16
|
+
lib/redcap/uploader.rb
|
17
|
+
|
18
|
+
Amend this line in redcap to fit your installation and give it a try.
|
19
|
+
require "redcap/uploader"
|
20
|
+
|
21
|
+
== usage
|
22
|
+
|
23
|
+
Upload a JPG:
|
24
|
+
redcap your_image.jpg first_name last_name SL_password
|
25
|
+
|
26
|
+
Upload a PNG to the preview grid:
|
27
|
+
redcap -g preview picture.png first_name last_name SL_password
|
28
|
+
|
29
|
+
Note you can upload any image format that your installation of ImageMagick
|
30
|
+
can read.
|
31
|
+
|
32
|
+
== release notes
|
33
|
+
All images are uploaded into the top level of your Inventory. [to be
|
34
|
+
fixed for release 1.0.0]
|
35
|
+
|
36
|
+
Second Life requires the uploaded image dimensions to be powers of 2. If you'd
|
37
|
+
like to re-size your image to its original aspect ratio, make use of
|
38
|
+
Uploader::aspect_ratio. (see forthcoming LSL example)
|
39
|
+
|
40
|
+
installing ImageMagick and RMagick: http://rmagick.rubyforge.org/install-osx.html
|
41
|
+
|
42
|
+
When you install ImageMagick, make sure you include the jpeg2 variant:
|
43
|
+
sudo port install tiff -macosx imagemagick +q8 +gs +wmf +jpeg2
|
44
|
+
|
45
|
+
To make sure your ImageMagick can produce SL-compatible JPEG-2000 files,
|
46
|
+
issue the following command:
|
47
|
+
identify -list format | grep JPC
|
48
|
+
The JPC line should include "rw-" indicating it can write files, like:
|
49
|
+
JPC* JPC rw- JPEG-2000 Code Stream Syntax
|
50
|
+
|
51
|
+
The /tests were developed for a previous release and do not run at this time.
|
52
|
+
|
53
|
+
|
54
|
+
== copyright
|
55
|
+
|
56
|
+
redcap -- library for uploading files to Second Life
|
57
|
+
Copyright 2009 Paul Morganthall
|
58
|
+
|
59
|
+
redcap is free software: you can redistribute it and/or modify
|
60
|
+
it under the terms of the GNU General Public License as published by
|
61
|
+
the Free Software Foundation, either version 3 of the License, or
|
62
|
+
any later version.
|
63
|
+
|
64
|
+
This program is distributed in the hope that it will be useful,
|
65
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
66
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
67
|
+
GNU General Public License for more details.
|
68
|
+
|
69
|
+
You should have received a copy of the GNU General Public License
|
70
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "redcap"
|
8
|
+
gem.summary = %Q{Utilities for uploading files to Second Life}
|
9
|
+
gem.email = "slothbear@constella.org"
|
10
|
+
gem.homepage = "http://adammarker.org/redcap"
|
11
|
+
gem.authors = ["Paul Morganthall"]
|
12
|
+
# gem.platform = Gem::Platform::Ruby
|
13
|
+
gem.has_rdoc = false
|
14
|
+
gem.required_ruby_version = '>=1.8'
|
15
|
+
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rake/testtask'
|
24
|
+
Rake::TestTask.new(:test) do |test|
|
25
|
+
test.libs << 'lib' << 'test'
|
26
|
+
test.pattern = 'test/**/*_test.rb'
|
27
|
+
test.verbose = true
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'rcov/rcovtask'
|
32
|
+
Rcov::RcovTask.new do |test|
|
33
|
+
test.libs << 'test'
|
34
|
+
test.pattern = 'test/**/*_test.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
rescue LoadError
|
38
|
+
task :rcov do
|
39
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
if File.exist?('VERSION.yml')
|
49
|
+
config = YAML.load(File.read('VERSION.yml'))
|
50
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
51
|
+
else
|
52
|
+
version = ""
|
53
|
+
end
|
54
|
+
|
55
|
+
rdoc.rdoc_dir = 'rdoc'
|
56
|
+
rdoc.title = "redcap #{version}"
|
57
|
+
rdoc.rdoc_files.include('README*')
|
58
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
59
|
+
end
|
60
|
+
|
data/VERSION.yml
ADDED
data/bin/redcap
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# redcap -- library for uploading files to Second Life
|
4
|
+
# Copyright 2009 Paul Morganthall
|
5
|
+
#
|
6
|
+
# redcap is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'redcap/uploader'
|
20
|
+
require 'getoptlong'
|
21
|
+
|
22
|
+
synopsis = %{# == Synopsis
|
23
|
+
#
|
24
|
+
# redcap: upload image to Second Life
|
25
|
+
# using the specified AV credentials.
|
26
|
+
#
|
27
|
+
# == Usage
|
28
|
+
#
|
29
|
+
# redcap [OPTIONS] [first_name last_name password] image_file
|
30
|
+
#
|
31
|
+
# --help, -h:
|
32
|
+
# show help
|
33
|
+
#
|
34
|
+
# --grid grid, -g grid:
|
35
|
+
# upload image to the specified grid, default = main
|
36
|
+
# other legal value: preview
|
37
|
+
#
|
38
|
+
# first_name, last_name, password:
|
39
|
+
# Second Life credentials used to login.
|
40
|
+
#
|
41
|
+
# image_file: path to the image file to be uploaded
|
42
|
+
}
|
43
|
+
|
44
|
+
grid = 'main'
|
45
|
+
|
46
|
+
opts = GetoptLong.new(
|
47
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
48
|
+
[ '--grid', '-g', GetoptLong::REQUIRED_ARGUMENT ]
|
49
|
+
)
|
50
|
+
|
51
|
+
opts.each do |opt, arg|
|
52
|
+
case opt
|
53
|
+
when '--help'
|
54
|
+
puts synopsis
|
55
|
+
exit
|
56
|
+
when '--grid'
|
57
|
+
grid = arg
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
case ARGV.size
|
62
|
+
when 4
|
63
|
+
first_name, last_name, password, image_file = ARGV
|
64
|
+
else
|
65
|
+
puts "missing arguments (try --help)"
|
66
|
+
exit
|
67
|
+
end
|
68
|
+
|
69
|
+
unless image_file =~ /^http/ or File.exists?(image_file)
|
70
|
+
puts "file not found: #{image_file}"
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
|
74
|
+
uploader = Redcap::Uploader.new(first_name, last_name, password, image_file, grid)
|
75
|
+
|
76
|
+
|
77
|
+
# on successful upload, prepend the original image aspect ratio.
|
78
|
+
if uploader.success
|
79
|
+
puts "file successfully uploaded, UUID=#{uploader.new_asset_id}"
|
80
|
+
puts "original aspect ratio: #{sprintf("%.4f", uploader.aspect_ratio)}"
|
81
|
+
else
|
82
|
+
puts "error encountered while uploading:\n#{uploader.msg}"
|
83
|
+
end
|
data/doc/placeholder
ADDED
File without changes
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# redcap -- library for uploading files to Second Life
|
2
|
+
# Copyright 2009 Paul Morganthall
|
3
|
+
#
|
4
|
+
# redcap is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'builder'
|
19
|
+
require 'digest/md5'
|
20
|
+
require 'logger'
|
21
|
+
require 'net/https'
|
22
|
+
require 'rexml/document'
|
23
|
+
require 'RMagick' # Requires ImageMagick with JPEG2000 (.JPC) installed
|
24
|
+
require 'tempfile'
|
25
|
+
require 'xmlrpc/client'
|
26
|
+
require 'yaml'
|
27
|
+
|
28
|
+
# Uploader: support for uploading files to Second Life
|
29
|
+
# The standard upload fee (L$10) is charged upon successful upload.
|
30
|
+
# This program was inspired and informed by the work of Katherine Berry, author
|
31
|
+
# of phpsimcaps, AjaxLife, and other cool stuff:
|
32
|
+
# http://blog.katharineberry.co.uk/
|
33
|
+
|
34
|
+
module Redcap
|
35
|
+
GRIDS = {"main" => "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
|
36
|
+
"preview" => "https://login.aditi.lindenlab.com/cgi-bin/login.cgi"}
|
37
|
+
|
38
|
+
class Uploader
|
39
|
+
attr_reader :success, :new_asset_id, :msg, :aspect_ratio
|
40
|
+
|
41
|
+
def initialize(first_name, last_name, password, filename, grid="main")
|
42
|
+
@debug = false
|
43
|
+
@log = Logger.new('redcap.log')
|
44
|
+
@log.level = Logger::DEBUG # DEBUG, WARN,
|
45
|
+
@trace = Array.new # global trace stored here for now
|
46
|
+
# ? seperate from log, so it can be glommed to log in one piece
|
47
|
+
@trace << [first_name, last_name, filename, grid, Time.now].join("/")
|
48
|
+
|
49
|
+
#1: invoke [begin block has rescue at end]
|
50
|
+
begin
|
51
|
+
#2: login
|
52
|
+
login_data = login(first_name, last_name, password, GRIDS[grid])
|
53
|
+
login_data["seed_capability"] or raise "seed_capability not found"
|
54
|
+
login_data["inventory-root"] or raise "inventory-root not found"
|
55
|
+
|
56
|
+
#3: request the upload capability
|
57
|
+
cap_request = send_request(login_data["seed_capability"],
|
58
|
+
%{<llsd><array><string>NewFileAgentInventory</string></array></llsd>})
|
59
|
+
cap_request["NewFileAgentInventory"] or raise "no image upload capability found"
|
60
|
+
|
61
|
+
#4: request an upload ticket
|
62
|
+
ticket_request = send_request(cap_request["NewFileAgentInventory"],
|
63
|
+
encode_ticket(login_data["inventory-root"], filename))
|
64
|
+
ticket_request["state"] == "upload" or raise "state not upload"
|
65
|
+
ticket_request["uploader"] or raise "no uploader found"
|
66
|
+
|
67
|
+
#5: upload image
|
68
|
+
upload_status = send_request(ticket_request["uploader"], make_slimage(filename))
|
69
|
+
upload_status["state"] = "complete" or raise "upload failed: #{upload_status["state"]}"
|
70
|
+
|
71
|
+
#7: retrieve the UUID of the newly uploaded image
|
72
|
+
@success = true
|
73
|
+
@new_asset_id = upload_status["new_asset"]
|
74
|
+
# upon success, log the original request and the final result.
|
75
|
+
@log.info(@trace[0].to_yaml + @trace[-1].to_yaml)
|
76
|
+
|
77
|
+
rescue
|
78
|
+
@success = false
|
79
|
+
@msg = "#{$!}"
|
80
|
+
@log.fatal @trace.to_yaml # on failure, log everything
|
81
|
+
if @debug
|
82
|
+
@msg += "\ntrace: #{@trace[0]}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end # initialize
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def login(first_name, last_name, password, login_url, method="login_to_simulator")
|
91
|
+
|
92
|
+
login_params = {
|
93
|
+
:first => first_name,
|
94
|
+
:last => last_name,
|
95
|
+
:passwd => "$1$" + Digest::MD5.hexdigest(password),
|
96
|
+
:options => %w{inventory-root},
|
97
|
+
:start => "last",
|
98
|
+
:channel => "redcap.rb", # w/channel, no need for build id.
|
99
|
+
:mac => "00:D0:4D:28:A1:F1", # required.
|
100
|
+
:read_critical => "true", # skip...
|
101
|
+
:agree_to_tos => "true", # potential...
|
102
|
+
:skipoptional => "true", # dialogs...
|
103
|
+
}
|
104
|
+
|
105
|
+
login_url or raise "unrecognized grid name"
|
106
|
+
@trace << [login_url, method] << login_params
|
107
|
+
@log.debug "#{login_url} / #{method} / #{login_params}"
|
108
|
+
server = XMLRPC::Client.new2(login_url)
|
109
|
+
server.http_header_extra = {"Content-Type" => "text/xml"}
|
110
|
+
server.timeout = 120
|
111
|
+
result = server.call(method, login_params)
|
112
|
+
@trace << result
|
113
|
+
@log.debug "raw login response: #{result.to_yaml}"
|
114
|
+
# unearth some parameters
|
115
|
+
if result.has_key?("inventory-root") then
|
116
|
+
result["inventory-root"] = result["inventory-root"][0]["folder_id"]
|
117
|
+
end
|
118
|
+
|
119
|
+
# If the login server says "indeterminate", follow the path given.
|
120
|
+
if "indeterminate" == result["login"] then
|
121
|
+
@log.info "indeterminate login:\n#{result.to_yaml}"
|
122
|
+
next_url = result["next_url"]
|
123
|
+
next_method = result["next_method"]
|
124
|
+
message = result["message"]
|
125
|
+
@log.warn "Login redirected:\n#{next_url}, method: #{next_method}, message: #{message}"
|
126
|
+
result = login(first_name, last_name, password, next_url, next_method)
|
127
|
+
elsif "false" == result["login"] or "true" != result["login"] then
|
128
|
+
raise "error during login: #{result["message"]}"
|
129
|
+
end
|
130
|
+
|
131
|
+
result
|
132
|
+
end # login
|
133
|
+
|
134
|
+
# Examine URL and return the base url (server), port number, and parameter path
|
135
|
+
def parse_capability(cap)
|
136
|
+
match = %r{https://(.+):(\d+)(.+)}.match(cap)
|
137
|
+
match or raise "capability format not understood: #{cap}"
|
138
|
+
match.captures
|
139
|
+
end
|
140
|
+
|
141
|
+
# Convert LLSD ( Linden Lab Structured Data) into a simple hash.
|
142
|
+
def decode_llsd(body)
|
143
|
+
doc = REXML::Document.new(body)
|
144
|
+
pairs = Hash[*doc.elements.to_a("/llsd/map/*")]
|
145
|
+
result = Hash.new
|
146
|
+
pairs.each { |key,value| result[key.text.strip] = value.text.strip }
|
147
|
+
result
|
148
|
+
end
|
149
|
+
|
150
|
+
def send_request(capability, data)
|
151
|
+
@log.debug "send_request / #{capability} #{data if data[0,1] == '<'}"
|
152
|
+
|
153
|
+
server, port, path = parse_capability(capability)
|
154
|
+
site = Net::HTTP.new(server, port)
|
155
|
+
# site.set_debug_output($stderr)
|
156
|
+
site.use_ssl = true
|
157
|
+
site.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
158
|
+
|
159
|
+
response = site.request_post(path, data, {"Content-Type" => "text/xml"})
|
160
|
+
result = decode_llsd(response.body)
|
161
|
+
@log.debug "response\n#{result.to_yaml}"
|
162
|
+
@trace << result
|
163
|
+
result
|
164
|
+
end
|
165
|
+
|
166
|
+
def encode_ticket(folder, filename)
|
167
|
+
request = {
|
168
|
+
"asset_type" => "texture",
|
169
|
+
"description" => "uploaded by http://adammarker.org/redcap/",
|
170
|
+
"folder_id" => folder,
|
171
|
+
"inventory_type" => "texture",
|
172
|
+
"name" => File.basename(filename)
|
173
|
+
}
|
174
|
+
|
175
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
176
|
+
xml.llsd {
|
177
|
+
xml.map {
|
178
|
+
request.each do |key, value|
|
179
|
+
xml.key(key)
|
180
|
+
xml.string(value)
|
181
|
+
end
|
182
|
+
}
|
183
|
+
}
|
184
|
+
xml.target!
|
185
|
+
end
|
186
|
+
|
187
|
+
# adapted from function by Simon Kröger posted in comp.lang.ruby
|
188
|
+
# maximum dimension of an SL image is 1024.
|
189
|
+
def nextpow2(n)
|
190
|
+
throw 'eeek' if n < 0 # Simon's. perhaps there is another way?
|
191
|
+
return 1 if n < 2
|
192
|
+
[1 << (n-1).to_s(2).size, 1024].min
|
193
|
+
end
|
194
|
+
|
195
|
+
# Convert image to SL standards:
|
196
|
+
# 1. JPEG-2000 Code Stream Syntax (JPC)
|
197
|
+
# 2. width and height must be a power of 2
|
198
|
+
def make_slimage(filename)
|
199
|
+
image = Magick::Image.read(filename).first
|
200
|
+
@aspect_ratio = image.columns.to_f / image.rows.to_f
|
201
|
+
new_image = image.resize(nextpow2(image.columns), nextpow2(image.rows))
|
202
|
+
slimage = Tempfile.new('redcap').path
|
203
|
+
new_image.write("jpc:#{slimage}") or raise "Unable to write JPC image."
|
204
|
+
open(slimage, 'rb') { |f| f.read }
|
205
|
+
end
|
206
|
+
|
207
|
+
end # class
|
208
|
+
end # module
|
data/redcap.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{redcap}
|
5
|
+
s.version = "0.7.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Paul Morganthall"]
|
9
|
+
s.date = %q{2009-07-24}
|
10
|
+
s.default_executable = %q{redcap}
|
11
|
+
s.email = %q{slothbear@constella.org}
|
12
|
+
s.executables = ["redcap"]
|
13
|
+
s.extra_rdoc_files = [
|
14
|
+
"LICENSE",
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
".document",
|
19
|
+
".gitignore",
|
20
|
+
"LICENSE",
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION.yml",
|
24
|
+
"bin/redcap",
|
25
|
+
"doc/placeholder",
|
26
|
+
"lib/redcap/uploader.rb",
|
27
|
+
"redcap.gemspec",
|
28
|
+
"test/redcap_image.jpc",
|
29
|
+
"test/redcap_test.rb",
|
30
|
+
"test/synapse.jpg",
|
31
|
+
"test/test_helper.rb",
|
32
|
+
"test/test_llsd.rb",
|
33
|
+
"test/test_nextpowerof2.rb",
|
34
|
+
"test/test_parsecap.rb",
|
35
|
+
"test/test_ticket.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://adammarker.org/redcap}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.required_ruby_version = Gem::Requirement.new(">= 1.8")
|
41
|
+
s.rubygems_version = %q{1.3.1}
|
42
|
+
s.summary = %q{Utilities for uploading files to Second Life}
|
43
|
+
s.test_files = [
|
44
|
+
"test/redcap_test.rb",
|
45
|
+
"test/test_helper.rb",
|
46
|
+
"test/test_llsd.rb",
|
47
|
+
"test/test_nextpowerof2.rb",
|
48
|
+
"test/test_parsecap.rb",
|
49
|
+
"test/test_ticket.rb"
|
50
|
+
]
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
|
+
s.specification_version = 2
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
57
|
+
else
|
58
|
+
end
|
59
|
+
else
|
60
|
+
end
|
61
|
+
end
|
Binary file
|
data/test/redcap_test.rb
ADDED
data/test/synapse.jpg
ADDED
Binary file
|
data/test/test_helper.rb
ADDED
data/test/test_llsd.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# LLSD (The Linden Lab Structured Data system) is the XML protocol
|
2
|
+
# used for redcap's communication with Second Life. All current
|
3
|
+
# structures are simple maps (hashes) with pairs of key/values.
|
4
|
+
|
5
|
+
# For more information regarding The LLSD flexible data system
|
6
|
+
# see http://wiki.secondlife.com/wiki/LLSD
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'redcap'
|
10
|
+
|
11
|
+
$llsd = %{
|
12
|
+
<llsd>
|
13
|
+
<map>
|
14
|
+
<key>name</key>
|
15
|
+
<string>/Users/orion/Desktop/textures/rose bases are red.jpeg</string>
|
16
|
+
<key>folder_id</key>
|
17
|
+
<string>98359cef-1609-4317-be54-f044cc784b0a</string>
|
18
|
+
<key>description</key>
|
19
|
+
<string>uploaded by redcap</string>
|
20
|
+
<key>inventory_type</key>
|
21
|
+
<string>texture</string>
|
22
|
+
<key>asset_type</key>
|
23
|
+
<string>texture</string>
|
24
|
+
</map>
|
25
|
+
</llsd>
|
26
|
+
}
|
27
|
+
|
28
|
+
$llsd_odd = %{
|
29
|
+
<llsd>
|
30
|
+
<map>
|
31
|
+
<key>name</key>
|
32
|
+
</map>
|
33
|
+
</llsd>
|
34
|
+
}
|
35
|
+
|
36
|
+
class TestLLSD < Test::Unit::TestCase
|
37
|
+
|
38
|
+
def testTypicalDecode
|
39
|
+
result = decode_llsd($llsd)
|
40
|
+
assert_equal("/Users/orion/Desktop/textures/rose bases are red.jpeg", result["name"])
|
41
|
+
assert_equal("98359cef-1609-4317-be54-f044cc784b0a", result["folder_id"])
|
42
|
+
assert_equal("uploaded by redcap", result["description"])
|
43
|
+
assert_equal("texture", result["inventory_type"])
|
44
|
+
assert_equal("texture", result["asset_type"])
|
45
|
+
end
|
46
|
+
|
47
|
+
def testDecodeBadXML
|
48
|
+
assert_raise(REXML::ParseException) { decode_llsd("<badxml>") }
|
49
|
+
end
|
50
|
+
|
51
|
+
# The LLSD should contain an even number of elements (key, value, key, value)
|
52
|
+
# Well formed LLSD shouldn't have an odd number, but if it does...
|
53
|
+
def testDecodeOddPair
|
54
|
+
assert_raise(ArgumentError) { decode_llsd($llsd_odd) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def testDecodeRandomText
|
58
|
+
result = decode_llsd("gibberish")
|
59
|
+
assert_equal({}, result)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# nextpow2 is adapted from function by Simon Kröger posted in comp.lang.ruby
|
2
|
+
# original post: http://www.ruby-forum.com/topic/76261
|
3
|
+
|
4
|
+
# The adaptation made for Second Life constrain the maximum value returned
|
5
|
+
# to 1024, the maximum dimention of an SL texture.
|
6
|
+
|
7
|
+
require 'test/unit'
|
8
|
+
require '../redcap'
|
9
|
+
|
10
|
+
class TestNextPowerOf2 < Test::Unit::TestCase
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@check_array = [[1, 1], [2,2]]
|
14
|
+
(2..9).each do | i |
|
15
|
+
@check_array << [ 2**i - 1, 2**i] << [2**i, 2**i] << [2**i + 1, 2**(i+1)]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# test copied from Rick Denatale's post to the thread mentioned above.
|
20
|
+
def test_normal
|
21
|
+
@check_array.each do | input, output |
|
22
|
+
assert_equal(output, nextpow2(input))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_over
|
27
|
+
assert_equal(1024, nextpow2(1023))
|
28
|
+
assert_equal(1024, nextpow2(1025))
|
29
|
+
assert_equal(1024, nextpow2(10240))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_under
|
33
|
+
assert_equal(2, nextpow2(2))
|
34
|
+
assert_equal(1, nextpow2(1))
|
35
|
+
assert_equal(1, nextpow2(0))
|
36
|
+
assert_raise(NameError) { nextpow2(-1) }
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Second Life hands us complete URLs, but Net::HTTP requires
|
2
|
+
# separate values for protocol, server, port, and path.
|
3
|
+
|
4
|
+
# HTTPS protocol is always used, so that part of the URL is ignored.
|
5
|
+
|
6
|
+
|
7
|
+
require 'test/unit'
|
8
|
+
# can I use require relative??
|
9
|
+
require '../lib/redcap/uploader'
|
10
|
+
|
11
|
+
class TestParseCap < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_typical
|
14
|
+
result = parse_capability("https://sim4.aditi.lindenlab.com:12043/cap/f692b7fc-3074-a277-c08c-74363e5248cb")
|
15
|
+
assert_equal(3,result.size)
|
16
|
+
server, port, path = result
|
17
|
+
assert_equal("sim4.aditi.lindenlab.com", server)
|
18
|
+
assert_equal("12043", port)
|
19
|
+
assert_equal("/cap/f692b7fc-3074-a277-c08c-74363e5248cb", path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_abnormal
|
23
|
+
assert_raise(RuntimeError) { parse_capability("") }
|
24
|
+
assert_raise(RuntimeError) { parse_capability("ftp://www.secondlife.com:12045/cap/xxx") }
|
25
|
+
assert_raise(RuntimeError) { parse_capability("https://test.server.com:/app/path") }
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# squeak
|
data/test/test_ticket.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# A more general routine to encode to LLSD is probably in the future,
|
2
|
+
# but the structure is so simple a custom routine is sufficient for now.
|
3
|
+
|
4
|
+
# encode_ticket wraps a hash around the asset upload parameters, then uses
|
5
|
+
# Builder to create the required LLSD structure.
|
6
|
+
|
7
|
+
# The required structure is a series of <key> <string> elements, wrapped
|
8
|
+
# in the top level <llsd><map>. For example:
|
9
|
+
# <llsd><map>
|
10
|
+
# <key>asset_type</key>
|
11
|
+
# <string>texture</string>
|
12
|
+
# </map></llsd>
|
13
|
+
|
14
|
+
# TODO: this test is lame and fragile; perhaps a mock object would help?
|
15
|
+
|
16
|
+
require "test/unit"
|
17
|
+
require "../redcap.rb"
|
18
|
+
|
19
|
+
$answer = %{<llsd>\n <map>\n <key>name</key>\n <string>file.jpg</string>\n <key>folder_id</key>\n <string>000-ffff-f0lde4</string>\n <key>description</key>\n <string>uploaded by redcap</string>\n <key>inventory_type</key>\n <string>texture</string>\n <key>asset_type</key>\n <string>texture</string>\n </map>\n</llsd>\n}
|
20
|
+
|
21
|
+
class TestTicket < Test::Unit::TestCase
|
22
|
+
def test_typical
|
23
|
+
result = encode_ticket("000-ffff-f0lde4", "file.jpg")
|
24
|
+
assert_equal($answer, result)
|
25
|
+
end
|
26
|
+
end
|