ruby_sol 0.1

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