ruby_sol 0.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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/README.rdoc +90 -0
  3. data/Rakefile +43 -0
  4. data/lib/ruby_sol.rb +113 -0
  5. data/lib/ruby_sol/class_mapping.rb +250 -0
  6. data/lib/ruby_sol/constants.rb +50 -0
  7. data/lib/ruby_sol/extensions.rb +22 -0
  8. data/lib/ruby_sol/pure/deserializer.rb +461 -0
  9. data/lib/ruby_sol/pure/io_helpers.rb +94 -0
  10. data/lib/ruby_sol/pure/serializer.rb +490 -0
  11. data/spec/class_mapping_spec.rb +110 -0
  12. data/spec/deserializer_spec.rb +449 -0
  13. data/spec/fixtures/objects/amf0-boolean.bin +1 -0
  14. data/spec/fixtures/objects/amf0-complex-encoded-string.bin +0 -0
  15. data/spec/fixtures/objects/amf0-date.bin +0 -0
  16. data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
  17. data/spec/fixtures/objects/amf0-hash.bin +0 -0
  18. data/spec/fixtures/objects/amf0-null.bin +1 -0
  19. data/spec/fixtures/objects/amf0-number.bin +0 -0
  20. data/spec/fixtures/objects/amf0-object.bin +0 -0
  21. data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
  22. data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
  23. data/spec/fixtures/objects/amf0-string.bin +0 -0
  24. data/spec/fixtures/objects/amf0-time.bin +0 -0
  25. data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
  26. data/spec/fixtures/objects/amf0-undefined.bin +1 -0
  27. data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
  28. data/spec/fixtures/objects/amf0-xml-doc.bin +0 -0
  29. data/spec/fixtures/objects/amf3-0.bin +0 -0
  30. data/spec/fixtures/objects/amf3-array-collection.bin +2 -0
  31. data/spec/fixtures/objects/amf3-array-ref.bin +1 -0
  32. data/spec/fixtures/objects/amf3-associative-array.bin +1 -0
  33. data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
  34. data/spec/fixtures/objects/amf3-byte-array-ref.bin +1 -0
  35. data/spec/fixtures/objects/amf3-byte-array.bin +0 -0
  36. data/spec/fixtures/objects/amf3-complex-array-collection.bin +6 -0
  37. data/spec/fixtures/objects/amf3-complex-encoded-string-array.bin +1 -0
  38. data/spec/fixtures/objects/amf3-date-ref.bin +0 -0
  39. data/spec/fixtures/objects/amf3-date.bin +0 -0
  40. data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
  41. data/spec/fixtures/objects/amf3-dynamic-object.bin +2 -0
  42. data/spec/fixtures/objects/amf3-empty-array-ref.bin +1 -0
  43. data/spec/fixtures/objects/amf3-empty-array.bin +1 -0
  44. data/spec/fixtures/objects/amf3-empty-dictionary.bin +0 -0
  45. data/spec/fixtures/objects/amf3-empty-string-ref.bin +1 -0
  46. data/spec/fixtures/objects/amf3-encoded-string-ref.bin +0 -0
  47. data/spec/fixtures/objects/amf3-externalizable.bin +0 -0
  48. data/spec/fixtures/objects/amf3-false.bin +1 -0
  49. data/spec/fixtures/objects/amf3-float.bin +0 -0
  50. data/spec/fixtures/objects/amf3-graph-member.bin +0 -0
  51. data/spec/fixtures/objects/amf3-hash.bin +2 -0
  52. data/spec/fixtures/objects/amf3-large-max.bin +0 -0
  53. data/spec/fixtures/objects/amf3-large-min.bin +0 -0
  54. data/spec/fixtures/objects/amf3-max.bin +1 -0
  55. data/spec/fixtures/objects/amf3-min.bin +0 -0
  56. data/spec/fixtures/objects/amf3-mixed-array.bin +10 -0
  57. data/spec/fixtures/objects/amf3-null.bin +1 -0
  58. data/spec/fixtures/objects/amf3-object-ref.bin +0 -0
  59. data/spec/fixtures/objects/amf3-primitive-array.bin +1 -0
  60. data/spec/fixtures/objects/amf3-string-ref.bin +0 -0
  61. data/spec/fixtures/objects/amf3-string.bin +1 -0
  62. data/spec/fixtures/objects/amf3-symbol.bin +1 -0
  63. data/spec/fixtures/objects/amf3-trait-ref.bin +3 -0
  64. data/spec/fixtures/objects/amf3-true.bin +1 -0
  65. data/spec/fixtures/objects/amf3-typed-object.bin +2 -0
  66. data/spec/fixtures/objects/amf3-vector-double.bin +0 -0
  67. data/spec/fixtures/objects/amf3-vector-int.bin +0 -0
  68. data/spec/fixtures/objects/amf3-vector-object.bin +0 -0
  69. data/spec/fixtures/objects/amf3-vector-uint.bin +0 -0
  70. data/spec/fixtures/objects/amf3-xml-doc.bin +1 -0
  71. data/spec/fixtures/objects/amf3-xml-ref.bin +1 -0
  72. data/spec/fixtures/objects/amf3-xml.bin +1 -0
  73. data/spec/serializer_spec.rb +503 -0
  74. data/spec/spec_helper.rb +59 -0
  75. metadata +128 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b2593b67e173346abecef94cbccce4e9df27a752
4
+ data.tar.gz: 326e3fdfedc35f62b87ef8f52d09dd948dde2513
5
+ SHA512:
6
+ metadata.gz: a5622c3d5f7dd27f473886ff0a594a645e8617e85597a04e077df44ddfca31862c1f9efaceefdee6ef9079d962a1c06eebf7c139ad28d9e5b54add7158a662b9
7
+ data.tar.gz: 7f14fdcaaacb200e80507c9537aa771c32844e006c136c0926801ba300f839afbecb64aa4d5e6847fb4b759ee81b8f6d0dd1727d9ea7a1251967a76742804037
data/README.rdoc ADDED
@@ -0,0 +1,90 @@
1
+ == DESCRIPTION:
2
+
3
+ ruby_sol - based on RocketAMF reader/write of Flash Player's .sol files.
4
+
5
+ == INSTALLATION
6
+ gem install ruby_sol
7
+
8
+ == EXAMPLE
9
+
10
+ require 'pp'
11
+ require "stringio"
12
+ require 'ruby_sol'
13
+
14
+ #
15
+ # reading sol file
16
+ #
17
+ # read_sol adds 2 field automatically
18
+ # __ruby_sol__amf_version - version of amf inside sol
19
+ # __ruby_sol__sol_name - name of the sol stored inside file
20
+ #
21
+ filename = 'test.sol';
22
+ content = RubySol::read_sol(File.new(filename).read)
23
+ pp content
24
+
25
+
26
+ #
27
+ # writing sol file
28
+ #
29
+ # data to store should be hash (as sol is key-value storage)
30
+ # hash keys - should be string, values - any object (that can be encoded via AMF)
31
+ # hash keys with __ruby_sol__ prefix don't stored, but used
32
+ # to send some sol-encoding parameters.
33
+ data = { "__ruby_sol__amf_version" => 3, # tells RubySol which amf version should be used
34
+ "__ruby_sol__sol_name" => "test", # tells RubySol the name of sol should be written inside
35
+ "sample_data1"=> {
36
+ 'a'=>'b',
37
+ 'c' => [1,2,3,4],
38
+ "float"=>135799632.71333,
39
+ "datetime"=>DateTime.new(2013,4,26,8,0,0,'+2'),
40
+ "int" => 1,
41
+ },
42
+ 'sample_data2' => ['a', 'b', 'c']
43
+ }
44
+ f = File.new('test.sol' , 'w')
45
+ f.write(RubySol::create_sol(data))
46
+ f.close()
47
+
48
+ == EXAMPLE2 - dump all FlashPlayer's files content
49
+
50
+ require 'pp'
51
+ require "stringio"
52
+ require 'ruby_sol'
53
+
54
+ files = Dir.glob(ENV['HOME']+"/.macromedia/Flash_Player/**/*.sol")
55
+ files.each {|filename|
56
+ puts filename
57
+ pp RubySol::read_sol(File.new(filename).read)
58
+ }
59
+
60
+ == SEE ALSO:
61
+
62
+ * http://wiki.gnashdev.org/SOL
63
+ * http://www.pyamf.org/tutorials/general/sharedobject.html
64
+
65
+ == LICENSE:
66
+
67
+ RocketAMF's licence
68
+
69
+ (The MIT License)
70
+
71
+ Copyright (c) 2011 Stephen Augenstein and Jacob Henry
72
+
73
+ Permission is hereby granted, free of charge, to any person obtaining
74
+ a copy of this software and associated documentation files (the
75
+ 'Software'), to deal in the Software without restriction, including
76
+ without limitation the rights to use, copy, modify, merge, publish,
77
+ distribute, sublicense, and/or sell copies of the Software, and to
78
+ permit persons to whom the Software is furnished to do so, subject to
79
+ the following conditions:
80
+
81
+ The above copyright notice and this permission notice shall be
82
+ included in all copies or substantial portions of the Software.
83
+
84
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
85
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
86
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
87
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
88
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
89
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
90
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rdoc/task'
4
+ require 'rubygems/package_task'
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc 'Default: run the specs.'
8
+ task :default => :spec
9
+
10
+ # I don't want to depend on bundler, so we do it the bundler way without it
11
+ gemspec_path = 'RubySol.gemspec'
12
+ spec = begin
13
+ eval(File.read(File.join(File.dirname(__FILE__), gemspec_path)), TOPLEVEL_BINDING, gemspec_path)
14
+ rescue LoadError => e
15
+ original_line = e.backtrace.find { |line| line.include?(gemspec_path) }
16
+ msg = "There was a LoadError while evaluating #{gemspec_path}:\n #{e.message}"
17
+ msg << " from\n #{original_line}" if original_line
18
+ msg << "\n"
19
+ puts msg
20
+ exit
21
+ end
22
+
23
+ RSpec::Core::RakeTask.new do |t|
24
+ end
25
+
26
+ desc 'Generate documentation'
27
+ RDoc::Task.new(:rdoc) do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = spec.name
30
+ rdoc.options += spec.rdoc_options
31
+ rdoc.rdoc_files.include(*spec.extra_rdoc_files)
32
+ rdoc.rdoc_files.include("lib") # Don't include ext folder because no one cares
33
+ end
34
+
35
+ Gem::PackageTask.new(spec) do |pkg|
36
+ pkg.need_zip = false
37
+ pkg.need_tar = false
38
+ end
39
+
40
+ desc "Build gem packages"
41
+ task :gems do
42
+ sh "rake gem RUBY_CC_VERSION=1.8.7:1.9.2"
43
+ end
data/lib/ruby_sol.rb ADDED
@@ -0,0 +1,113 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+ $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/ruby_sol/"
3
+
4
+ require "date"
5
+ require "stringio"
6
+ require 'ruby_sol/extensions'
7
+ require 'ruby_sol/class_mapping'
8
+ require 'ruby_sol/constants'
9
+ require 'ruby_sol/pure/deserializer'
10
+ require 'ruby_sol/pure/serializer'
11
+
12
+ SOL_HEADER = "\0\xBF"
13
+ SOL_SIGNATURE = "TCSO\0\x04\0\0\0\0"
14
+ SOL_PADDING = "\0"
15
+
16
+ module RubySol
17
+
18
+ Deserializer = RubySol::Pure::Deserializer
19
+ Serializer = RubySol::Pure::Serializer
20
+
21
+ # usage RubySol::read_sol(File.new(filename, 'r:ASCII-8BIT').read)
22
+ def self.read_sol(source)
23
+
24
+ if StringIO === source
25
+ f = source
26
+ elsif source
27
+ f = StringIO.new(source)
28
+ elsif f.nil?
29
+ raise AMFError, "no source to deserialize"
30
+ end
31
+
32
+ header = f.read(2)
33
+ len=f.read(4).unpack('N').first
34
+ throw SOLError, 'bad size' unless (len + 6 == f.size)
35
+ signature = f.read(10)
36
+ throw SOLError, 'bad signature' unless (signature == SOL_SIGNATURE)
37
+
38
+ des = RubySol::Deserializer.new(RubySol::ClassMapper.new)
39
+ des.source = f
40
+ sol_name=des.amf0_read_string()
41
+
42
+ zeros=f.read(3)
43
+ throw SOLError, 'bad signature' unless (zeros == SOL_PADDING * 3)
44
+
45
+ amf_version = f.read(1).unpack('C').first
46
+ values = {'__ruby_sol__amf_version' => amf_version,
47
+ '__ruby_sol__sol_name' => sol_name }
48
+
49
+ des.source = f
50
+
51
+ while !f.eof?
52
+ str = amf_version == 3? des.amf3_read_string() : des.amf0_read_string();
53
+ obj = amf_version == 3? des.amf3_deserialize() : des.amf0_deserialize();
54
+ values[str] = obj
55
+ padding = f.read(1);
56
+ end
57
+ return values
58
+ end
59
+
60
+ def self.create_sol(content)
61
+ name = content['__ruby_sol__sol_name'] || 'unknown'
62
+ version = content['__ruby_sol__amf_version'] || 0
63
+
64
+ ser = RubySol::Serializer.new(RubySol::ClassMapper.new)
65
+
66
+ ser.stream << SOL_SIGNATURE
67
+ ser.amf0_write_string_wo_marker(name)
68
+ ser.stream << SOL_PADDING * 3
69
+ ser.stream << [version].pack('c')
70
+
71
+ content.each_key { |key|
72
+ if key !~ /^__ruby_sol__/
73
+ if version == 0
74
+ ser.amf0_write_string_wo_marker(key)
75
+ ser.amf0_serialize(content[key])
76
+ ser.stream << SOL_PADDING
77
+ elsif version == 3
78
+ ser.amf3_write_utf8_vr(key)
79
+ ser.amf3_serialize(content[key])
80
+ ser.stream << SOL_PADDING
81
+ end
82
+ end
83
+ }
84
+ res = ser.stream
85
+ return SOL_HEADER.force_encoding("ASCII-8BIT") + [res.bytesize].pack('N').force_encoding("ASCII-8BIT") + res.force_encoding("ASCII-8BIT")
86
+ end
87
+ # We use const_missing to define the active ClassMapper at runtime. This way,
88
+ # heavy modification of class mapping functionality is still possible without
89
+ # forcing extenders to redefine the constant.
90
+ def self.const_missing const #:nodoc:
91
+ if const == :ClassMapper
92
+ RubySol.const_set(:ClassMapper, RubySol::ClassMapping)
93
+ else
94
+ super(const)
95
+ end
96
+ end
97
+
98
+ # to have RocketAMF specs compatibility
99
+ def self.deserialize source, amf_version = 0
100
+ des = RubySol::Deserializer.new(RubySol::ClassMapper.new)
101
+ des.deserialize(amf_version, source)
102
+ end
103
+ def self.serialize obj, amf_version = 0
104
+ ser = RubySol::Serializer.new(RubySol::ClassMapper.new)
105
+ ser.serialize(amf_version, obj)
106
+ end
107
+
108
+ # The base exception for AMF errors.
109
+ class AMFError < StandardError; end
110
+
111
+ # The base exception for ruby SOL errors.
112
+ class SOLError < StandardError; end
113
+ end
@@ -0,0 +1,250 @@
1
+ #require 'ruby_sol/values/typed_hash'
2
+ #require 'ruby_sol/values/messages'
3
+ module RubySol
4
+ module Values #:nodoc:
5
+ # Hash-like object that can store a type string. Used to preserve type information
6
+ # for unmapped objects after deserialization.
7
+ class TypedHash < Hash
8
+ attr_reader :type
9
+
10
+ def initialize type
11
+ @type = type
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ module RubySol
18
+ # Container for all mapped classes
19
+ class MappingSet
20
+ # Creates a mapping set object and populates the default mappings
21
+ def initialize
22
+ @as_mappings = {}
23
+ @ruby_mappings = {}
24
+ map_defaults
25
+ end
26
+
27
+ # Adds required mapping configs, calling map for the required base mappings.
28
+ # Designed to allow extenders to take advantage of required default mappings.
29
+ def map_defaults
30
+ # map :as => 'flex.messaging.messages.AbstractMessage', :ruby => 'RubySol::Values::AbstractMessage'
31
+ # map :as => 'flex.messaging.messages.RemotingMessage', :ruby => 'RubySol::Values::RemotingMessage'
32
+ # map :as => 'flex.messaging.messages.AsyncMessage', :ruby => 'RubySol::Values::AsyncMessage'
33
+ # map :as => 'DSA', :ruby => 'RubySol::Values::AsyncMessageExt'
34
+ # map :as => 'flex.messaging.messages.CommandMessage', :ruby => 'RubySol::Values::CommandMessage'
35
+ # map :as => 'DSC', :ruby => 'RubySol::Values::CommandMessageExt'
36
+ # map :as => 'flex.messaging.messages.AcknowledgeMessage', :ruby => 'RubySol::Values::AcknowledgeMessage'
37
+ # map :as => 'DSK', :ruby => 'RubySol::Values::AcknowledgeMessageExt'
38
+ # map :as => 'flex.messaging.messages.ErrorMessage', :ruby => 'RubySol::Values::ErrorMessage'
39
+ self
40
+ end
41
+
42
+ # Map a given AS class to a ruby class.
43
+ #
44
+ # Use fully qualified names for both.
45
+ #
46
+ # Example:
47
+ #
48
+ # m.map :as => 'com.example.Date', :ruby => 'Example::Date'
49
+ def map params
50
+ [:as, :ruby].each {|k| params[k] = params[k].to_s} # Convert params to strings
51
+ @as_mappings[params[:as]] = params[:ruby]
52
+ @ruby_mappings[params[:ruby]] = params[:as]
53
+ end
54
+
55
+ # Returns the AS class name for the given ruby class name, returing nil if
56
+ # not found
57
+ def get_as_class_name class_name #:nodoc:
58
+ @ruby_mappings[class_name.to_s]
59
+ end
60
+
61
+ # Returns the ruby class name for the given AS class name, returing nil if
62
+ # not found
63
+ def get_ruby_class_name class_name #:nodoc:
64
+ @as_mappings[class_name.to_s]
65
+ end
66
+ end
67
+
68
+ # Handles class name mapping between actionscript and ruby and assists in
69
+ # serializing and deserializing data between them. Simply map an AS class to a
70
+ # ruby class and when the object is (de)serialized it will end up as the
71
+ # appropriate class.
72
+ #
73
+ # Example:
74
+ #
75
+ # RubySol::ClassMapper.define do |m|
76
+ # m.map :as => 'AsClass', :ruby => 'RubyClass'
77
+ # m.map :as => 'vo.User', :ruby => 'Model::User'
78
+ # end
79
+ #
80
+ # == Object Population/Serialization
81
+ #
82
+ # In addition to handling class name mapping, it also provides helper methods
83
+ # for populating ruby objects from AMF and extracting properties from ruby objects
84
+ # for serialization. Support for hash-like objects and objects using
85
+ # <tt>attr_accessor</tt> for properties is currently built in, but custom classes
86
+ # may require subclassing the class mapper to add support.
87
+ #
88
+ # == Complete Replacement
89
+ #
90
+ # In some cases, it may be beneficial to replace the default provider of class
91
+ # mapping completely. In this case, simply assign your class mapper class to
92
+ # <tt>RubySol::ClassMapper</tt> after loading RubySol. Through the magic of
93
+ # <tt>const_missing</tt>, <tt>ClassMapper</tt> is only defined after the first
94
+ # access by default, so you get no annoying warning messages. Custom class mappers
95
+ # must implement the following methods on instances: <tt>use_array_collection</tt>,
96
+ # <tt>get_as_class_name</tt>, <tt>get_ruby_obj</tt>, <tt>populate_ruby_obj</tt>,
97
+ # and <tt>props_for_serialization</tt>. In addition, it should have a class level
98
+ # <tt>mappings</tt> method that returns the mapping set it's using, although its
99
+ # not required. If you'd like to see an example of what complete replacement
100
+ # offers, check out RubyAMF (http://github.com/rubyamf/rubyamf).
101
+ #
102
+ # Example:
103
+ #
104
+ # require 'rubygems'
105
+ # require 'ruby_sol'
106
+ #
107
+ # RubySol::ClassMapper = MyCustomClassMapper
108
+ # # No warning about already initialized constant ClassMapper
109
+ # RubySol::ClassMapper # MyCustomClassMapper
110
+ #
111
+ # == C ClassMapper
112
+ #
113
+ # The C class mapper, <tt>RubySol::Ext::FastClassMapping</tt>, has the same
114
+ # public API that <tt>RubyAMF::ClassMapping</tt> does, but has some additional
115
+ # performance optimizations that may interfere with the proper serialization of
116
+ # objects. To reduce the cost of processing public methods for every object,
117
+ # its implementation of <tt>props_for_serialization</tt> caches valid properties
118
+ # by class, using the class as the hash key for property lookup. This means that
119
+ # adding and removing properties from instances while serializing using a given
120
+ # class mapper instance will result in the changes not being detected. As such,
121
+ # it's not enabled by default. So long as you aren't planning on modifying
122
+ # classes during serialization using <tt>encode_amf</tt>, the faster C class
123
+ # mapper should be perfectly safe to use.
124
+ #
125
+ # Activating the C Class Mapper:
126
+ #
127
+ # require 'rubygems'
128
+ # require 'ruby_sol'
129
+ # RubySol::ClassMapper = RubySol::Ext::FastClassMapping
130
+ class ClassMapping
131
+ class << self
132
+ # Global configuration variable for sending Arrays as ArrayCollections.
133
+ # Defaults to false.
134
+ attr_accessor :use_array_collection
135
+
136
+ # Returns the mapping set with all the class mappings that is currently
137
+ # being used.
138
+ def mappings
139
+ @mappings ||= MappingSet.new
140
+ end
141
+
142
+ # Define class mappings in the block. Block is passed a <tt>MappingSet</tt> object
143
+ # as the first parameter.
144
+ #
145
+ # Example:
146
+ #
147
+ # RubySol::ClassMapper.define do |m|
148
+ # m.map :as => 'AsClass', :ruby => 'RubyClass'
149
+ # end
150
+ def define &block #:yields: mapping_set
151
+ yield mappings
152
+ end
153
+
154
+ # Reset all class mappings except the defaults and return
155
+ # <tt>use_array_collection</tt> to false
156
+ def reset
157
+ @use_array_collection = false
158
+ @mappings = nil
159
+ end
160
+ end
161
+
162
+ attr_reader :use_array_collection
163
+
164
+ # Copies configuration from class level configs to populate object
165
+ def initialize
166
+ @mappings = self.class.mappings
167
+ @use_array_collection = self.class.use_array_collection === true
168
+ end
169
+
170
+ # Returns the ActionScript class name for the given ruby object. Will also
171
+ # take a string containing the ruby class name.
172
+ def get_as_class_name obj
173
+ # Get class name
174
+ if obj.is_a?(String)
175
+ ruby_class_name = obj
176
+ elsif obj.is_a?(Values::TypedHash)
177
+ ruby_class_name = obj.type
178
+ elsif obj.is_a?(Hash)
179
+ return nil
180
+ else
181
+ ruby_class_name = obj.class.name
182
+ end
183
+
184
+ # Get mapped AS class name
185
+ @mappings.get_as_class_name ruby_class_name
186
+ end
187
+
188
+ # Instantiates a ruby object using the mapping configuration based on the
189
+ # source ActionScript class name. If there is no mapping defined, it returns
190
+ # a <tt>RubySol::Values::TypedHash</tt> with the serialized class name.
191
+ def get_ruby_obj as_class_name
192
+ ruby_class_name = @mappings.get_ruby_class_name as_class_name
193
+ if ruby_class_name.nil?
194
+ # Populate a simple hash, since no mapping
195
+ return Values::TypedHash.new(as_class_name)
196
+ else
197
+ ruby_class = ruby_class_name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
198
+ return ruby_class.new
199
+ end
200
+ end
201
+
202
+ # Populates the ruby object using the given properties. props and
203
+ # dynamic_props will be hashes with symbols for keys.
204
+ def populate_ruby_obj obj, props, dynamic_props=nil
205
+ props.merge! dynamic_props if dynamic_props
206
+
207
+ # Don't even bother checking if it responds to setter methods if it's a TypedHash
208
+ if obj.is_a?(Values::TypedHash)
209
+ obj.merge! props
210
+ return obj
211
+ end
212
+
213
+ # Some type of object
214
+ hash_like = obj.respond_to?("[]=")
215
+ props.each do |key, value|
216
+ if obj.respond_to?("#{key}=")
217
+ obj.send("#{key}=", value)
218
+ elsif hash_like
219
+ obj[key] = value
220
+ end
221
+ end
222
+ obj
223
+ end
224
+
225
+ # Extracts all exportable properties from the given ruby object and returns
226
+ # them in a hash. If overriding, make sure to return a hash wth string keys
227
+ # unless you are only going to be using the native C extensions, as the pure
228
+ # ruby serializer performs a sort on the keys to acheive consistent, testable
229
+ # results.
230
+ def props_for_serialization ruby_obj
231
+ # Handle hashes
232
+ if ruby_obj.is_a?(Hash)
233
+ # Stringify keys to make it easier later on and allow sorting
234
+ h = {}
235
+ ruby_obj.each {|k,v| h[k.to_s] = v}
236
+ return h
237
+ end
238
+
239
+ # Generic object serializer
240
+ props = {}
241
+ @ignored_props ||= Object.new.public_methods
242
+ (ruby_obj.public_methods - @ignored_props).each do |method_name|
243
+ # Add them to the prop hash if they take no arguments
244
+ method_def = ruby_obj.method(method_name)
245
+ props[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0
246
+ end
247
+ props
248
+ end
249
+ end
250
+ end