swxruby 0.7

Sign up to get free protection for your applications and to get access to all the features.
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