swxruby 0.7

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.
Files changed (40) hide show
  1. data/CHANGELOG +21 -0
  2. data/LICENSE +20 -0
  3. data/README +143 -0
  4. data/Rakefile +111 -0
  5. data/bin/swxruby +58 -0
  6. data/examples/standalone/standalone.fla +0 -0
  7. data/examples/standalone/standalone.rb +37 -0
  8. data/examples/standalone/standalone.swf +0 -0
  9. data/init.rb +22 -0
  10. data/install.rb +69 -0
  11. data/lib/swxruby.rb +6 -0
  12. data/lib/swxruby/bytecode_converter.rb +139 -0
  13. data/lib/swxruby/core_extensions.rb +41 -0
  14. data/lib/swxruby/helper_module.rb +72 -0
  15. data/lib/swxruby/rails_integration/render_decorator.rb +20 -0
  16. data/lib/swxruby/rails_integration/swx.yml +18 -0
  17. data/lib/swxruby/rails_integration/swx_controller.rb +6 -0
  18. data/lib/swxruby/services/arithmetic.rb +9 -0
  19. data/lib/swxruby/services/discovery_service.rb +3 -0
  20. data/lib/swxruby/services/hello_world.rb +7 -0
  21. data/lib/swxruby/services/simple.rb +5 -0
  22. data/lib/swxruby/services/test_data_types.rb +94 -0
  23. data/lib/swxruby/swx_assembler.rb +138 -0
  24. data/lib/swxruby/swx_gateway.rb +104 -0
  25. data/lib/swxruby/version.rb +9 -0
  26. data/spec/spec.opts +1 -0
  27. data/spec/spec_helper.rb +6 -0
  28. data/spec/swxruby/bytecode_converter_spec.rb +162 -0
  29. data/spec/swxruby/core_extensions_spec.rb +16 -0
  30. data/spec/swxruby/fixtures/number_one_no_debug_compression_4.swx +0 -0
  31. data/spec/swxruby/fixtures/number_one_no_debug_no_compression.swx +0 -0
  32. data/spec/swxruby/fixtures/number_one_no_debug_no_compression_arbitrary_allow_domain.swx +0 -0
  33. data/spec/swxruby/fixtures/number_one_with_debug_compression_4.swx +0 -0
  34. data/spec/swxruby/fixtures/number_one_with_debug_no_compression.swx +0 -0
  35. data/spec/swxruby/rails_integration/init_spec.rb +0 -0
  36. data/spec/swxruby/rails_integration/render_decorator_spec.rb +73 -0
  37. data/spec/swxruby/rails_integration/swx_controller_spec.rb +5 -0
  38. data/spec/swxruby/swx_assembler_spec.rb +60 -0
  39. data/spec/swxruby/swx_gateway_spec.rb +160 -0
  40. metadata +108 -0
@@ -0,0 +1,6 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ $:.unshift File.join(File.dirname(__FILE__), 'swxruby')
3
+ class SwxRuby
4
+ VERSION = '0.7'
5
+ end
6
+ require 'swx_gateway'
@@ -0,0 +1,139 @@
1
+ require 'core_extensions'
2
+ require 'date'
3
+ require 'helper_module'
4
+
5
+ class BytecodeConverter
6
+ include HelperMethods
7
+ class << self
8
+ NULL_TERMINATOR = '00'
9
+
10
+ def convert(data)
11
+ conversion_method = "#{data.class.to_s.downcase}_to_bytecode"
12
+
13
+ if respond_to?(conversion_method)
14
+ send(conversion_method, data)
15
+ else
16
+ # Convert the object to a hash of its instance variables
17
+ object_hash = data.instance_values
18
+
19
+ if object_hash.empty?
20
+ raise StandardError, "#{data.class} is an unhandled data type."
21
+ else
22
+ hash_to_bytecode(object_hash)
23
+ end
24
+ end
25
+ end
26
+
27
+ protected
28
+ # = Data Type Conversion Methods
29
+ def complex_data_structure_to_bytecode(data) #:nodoc#
30
+ bytecode = []
31
+
32
+ # Keeps track of bytecode when recursing into nested data structures
33
+ stack = []
34
+
35
+ # Add the bytecode to initialize the data structure
36
+ bytecode.push(data.is_a?(Array) ? ActionCodes::INIT_ARRAY : ActionCodes::INIT_OBJECT)
37
+
38
+ # Add the length of the data structure to the bytecode
39
+ bytecode.push integer_to_bytecode(data.length)
40
+
41
+ # Convert each element in the data structure to bytecode
42
+ data.each do |element|
43
+
44
+ # assume we're not iterating into a recursive data structure
45
+ complex_data_structure = false
46
+
47
+ # if we're converting a hash, then convert the hash's value
48
+ if data.is_a?(Hash)
49
+ value_bytecode = convert(element[1])
50
+ # if we just converted an array or a hash, set complex_data_structure to true
51
+ complex_data_structure = true if value_bytecode.concludes_with?(ActionCodes::INIT_OBJECT, ActionCodes::INIT_ARRAY)
52
+ # set the element local variable to the key of the hash
53
+ element = element[0]
54
+ end
55
+
56
+ # element will always contain something (whether iterating over a hash or an array)
57
+ element_bytecode = convert(element)
58
+ # if we just converted an array or a hash, set complex_data_structure to true
59
+ complex_data_structure = true if element_bytecode.concludes_with?(ActionCodes::INIT_OBJECT, ActionCodes::INIT_ARRAY)
60
+
61
+ # Create a push of the current bytecode, if
62
+ # we recursed into a complex data structure
63
+ # or we're approaching the 65535 byte limit that can be stored in a single push.
64
+ if (complex_data_structure || calculate_bytecode_length(bytecode) > 65518)
65
+
66
+ # If we haven't written any bytecode into the local
67
+ # buffer yet (if it's empty), or all the data is already pushed, skip writing the push statement
68
+ bytecode.push generate_push_statement(bytecode) unless bytecode.empty? || bytecode.last.begins_with?('96')
69
+
70
+ # Store current instruction on the stack (SWF bytecode is stored in reverse, so we reverse it here)
71
+ stack.push bytecode.reverse.join
72
+
73
+ # Reset the bytecode
74
+ bytecode = []
75
+ end
76
+
77
+ # value_bytecode will be nil unless we converted a Hash
78
+ bytecode.push value_bytecode unless value_bytecode.nil?
79
+
80
+ bytecode.push element_bytecode
81
+ end
82
+
83
+ # If we haven't written any bytecode into the local
84
+ # buffer yet (if it's empty), or all the data is already pushed, skip writing the push statement
85
+ bytecode.push generate_push_statement(bytecode) unless bytecode.empty? || bytecode.last.begins_with?('96')
86
+
87
+ # Add the bytecode to the local stack variable (SWF bytecode is stored in reverse, so we reverse it here)
88
+ stack.push bytecode.reverse.join
89
+
90
+ # Join the stack array into a string and return it (SWF bytecode is stored in reverse, so we reverse it here)
91
+ stack.reverse.join
92
+ end
93
+ alias array_to_bytecode complex_data_structure_to_bytecode
94
+ alias hash_to_bytecode complex_data_structure_to_bytecode
95
+
96
+ def date_to_bytecode(date) #:nodoc#
97
+ # Format: 2006-09-14
98
+ string_to_bytecode(date.strftime('%Y-%m-%d'))
99
+ end
100
+
101
+ def datetime_to_bytecode(datetime) #:nodoc#
102
+ # Format: 2006-09-14 02:21:10
103
+ string_to_bytecode(datetime.strftime('%Y-%m-%d %I:%M:%S'))
104
+ end
105
+
106
+ def falseclass_to_bytecode(*args) #:nodoc#
107
+ DataTypeCodes::BOOLEAN + '00'
108
+ end
109
+
110
+ def float_to_bytecode(float) #:nodoc#
111
+ hex = []
112
+ [float].pack('E').each_byte { |byte| hex << '%02X' % byte }
113
+ # Aral did this in SWX PHP, so I'm doing it here
114
+ DataTypeCodes::FLOAT + (hex[4..-1] + hex[0..3]).join
115
+ end
116
+
117
+ def integer_to_bytecode(integer) #:nodoc#
118
+ DataTypeCodes::INTEGER + integer_to_hexadecimal(integer, 4)
119
+ end
120
+ alias bignum_to_bytecode integer_to_bytecode
121
+ alias fixnum_to_bytecode integer_to_bytecode
122
+
123
+ def nilclass_to_bytecode(*args) #:nodoc#
124
+ '02'
125
+ end
126
+
127
+ def string_to_bytecode(string) #:nodoc#
128
+ DataTypeCodes::STRING + string.unpack('H*').join.upcase + NULL_TERMINATOR
129
+ end
130
+
131
+ def symbol_to_bytecode(symbol)
132
+ string_to_bytecode(symbol.to_s)
133
+ end
134
+
135
+ def trueclass_to_bytecode(*args) #:nodoc#
136
+ DataTypeCodes::BOOLEAN + '01'
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,41 @@
1
+ # = Core Extensions
2
+ class Array
3
+ def begins_with?(string)
4
+ self.join.begins_with?(string)
5
+ end
6
+ end
7
+
8
+ class Object
9
+ # Taken from ActiveRecord
10
+ def instance_values
11
+ instance_variables.inject({}) do |values, name|
12
+ values[name[1..-1]] = instance_variable_get(name)
13
+ values
14
+ end
15
+ end
16
+ end
17
+
18
+ class String
19
+ def begins_with?(string)
20
+ self[0..string.length-1] == string
21
+ end
22
+
23
+ def concludes_with?(*strings)
24
+ strings.any? { |string| self[-(string.length)..-1] == string }
25
+ end
26
+
27
+ def hex_to_ascii
28
+ hex = self.gsub(' ', '')
29
+ [hex].pack('H*')
30
+ end
31
+
32
+ # Taken from Rails' Inflector module
33
+ def underscore
34
+ self.to_s.gsub(/::/, '/').
35
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
36
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
37
+ tr("-", "_").
38
+ downcase
39
+ end
40
+ end
41
+
@@ -0,0 +1,72 @@
1
+ require 'core_extensions'
2
+
3
+ module DataTypeCodes
4
+ BOOLEAN = '05'
5
+ FLOAT = '06'
6
+ INTEGER = '07'
7
+ NULL = '02'
8
+ STRING = '00'
9
+ end
10
+
11
+ module ActionCodes
12
+ DO_ACTION = '3F03'
13
+ END_SWF = '0000'
14
+ INIT_ARRAY = '42'
15
+ INIT_OBJECT = '43'
16
+ PUSH = '96LLLL'
17
+ SET_VARIABLE = '1D' # 00
18
+ SHOW_FRAME = '4000'
19
+ end
20
+
21
+ module HelperMethods
22
+ module ClassMethods
23
+ def calculate_bytecode_length(bytecode)
24
+ # Calculate bytecode length *without* counting the init array or init object action
25
+ bytecode.join.sub(/^(#{ActionCodes::INIT_ARRAY}|#{ActionCodes::INIT_OBJECT})/, '').length/2
26
+ end
27
+
28
+ def generate_push_statement(bytecode)
29
+ unpushed_data = []
30
+ # Iterate over the bytecode array in reverse and add all of the unpushed
31
+ # data to 'unpushed_data'
32
+ bytecode.reverse_each do |bytecode_chunk|
33
+ if bytecode_chunk.begins_with?('96') || bytecode_chunk.begins_with?('1D') then break else unpushed_data << bytecode_chunk end
34
+ end
35
+
36
+ # Since we iterated over the bytecode in reverse, unpushed_data is reversed, so
37
+ # reverse it again before passing it into #calculate_bytecode_length
38
+ bytecode_length = calculate_bytecode_length(unpushed_data.reverse)
39
+
40
+ # TODO: Replace with constant
41
+ '96' + integer_to_hexadecimal(bytecode_length, 2)
42
+ end
43
+
44
+ def integer_to_hexadecimal(integer, number_of_bytes=1)
45
+ make_little_endian("%0#{number_of_bytes*2}X" % integer)
46
+ end
47
+
48
+ def make_little_endian(hex_string)
49
+ # split into an array of string pairs
50
+ # reverse the array and join back into a string
51
+ pad_string_to_byte_boundary(hex_string).scan(/../).reverse.join
52
+ end
53
+
54
+ def pad_string_to_byte_boundary(hex_string)
55
+ hex_string += '0' if hex_string.length % 2 == 1
56
+ hex_string
57
+ end
58
+
59
+ # Returns a string with the length of the passed hex string in bytes
60
+ # padded to display in 'number_of_bytes' bytes.
61
+ def string_length_in_bytes_hex(string, number_of_bytes)
62
+ # Divide length in chars by 2 to get length in bytes
63
+ bytecode_length_in_hex = integer_to_hexadecimal(string.length/2, number_of_bytes)
64
+ end
65
+ end
66
+
67
+ extend ClassMethods
68
+
69
+ def self.included(receiver)
70
+ receiver.extend(ClassMethods)
71
+ end
72
+ end
@@ -0,0 +1,20 @@
1
+ require 'swx_assembler'
2
+ require 'swx_gateway'
3
+
4
+ ActionController::Base.class_eval do
5
+ def render_with_swx(options = nil, &block)
6
+ if options.is_a?(Hash) && options.keys.include?(:swx)
7
+ swf_bytecode = SwxAssembler.write_swf(
8
+ options[:swx],
9
+ params[:debug],
10
+ SwxGateway.swx_config['compression_level'],
11
+ params[:url],
12
+ SwxGateway.swx_config['allow_domain']
13
+ )
14
+ send_data(swf_bytecode, :type => 'application/swf', :filename => 'data.swf')
15
+ else
16
+ render_without_swx(options, &block)
17
+ end
18
+ end
19
+ alias_method_chain :render, :swx
20
+ end
@@ -0,0 +1,18 @@
1
+ # Service Path: Put your service classes in this folder.
2
+ # Is relative to RAILS_ROOT
3
+ services_path: app/services
4
+
5
+ # Allow any domain to access the data SWFs from the SWX gateway?
6
+ # If you set this to false, you will only be able to call the
7
+ # SWX gateway from SWFs that reside on exactly the same domain
8
+ # (not even subdomains are allowed.)
9
+ #
10
+ # TODO: Allow user to specify a domain (or domains) to allow by
11
+ # providing URLs.
12
+ allow_domain: true
13
+
14
+ # How much to compress the returned SWF files. Values range from
15
+ # 0 (no compression) to 9 (maximum compression). The default
16
+ # compression level is 4. Higher levels of compression may result in
17
+ # smaller SWF files but will take longer to process.
18
+ compression_level: 4
@@ -0,0 +1,6 @@
1
+ class SwxController < ApplicationController
2
+ def gateway
3
+ # request handler takes in the params hash
4
+ send_data(SwxGateway.process(params), :type => 'application/swf', :filename => 'data.swf')
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ class Arithmetic
2
+ def addition(first, second)
3
+ first + second
4
+ end
5
+
6
+ def multiply(first, second)
7
+ first * second
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ class DiscoveryService
2
+
3
+ end
@@ -0,0 +1,7 @@
1
+ # Class and method names follow standard Ruby conventions
2
+ class HelloWorld
3
+ # Service class methods are instance methods.
4
+ def just_say_the_words
5
+ 'Hello World!'
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class Simple
2
+ def add_numbers(first, second)
3
+ first + second
4
+ end
5
+ end
@@ -0,0 +1,94 @@
1
+ class TestTO
2
+ def initialize
3
+ @prop1 = 'A string'
4
+ @prop2 = 42
5
+ end
6
+ end
7
+
8
+ ###
9
+ # Data type test service. Run the TestDataTypes.fla to run through all these tests using SWX RPC.
10
+ #
11
+ # @package default
12
+ # @author Aral Balkan
13
+ ###
14
+ class TestDataTypes
15
+ ###
16
+ # Returns the boolean true.
17
+ # @author Aral Balkan
18
+ ###
19
+ def test_true
20
+ true
21
+ end
22
+
23
+ ###
24
+ # Returns the boolean false.
25
+ # @author Aral Balkan
26
+ ###
27
+ def test_false
28
+ false
29
+ end
30
+
31
+ ###
32
+ # Returns the array ['It', 'works']
33
+ # @author Aral Balkan
34
+ ###
35
+ def test_array
36
+ %w(It works)
37
+ end
38
+
39
+ ###
40
+ # Returns the nested array ['It', ['also'], 'works]
41
+ # @author Aral Balkan
42
+ ###
43
+ def test_nested_array
44
+ ['It', ['also'], 'works']
45
+ end
46
+
47
+ ###
48
+ # Returns the integer 42.
49
+ # @author Aral Balkan
50
+ ###
51
+ def test_integer
52
+ 42
53
+ end
54
+
55
+ ###
56
+ # Returns the float 42.12345.
57
+ # @author Aral Balkan
58
+ ###
59
+ def test_float
60
+ 42.12345
61
+ end
62
+
63
+ ###
64
+ # Returns the string "It works!"
65
+ # @author Aral Balkan
66
+ ###
67
+ def test_string
68
+ "It works!"
69
+ end
70
+
71
+ ###
72
+ # Returns the associative array ['it' => 'works', 'number' => 42]
73
+ # @author Aral Balkan
74
+ ###
75
+ def test_associative_array
76
+ {'it' => 'works', 'number' => 42}
77
+ end
78
+
79
+ ###
80
+ # Returns an instance of the TestTO class with properties prop1: "A string" and prop2: 42.
81
+ # @author Aral Balkan
82
+ ###
83
+ def test_object
84
+ TestTO.new
85
+ end
86
+
87
+ ###
88
+ # Returns null.
89
+ # @author Aral Balkan
90
+ ###
91
+ def test_null
92
+ nil
93
+ end
94
+ end
@@ -0,0 +1,138 @@
1
+ require 'bytecode_converter'
2
+ require 'helper_module'
3
+ require 'uri/common.rb'
4
+ require 'zlib'
5
+
6
+ class SwxAssembler
7
+ include HelperMethods
8
+
9
+ # Header - FCS (uncompressed), version Flash 6
10
+ UNCOMPRESSED_SWF = '46'
11
+ COMPRESSED_SWF = '43'
12
+ HEADER = '575306LLLLLLLL300A00A0000101004302FFFFFF'
13
+
14
+ # Misc
15
+ NULL_TERMINATOR = '00'
16
+
17
+ # Allow domain (*)
18
+ ALLOW_DOMAIN = '960900005F706172656E74001C960600005F75726C004E960D0007010000000053797374656D001C960A00007365637572697479004E960D0000616C6C6F77446F6D61696E005217'
19
+ SYSTEM_ALLOW_DOMAIN = '07010000000053797374656D001C960A00007365637572697479004E960D0000616C6C6F77446F6D61696E005217'
20
+
21
+ # Debug SWX bytecode. Creates a local connection to the SWX Debugger front-end.)
22
+ DEBUG_START = '883C000700726573756C74006C63004C6F63616C436F6E6E656374696F6E005F737778446562756767657200636F6E6E6563740064656275670073656E6400'
23
+ DEBUG_END = '960D0008010600000000000000000802403C9609000803070100000008011C9602000804521796020008001C960500070100000042960B0008050803070300000008011C96020008065217'
24
+
25
+ class << self
26
+ def allow_domain_bytecode(allow_domain_url = '')
27
+ if (allow_domain_url.nil? || allow_domain_url.empty?)
28
+ # No URL passed -- possibly called by legacy code, use the old _parent._url version.
29
+ # error_log('[SWX] INFO: No URL passed from client. Defaulting to old behavior. You must call System.security.allowDomain on the dataHolder for cross domain data loading to work.');
30
+ ALLOW_DOMAIN
31
+ else
32
+ # Firefox/Flash (at least, and tested only on a Mac), sends
33
+ # file:/// (three slashses) in the URI and that fails the validation
34
+ # so replacing that with two slashes instead.
35
+ allow_domain_url.gsub!('///', '//')
36
+ # URL is passed, write that into the returned code
37
+ allow_domain_bytecode = BytecodeConverter.convert(URI.unescape(allow_domain_url))
38
+
39
+ # The -13 is to accomodate the other elements being pushed to the
40
+ # stack in the hard-coded part of the bytecode.
41
+ allow_domain_bytecode_length_dec = allow_domain_bytecode.length/2 + 13
42
+
43
+ allow_domain_bytecode_length = integer_to_hexadecimal(allow_domain_bytecode_length_dec, 2);
44
+ allow_domain_bytecode = '96' + allow_domain_bytecode_length + allow_domain_bytecode + SYSTEM_ALLOW_DOMAIN;
45
+ allow_domain_bytecode
46
+ end
47
+ end
48
+
49
+ def compress_swx_file(swx_file, compression_level)
50
+ # The first eight bytes of a compressed SWF file are left uncompressed
51
+ swx_file.slice!(0...8) + Zlib::Deflate.deflate(swx_file, compression_level)
52
+ end
53
+
54
+ def generate_data_bytecode(data)
55
+ data_bytecode = []
56
+
57
+ # Add a flag to the beginning of the bytecode that tells Flash we're setting a variable (result)
58
+ data_bytecode.push ActionCodes::SET_VARIABLE
59
+
60
+ # Convert the data (payload) to bytecode
61
+ data_bytecode.push BytecodeConverter.convert(data)
62
+
63
+ # Generate a push tag if the data was not an Array or a Hash
64
+ data_bytecode.push generate_push_statement(data_bytecode) unless data.is_a?(Array) || data.is_a?(Hash)
65
+
66
+ # Add the 'result' variable name -- either
67
+ # using the constant table if in debug mode
68
+ # or as a regular string otherwise
69
+ if @debug
70
+ data_bytecode.push '9602000800'
71
+ else
72
+ data_bytecode.push '96080000726573756C7400'
73
+ end
74
+
75
+ # (SWF bytecode is stored in reverse, so we reverse it here)
76
+ data_bytecode.reverse.join
77
+ end
78
+
79
+ def generate_swx_bytecode(data)
80
+ # Create the DoAction tag
81
+ do_action_block = []
82
+
83
+ # Wrap the data bytecode in debug flags if debugging is turned on
84
+ do_action_block.push DEBUG_START if @debug
85
+
86
+ # Generate bytecode for the data (payload)
87
+ do_action_block.push generate_data_bytecode(data)
88
+
89
+ # Allow domain? If so add allow domain statement to the SWF
90
+ if (@allow_domain)
91
+ do_action_block.push allow_domain_bytecode(@allow_domain_url)
92
+ end
93
+
94
+ # Wrap the data bytecode in debug flags if debugging is turned on
95
+ do_action_block.push DEBUG_END if @debug
96
+
97
+ # Calculate the size of the do_action block
98
+ do_action_block_size_in_bytes = string_length_in_bytes_hex(do_action_block.join, 4)
99
+ # Add the appropriate flags to the do_action block and concat the finished do_action block into a string
100
+ do_action_block_string = ActionCodes::DO_ACTION + do_action_block_size_in_bytes + do_action_block.join
101
+
102
+ # Create the rest of the SWF
103
+ header_type = if @compression_level > 0 then COMPRESSED_SWF else UNCOMPRESSED_SWF end
104
+
105
+ swf = header_type + HEADER + do_action_block_string + ActionCodes::SHOW_FRAME + ActionCodes::END_SWF
106
+ swf_size_in_bytes = string_length_in_bytes_hex(swf, 4)
107
+
108
+ # Replace length placeholder (from HEADER constant) with actual bytecode length
109
+ swf.sub('LLLLLLLL', swf_size_in_bytes)
110
+ end
111
+
112
+ def write_swf(data, debug=false, compression_level=4, allow_domain_url='', allow_domain=true)
113
+ # Set up SwfAssembler state
114
+ @debug = debug
115
+ @compression_level = compression_level
116
+ @allow_domain_url = allow_domain_url
117
+ @allow_domain = allow_domain
118
+
119
+ swx_bytecode = generate_swx_bytecode(data)
120
+
121
+ # Convert the bytecode string to ASCII file format
122
+ swx_file = swx_bytecode.hex_to_ascii
123
+
124
+ # Compress the file if compression is turned on
125
+ swx_file = compress_swx_file(swx_file, compression_level) if compression_level > 0
126
+
127
+ # ====================================
128
+ # = TODO: Remove this before release =
129
+ # ====================================
130
+ # Write the file (for manual 'loadMovie' testing in Flash)
131
+ # File.open('/Users/Jed/Development/Libraries/rSWX/testing/flash/rswx_data.swx', 'w+') do |file|
132
+ # file << swx_file
133
+ # end
134
+
135
+ swx_file
136
+ end
137
+ end
138
+ end