rubyamf-ouvrages 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,272 @@
1
+ module RubyAMF
2
+ # RubyAMF configuration container. It can be accessed by calling
3
+ # <tt>RubyAMF.configuration</tt>, or modified in a block like so:
4
+ #
5
+ # RubyAMF.configure do |config|
6
+ # config.gateway_path = "/amf"
7
+ # end
8
+ #
9
+ # === Gateway configuration
10
+ #
11
+ # Gateway configuration includes the gateway path and details about parameter
12
+ # mapping.
13
+ #
14
+ # +gateway_path+::
15
+ # Default: <tt>"/rubyamf/gateway"</tt>. The URL that responds to AMF requests.
16
+ # The URL should start with a "/" and not end with a "/".
17
+ #
18
+ # +populate_params_hash+::
19
+ # Default: <tt>true</tt>. For Rails users, all amf parameters can be accessed
20
+ # in the controller by calling <tt>rubyamf_params</tt>. If enabled, the amf
21
+ # parameters are merged into the <tt>params</tt> hash as well.
22
+ #
23
+ # +show_html_gateway+::
24
+ # Default: <tt>true</tt>. If enabled, non-AMF requests to the gateway url
25
+ # will result in a simple HTML page being returned.
26
+ #
27
+ # +param_mappings+::
28
+ # A hash that stores parameter mappings. Should only be modified through
29
+ # calls to <tt>map_params</tt>
30
+ #
31
+ # === Serialization options
32
+ #
33
+ # RubyAMF provides a wide variety of customization options for serialization
34
+ # to simplify integration.
35
+ #
36
+ # +translate_case+::
37
+ # Default: <tt>false</tt>. If enabled, properties will be converted to
38
+ # underscore style on deserialization from actionscript and will be converted
39
+ # to camelcase on serialization. This allows you to use language appropriate
40
+ # case style and have RubyAMF automatically care of translation.
41
+ #
42
+ # +auto_class_mapping+::
43
+ # Default: <tt>false</tt>. If a class mapping for a given ruby or actionscript
44
+ # class has not been defined, automatically maps it during serialization or
45
+ # deserialization. Nested ruby or actionscript classes will be automatically
46
+ # mapped to the class name without the namespace. Example:
47
+ # <tt>com.rubyamf.User => User</tt> or <tt>RubyAMF::User => User</tt>.
48
+ #
49
+ # +use_array_collection+::
50
+ # Default: <tt>false</tt>. If enabled, all arrays will be serialized as
51
+ # <tt>ArrayCollection</tt> objects. You can override this on a per-array
52
+ # basis by setting <tt>is_array_collection</tt> on the array to <tt>true</tt>
53
+ # or <tt>false</tt>. (Implementation in RocketAMF)
54
+ #
55
+ # +hash_key_access+::
56
+ # Default: <tt>:string</tt>. If set to <tt>:symbol</tt>, all deserialized
57
+ # hashes have the keys as symbols. RocketAMF defaults to strings, so setting
58
+ # to <tt>:symbol</tt> will reduce performance and possibly open you up to
59
+ # memory usage attacks.
60
+ #
61
+ # +preload_models+::
62
+ # If you are using in-model mapping and don't have <tt>auto_class_mapping</tt>
63
+ # enabled, you may need to force classes to be loaded before mapping takes
64
+ # effect. Simply include all necessary classes as strings to have RubyAMF
65
+ # force them to be loaded on initialization.
66
+ # Example: <tt>config.preload_models = ["User", "Course"]</tt>
67
+ #
68
+ # +check_for_associations+::
69
+ # Default: <tt>true</tt>. If enabled, associations that have been pre-loaded,
70
+ # either through :include or simply by being accessed, will be automatically
71
+ # included during serialization without any additional configuration.
72
+ #
73
+ # +ignore_fields+::
74
+ # Default: <tt>['created_at', 'created_on', 'updated_at', 'updated_on']</tt>.
75
+ # A list of all properties that should not be deserialized by default. The
76
+ # class-level <tt>:ignore_fields</tt> config overrides this.
77
+ class Configuration
78
+ # Gateway options
79
+ attr_accessor :gateway_path, :populate_params_hash, :show_html_gateway
80
+ attr_reader :param_mappings
81
+
82
+ # Serialization options
83
+ attr_accessor :translate_case, :auto_class_mapping, :use_array_collection,
84
+ :hash_key_access, :preload_models, :check_for_associations,
85
+ :ignore_fields
86
+
87
+ def initialize
88
+ @gateway_path = "/rubyamf/gateway"
89
+ @param_mappings = {}
90
+ @populate_params_hash = true
91
+ @show_html_gateway = true
92
+
93
+ @translate_case = false
94
+ @auto_class_mapping = false
95
+ @use_array_collection = false
96
+ @hash_key_access = :string
97
+ @preload_models = []
98
+ @check_for_associations = true
99
+ @ignore_fields = ['created_at', 'created_on', 'updated_at', 'updated_on']
100
+ end
101
+
102
+ # Maps the given array of named parameters to the arguments for calls to the
103
+ # given controller and action. For Rails users, the prefered method of
104
+ # parameter mapping is through routing (see RubyAMF::Rails::Routing).
105
+ #
106
+ # Example:
107
+ #
108
+ # config.map_params "UserController", "login", [:session_token, :username, :password]
109
+ # # params hash => {
110
+ # # 0 => "asdf", 1 => "user", 2 => "pass",
111
+ # # :session_token => "asdf", :username => "user", :password => "pass"
112
+ # # }
113
+ def map_params controller, action, params
114
+ @param_mappings[controller.to_s+"#"+action.to_s] = params
115
+ end
116
+
117
+ # Returns the class mapper class being used
118
+ def class_mapper
119
+ if @class_mapper.nil?
120
+ @class_mapper = RubyAMF::ClassMapping
121
+ end
122
+ @class_mapper
123
+ end
124
+
125
+ # Set to the class of any conforming class mapper to use it instead of
126
+ # <tt>RubyAMF::ClassMapping</tt>. If you don't need any of the advanced
127
+ # features offered by the RubyAMF class mapper, you can gain some substantial
128
+ # performance improvements by settings this to
129
+ # <tt>RocketAMF::Ext::FastClassMapping</tt> or <tt>RocketAMF::ClassMapping</tt>
130
+ # for the slower pure-ruby version.
131
+ def class_mapper= klass
132
+ @class_mapper = klass
133
+ end
134
+
135
+ # Loads the legacy config file at the given path or tries to locate it by
136
+ # looking for a <tt>rubyamf_config.rb</tt> file in several possible locations.
137
+ # Automatically run by RubyAMF if you are using Rails 2, it should be run
138
+ # before any additional configuration if you are not using Rails 2, as it
139
+ # overrides all previous configuration.
140
+ def load_legacy path=nil
141
+ # Locate legacy config
142
+ unless path
143
+ possible = []
144
+ possible << File.join(RAILS_ROOT, 'config', 'rubyamf_config.rb') if defined?(RAILS_ROOT)
145
+ possible << File.join('config', 'rubyamf_config.rb')
146
+ possible << 'rubyamf_config.rb'
147
+ unless path = possible.find {|p| File.exists?(p)}
148
+ raise "rubyamf_config.rb not found"
149
+ end
150
+ end
151
+
152
+ # Load legacy config
153
+ $" << "app/configuration" # prevent legacy code require from doing anything
154
+ LegacySandbox.module_eval(File.read(path))
155
+ $".pop
156
+ cm = LegacySandbox::RubyAMF::ClassMappings
157
+ pm = LegacySandbox::RubyAMF::ParameterMappings
158
+
159
+ # Raise exceptions for disabled settings
160
+ if cm.force_active_record_ids != nil; raise "CONFIG PARSE ERROR: force_active_record_ids is no longer supported. Use <tt>:except</tt> if you want to prevent the id from being serialized."; end
161
+ if cm.hash_key_access == :indifferent; raise "CONFIG PARSE ERROR: indifferent hash_key_access is not supported for performance reasons. Use either :string or :symbol, the default."; end
162
+ if cm.default_mapping_scope != nil; raise "CONFIG PARSE ERROR: default_mapping_scope is not supported globally. Please log a feature request if you need it, or use switch to the new config syntax which supports per-model defaults."; end
163
+ if cm.use_ruby_date_time == true; raise "CONFIG PARSE ERROR: use_ruby_date_time is not supported by RocketAMF. Please log a feature request if you need it."; end
164
+ if pm.scaffolding == true; raise "CONFIG PARSE ERROR: scaffolding is not supported. Please log a feature request if you need it."; end
165
+
166
+ # Populate ClassMappings configs from legacy config
167
+ @ignore_fields = cm.ignore_fields unless cm.ignore_fields.nil?
168
+ @translate_case = cm.translate_case if cm.translate_case == true
169
+ @hash_key_access = cm.hash_key_access unless cm.hash_key_access.nil?
170
+ @use_array_collection = cm.use_array_collection if cm.use_array_collection == true
171
+ @check_for_associations = cm.check_for_associations if cm.check_for_associations == false
172
+ mapset = class_mapper.mappings
173
+ (cm.mappings || []).each do |legacy_mapping|
174
+ mapping = {}
175
+ if legacy_mapping[:type] == 'active_resource'
176
+ raise "CONFIG PARSE ERROR: active_resource mapping type is not supported. Please log a feature request or stop using it."
177
+ end
178
+
179
+ # Extract unscoped settings
180
+ mapping[:as] = legacy_mapping[:actionscript]
181
+ mapping[:ruby] = legacy_mapping[:ruby]
182
+ mapping[:methods] = legacy_mapping[:methods] unless legacy_mapping[:methods].nil?
183
+ mapping[:ignore_fields] = legacy_mapping[:ignore_fields] unless legacy_mapping[:ignore_fields].nil?
184
+
185
+ # Process possibly scoped settings
186
+ attrs = legacy_mapping[:attributes]
187
+ assoc = legacy_mapping[:associations]
188
+ if attrs.is_a?(Hash) || assoc.is_a?(Hash)
189
+ # Extract scopes
190
+ scopes = []
191
+ scopes += attrs.keys if attrs.is_a?(Hash)
192
+ scopes += assoc.keys if assoc.is_a?(Hash)
193
+
194
+ # Build settings for scopes
195
+ scopes.each do |scope|
196
+ scoped = mapping.dup
197
+ scoped[:scope] = scope.to_sym
198
+ scoped[:only] = attrs.is_a?(Hash) ? attrs[scope] : attrs
199
+ scoped.delete(:only) if scoped[:only].nil?
200
+ scoped[:include] = assoc.is_a?(Hash) ? assoc[scope] : assoc
201
+ scoped.delete(:include) if scoped[:include].nil?
202
+ mapset.map scoped
203
+ end
204
+ else
205
+ # No scoping
206
+ mapping[:only] = attrs unless attrs.nil?
207
+ mapping[:include] = assoc unless assoc.nil?
208
+ mapset.map mapping
209
+ end
210
+ end
211
+
212
+ # Populate ParameterMapping configs from legacy config
213
+ @populate_params_hash = pm.always_add_to_params if pm.always_add_to_params == false
214
+ (pm.mappings || []).each do |m|
215
+ params = []
216
+ m[:params].each do |k, v|
217
+ unless v =~ /\[(\d+)\]/
218
+ raise "CONFIG PARSE ERROR: parameter mappings are no longer evalled - '#{v}' must match [DIGITS]. Please log a feature request if you need this."
219
+ end
220
+ params[$1.to_i] = k
221
+ end
222
+ self.map_params m[:controller], m[:action], params
223
+ end
224
+
225
+ # Reset sandbox
226
+ cm.reset
227
+ pm.reset
228
+
229
+ self
230
+ end
231
+ end
232
+
233
+ module LegacySandbox #:nodoc:
234
+ module RubyAMF #:nodoc:
235
+ module ClassMappings #:nodoc:
236
+ class << self
237
+ attr_accessor :ignore_fields, :translate_case, :force_active_record_ids, :hash_key_access,
238
+ :assume_types, :default_mapping_scope, :use_ruby_date_time, :use_array_collection,
239
+ :check_for_associations, :mappings
240
+
241
+ def register mapping
242
+ @mappings ||= []
243
+ @mappings << mapping
244
+ end
245
+
246
+ def reset
247
+ methods.each do |m|
248
+ send(m, nil) if m =~ /[\w_]+=/
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ module ParameterMappings #:nodoc:
255
+ class << self
256
+ attr_accessor :always_add_to_params, :scaffolding, :mappings
257
+
258
+ def register mapping
259
+ @mappings ||= []
260
+ @mappings << mapping
261
+ end
262
+
263
+ def reset
264
+ methods.each do |m|
265
+ send(m, nil) if m =~ /[\w_]+=/
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,102 @@
1
+ require 'base64'
2
+
3
+ module RubyAMF
4
+ # Adds several important features to RocketAMF::Envelope. None of these features
5
+ # are dependent on Rails, and as such can be used by any Rack compliant framework.
6
+ # Features are credentials support, easy parameter mapping based on configured
7
+ # parameter mappings, mapping scope support for serialization, and error handling
8
+ # for method dispatch using <tt>each_method_call</tt>.
9
+ class Envelope < RocketAMF::Envelope
10
+ attr_accessor :mapping_scope
11
+
12
+ # Finds and returns credentials set on the request as a hash with keys
13
+ # <tt>username</tt> and <tt>password</tt>, with the type dependent on the
14
+ # <tt>hash_key_access</tt> setting. <tt>setHeader('Credentials')</tt>
15
+ # credentials are used first, followed by new-style <tt>DSRemoteCredentials</tt>.
16
+ # If no credentials are found, a hash is returned with a username and password
17
+ # of <tt>nil</tt>.
18
+ def credentials
19
+ ds_cred_key = RubyAMF.configuration.translate_case ? "ds_remote_credentials" : "DSRemoteCredentials"
20
+ if RubyAMF.configuration.hash_key_access == :symbol
21
+ userid_key = :userid
22
+ username_key = :username
23
+ password_key = :password
24
+ ds_cred_key = ds_cred_key.to_sym
25
+ else
26
+ userid_key = "userid"
27
+ username_key = "username"
28
+ password_key = "password"
29
+ end
30
+
31
+ # Old style setHeader('Credentials', CREDENTIALS_HASH)
32
+ if @headers['Credentials']
33
+ h = @headers['Credentials']
34
+ return {username_key => h.data[userid_key], password_key => h.data[password_key]}
35
+ end
36
+
37
+ # New style DSRemoteCredentials
38
+ messages.each do |m|
39
+ if m.data.is_a?(RocketAMF::Values::RemotingMessage)
40
+ if m.data.headers && m.data.headers[ds_cred_key]
41
+ username,password = Base64.decode64(m.data.headers[ds_cred_key]).split(':')
42
+ return {username_key => username, password_key => password}
43
+ end
44
+ end
45
+ end
46
+
47
+ # Failure case sends empty credentials, because rubyamf_plugin does it
48
+ {username_key => nil, password_key => nil}
49
+ end
50
+
51
+ # Given a controller, action, and the flash arguments array, returns a hash
52
+ # containing the arguments indexed by number as well as named key if a named
53
+ # mapping has been configured. Returned hash respects <tt>hash_key_access</tt>
54
+ # setting for named keys.
55
+ #
56
+ # Example:
57
+ #
58
+ # RubyAMF.configuration.map_params "c", "a", ["param1", "param2"]
59
+ # params = envelope.params_hash "c", "a", ["asdf", "fdsa"]
60
+ # params.should == {:param1 => "asdf", :param2 => "fdsa", 0 => "asdf", 1 => "fdsa"}
61
+ def params_hash controller, action, arguments
62
+ conf = RubyAMF.configuration
63
+ mapped = {}
64
+ mapping = conf.param_mappings[controller+"#"+action]
65
+ arguments.each_with_index do |arg, i|
66
+ mapped[i] = arg
67
+ if mapping && mapping[i]
68
+ mapping_key = conf.hash_key_access == :symbol ? mapping[i].to_sym : mapping[i].to_s
69
+ mapped[mapping_key] = arg
70
+ end
71
+ end
72
+ mapped
73
+ end
74
+
75
+ # Extends default RocketAMF implementation to log caught exceptions and
76
+ # translate them into a RocketAMF::Values::ErrorMessage for return to flash
77
+ # after removing the backtrace (for safety).
78
+ def dispatch_call p
79
+ begin
80
+ ret = p[:block].call(p[:method], p[:args])
81
+ raise ret if ret.is_a?(Exception) # If they return FaultObject like you could in rubyamf_plugin
82
+ ret
83
+ rescue Exception => e
84
+ # Log exception
85
+ RubyAMF.logger.log_error(e)
86
+
87
+ # Clear backtrace so that RocketAMF doesn't send back the full backtrace
88
+ e.set_backtrace([])
89
+
90
+ # Create ErrorMessage object using the source message as the base
91
+ RocketAMF::Values::ErrorMessage.new(p[:source], e)
92
+ end
93
+ end
94
+
95
+ def serialize class_mapper=nil #:nodoc:
96
+ # Create a ClassMapper and set the mapping scope to pass to super implementation.
97
+ cm = class_mapper || RubyAMF::ClassMapper.new
98
+ cm.mapping_scope = mapping_scope if cm.respond_to?(:mapping_scope=)
99
+ super cm
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,31 @@
1
+ module RubyAMF
2
+ class Fault < ::Exception #:nodoc:
3
+ end
4
+ end
5
+
6
+ # Convenience object carried over from rubyamf_plugin, it can be returned for
7
+ # AMF instead of raising an exception.
8
+ #
9
+ # Example:
10
+ #
11
+ # render :amf => FaultObject.new("No users to get")
12
+ class FaultObject
13
+ def self.new *args, &block
14
+ if args.length == 2
15
+ raise "payload for FaultObject is no longer available - if you need it you will need to create a custom class to contain your payload"
16
+ end
17
+ message = args.length > 0 ? args[0] : ''
18
+
19
+ begin
20
+ raise RubyAMF::Fault.new(message)
21
+ rescue Exception => e
22
+ # Fix backtrace
23
+ b = e.backtrace
24
+ b.shift
25
+ e.set_backtrace(b)
26
+
27
+ # Return new fault object
28
+ return e
29
+ end
30
+ end
31
+ end
Binary file
@@ -0,0 +1,18 @@
1
+ module RubyAMF
2
+ # IntermediateObject packages an object with its requested serialization options.
3
+ # This allows the pre-configured serialization configuration to be overridden
4
+ # as needed. They are automatically created by <tt>to_amf</tt> and should not
5
+ # be generated manually.
6
+ class IntermediateObject
7
+ attr_accessor :object, :options
8
+
9
+ def initialize object, options
10
+ @object = object
11
+ @options = options
12
+ end
13
+
14
+ def rubyamf_hash
15
+ @object.rubyamf_hash @options
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ require 'logger'
2
+
3
+ module RubyAMF
4
+ class Logger #:nodoc:
5
+ # Log exceptions in rails-style, with cleaned backtrace if available
6
+ def log_error e
7
+ msg = "#{e.class} (#{e.message}):\n "
8
+ msg += clean_backtrace(e).join("\n ")
9
+ logger.fatal(msg)
10
+ end
11
+
12
+ # Send every other method call to internally wrapped logger
13
+ def method_missing name, *args
14
+ logger.send(name, args)
15
+ end
16
+
17
+ private
18
+ # Use rails logger if available or create standard ruby logger to STDERR
19
+ def logger
20
+ unless @logger
21
+ if defined?(Rails) && ::Rails.logger
22
+ @logger = ::Rails.logger
23
+ else
24
+ @logger = ::Logger.new(STDERR)
25
+ end
26
+ end
27
+ @logger
28
+ end
29
+
30
+ # Use Rails backtrace cleaner if it exists to clean
31
+ def clean_backtrace e
32
+ if defined?(Rails) && ::Rails.respond_to?(:backtrace_cleaner)
33
+ ::Rails.backtrace_cleaner.clean(e.backtrace)
34
+ else
35
+ e.backtrace
36
+ end
37
+ end
38
+ end
39
+ end