rubyamf-ouvrages 2.0.0

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.
@@ -0,0 +1,215 @@
1
+ module RubyAMF
2
+ # Simply include in your ruby object to enable advanced serialization features
3
+ # like an in-model mapping API, customizable initialization after
4
+ # deserialization, scoped property configuration for serialization, and several
5
+ # other things. See RubyAMF::Model::ClassMethods for details of in-model mapping
6
+ # API.
7
+ #
8
+ # Example:
9
+ #
10
+ # class SerializableObject
11
+ # include RubyAMF::Model
12
+ #
13
+ # as_class "com.rubyamf.ASObject"
14
+ # map_amf :only => "prop_a"
15
+ #
16
+ # attr_accessor :prop_a, :prop_b
17
+ # end
18
+ #
19
+ # == Integration
20
+ #
21
+ # If the object you include RubyAMF::Model into implements <tt>attributes</tt>
22
+ # and <tt>attributes=</tt>, those two methods will be automatically used to
23
+ # determine serializable properties and to set them after deserialization. If
24
+ # you do not implement those methods, attributes will be guessed by going through
25
+ # all methods that don't take arguments, and attribute setters will be used
26
+ # rather than <tt>attributes=</tt>.
27
+ #
28
+ # For most ORMs, the provided <tt>rubyamf_init</tt>, <tt>rubyamf_hash</tt>, and
29
+ # <tt>rubyamf_retrieve_association</tt> should work correctly. However, they
30
+ # can be overridden to provide custom behavior if the default has issues with
31
+ # the ORM you are using. See RubyAMF::Rails::Model for an example of ORM-specific
32
+ # customization.
33
+ module Model
34
+ def self.included base #:nodoc:
35
+ base.send :extend, ClassMethods
36
+ end
37
+
38
+ # In-model mapping configuration methods
39
+ module ClassMethods
40
+ # Specify the actionscript class name that this class maps to.
41
+ #
42
+ # Example:
43
+ #
44
+ # class SerializableObject
45
+ # include RubyAMF::Model
46
+ # as_class "com.rubyamf.ASObject"
47
+ # end
48
+ def as_class class_name
49
+ @as_class = class_name.to_s
50
+ RubyAMF::ClassMapper.mappings.map :as => @as_class, :ruby => self.name
51
+ end
52
+ alias :actionscript_class :as_class
53
+ alias :flash_class :as_class
54
+ alias :amf_class :as_class
55
+
56
+ # Define a parameter mapping for the default scope or a given scope. If the
57
+ # first parameter is a hash, it looks for a <tt>:default_scope</tt> key to
58
+ # set the default scope and scope the given configuration, and parses the
59
+ # other keys like <tt>serializable_hash</tt> does. If the first argument
60
+ # is a symbol, that symbol is assumed to be the scope for the given
61
+ # configuration. Just like <tt>serializable_hash</tt>, it supports
62
+ # <tt>:except</tt>, <tt>:only</tt>, <tt>:methods</tt>, and <tt>:include</tt>
63
+ # for relations. It also has an <tt>:ignore_fields</tt> configuration for
64
+ # skipping certain fields during deserialization if the actionscript object
65
+ # contains extra fields or to protect yourself from modification of
66
+ # protected properties. <tt>:ignore_fields</tt> must be defined on the
67
+ # default scope, or it will be ignored.
68
+ #
69
+ # Example:
70
+ #
71
+ # class SerializableObject
72
+ # include RubyAMF::Model
73
+ # as_class "com.rubyamf.ASObject"
74
+ # map_amf :only => "prop_a"
75
+ # map_amf :testing, :only => "prop_b"
76
+ # map_amf :default_scope => :asdf, :only => "prop_c", :ignore_fields => ["password", "password_confirm"]
77
+ # end
78
+ def map_amf scope_or_options=nil, options=nil
79
+ # Make sure they've already called as_class first
80
+ raise "Must define as_class first" unless @as_class
81
+
82
+ # Format parameters to pass to RubyAMF::MappingSet#map
83
+ if options
84
+ options[:scope] = scope_or_options
85
+ else
86
+ options = scope_or_options
87
+ end
88
+ options[:as] = @as_class
89
+ options[:ruby] = self.name
90
+ RubyAMF::ClassMapper.mappings.map options
91
+ end
92
+ end
93
+
94
+ # Populates the object after deserialization. By default it calls initialize,
95
+ # calls setters for keys not in attributes, and calls <tt>attributes=</tt> for
96
+ # the remaining properties if it's implemented. Override if necessary to
97
+ # support your ORM.
98
+ def rubyamf_init props, dynamic_props = nil
99
+ initialize # warhammerkid: Call initialize by default - good decision?
100
+
101
+ props.merge!(dynamic_props) if dynamic_props
102
+ if respond_to?(:attributes=)
103
+ attrs = self.attributes
104
+ rubyamf_set_non_attributes props, attrs
105
+ self.attributes = props # Populate using attributes setter
106
+ else
107
+ rubyamf_set_non_attributes props, {} # Calls setters for all props it finds setters for
108
+ end
109
+ end
110
+
111
+ # Calls setters for all keys in the given hash not found in the base attributes
112
+ # hash and deletes those keys from the hash. Performs some simple checks on
113
+ # the keys to hopefully prevent more private setters from being called.
114
+ def rubyamf_set_non_attributes attrs, base_attrs
115
+ not_attributes = attrs.keys.select {|k| !base_attrs.include?(k)}
116
+ not_attributes.each do |k|
117
+ setter = "#{k}="
118
+ next if setter !~ /^[a-z][A-Za-z0-9_]+=/ # Make sure setter doesn't start with capital, dollar, or underscore to make this safer
119
+ if respond_to?(setter)
120
+ send(setter, attrs.delete(k))
121
+ else
122
+ RubyAMF.logger.warn("RubyAMF: Cannot call setter for non-attribute on #{self.class.name}: #{k}")
123
+ end
124
+ end
125
+ end
126
+
127
+ # Like serializable_hash, rubyamf_hash returns a hash for serialization
128
+ # calculated from the given options. Supported options are <tt>:only</tt>,
129
+ # <tt>:except</tt>, <tt>:methods</tt>, and <tt>:include</tt>. This method
130
+ # is automatically called by RubyAMF::ClassMapping on serialization with
131
+ # the pre-configured options for whatever the current scope is.
132
+ def rubyamf_hash options=nil
133
+ # Process options
134
+ options ||= {}
135
+ only = Array.wrap(options[:only]).map(&:to_s)
136
+ except = Array.wrap(options[:except]).map(&:to_s)
137
+ method_names = []
138
+ Array.wrap(options[:methods]).each do |name|
139
+ method_names << name.to_s if respond_to?(name)
140
+ end
141
+
142
+ # Get list of attributes
143
+ if respond_to?(:attributes)
144
+ attrs = send(:attributes)
145
+ else
146
+ attrs = {}
147
+ ignored_props = Object.new.public_methods
148
+ (self.public_methods - ignored_props).each do |method_name|
149
+ # Add them to the attrs hash if they take no arguments
150
+ method_def = self.method(method_name)
151
+ attrs[method_name.to_s] = send(method_name) if method_def.arity == 0
152
+ end
153
+ end
154
+ attribute_names = attrs.keys.sort
155
+ if only.any?
156
+ attribute_names &= only
157
+ elsif except.any?
158
+ attribute_names -= except
159
+ end
160
+
161
+ # Build hash from attributes and methods
162
+ hash = {}
163
+ attribute_names.each {|name| hash[name] = attrs[name]}
164
+ method_names.each {|name| hash[name] = send(name)}
165
+
166
+ # Add associations using ActiveRecord::Serialization style options
167
+ # processing
168
+ if include_associations = options.delete(:include)
169
+ # Process options
170
+ base_only_or_except = {:except => options[:except], :only => options[:only]}
171
+ include_has_options = include_associations.is_a?(Hash)
172
+ associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
173
+
174
+ # Call to_amf on each object in the association, passing processed options
175
+ associations.each do |association|
176
+ records = rubyamf_retrieve_association(association)
177
+ if records
178
+ opts = include_has_options ? include_associations[association] : nil
179
+ if records.is_a?(Enumerable)
180
+ hash[association.to_s] = records.map {|r| opts.nil? ? r : r.to_amf(opts)}
181
+ else
182
+ hash[association.to_s] = opts.nil? ? records : records.to_amf(opts)
183
+ end
184
+ end
185
+ end
186
+
187
+ options[:include] = include_associations
188
+ end
189
+
190
+ hash
191
+ end
192
+
193
+ # Override if necessary to support your ORM's system of retrieving objects
194
+ # in an association.
195
+ def rubyamf_retrieve_association association
196
+ # Naive implementation that should work for most cases without
197
+ # need for overriding
198
+ send(association)
199
+ end
200
+
201
+ # Stores the given options and object in an IntermediateObject so that the
202
+ # default serialization mapping options can be overriden if necessary.
203
+ def to_amf options=nil
204
+ RubyAMF::IntermediateObject.new(self, options)
205
+ end
206
+ end
207
+ end
208
+
209
+ class Array
210
+ # Returns an array of RubyAMF::IntermediateObject objects created from calling
211
+ # <tt>to_amf</tt> on each object in the array with the given options.
212
+ def to_amf options=nil
213
+ self.map {|o| o.to_amf(options)}
214
+ end
215
+ end
@@ -0,0 +1,23 @@
1
+ module RubyAMF::Rails
2
+ # Rails controller extensions to access AMF information.
3
+ module Controller
4
+ protected
5
+ # Contains the parameters hash with named properties if mapped
6
+ attr_reader :rubyamf_params
7
+
8
+ # Contains the credentials hash from RubyAMF::Envelope#credentials
9
+ attr_reader :credentials
10
+
11
+ # Used internally by RequestProcessor
12
+ attr_reader :amf_response
13
+ # Used internally by RequestProcessor
14
+ attr_reader :mapping_scope
15
+
16
+ # Returns whether or not the request is an AMF request
17
+ def is_amf?
18
+ @is_amf == true
19
+ end
20
+ alias_method :is_amf, :is_amf?
21
+ alias_method :is_rubyamf, :is_amf?
22
+ end
23
+ end
@@ -0,0 +1,151 @@
1
+ module RubyAMF::Rails
2
+ # Rails-specific implementation of <tt>RubyAMF::Model</tt> APIs
3
+ module Model
4
+ include RubyAMF::Model
5
+
6
+ def self.included base #:nodoc:
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ def rubyamf_init props, dynamic_props = nil
11
+ # Convert props and dynamic props to hash with string keys for attributes
12
+ attrs = {}
13
+ props.each {|k,v| attrs[k.to_s] = v}
14
+ dynamic_props.each {|k,v| attrs[k.to_s] = v} unless dynamic_props.nil?
15
+
16
+ # Is it a new record or existing? - support composite primary keys just in case
17
+ is_new = true
18
+ pk = Array.wrap(self.class.primary_key).map &:to_s
19
+ if pk.length > 1 || pk[0] != 'id'
20
+ unless pk.any? {|k| empty_key?(attrs, k)}
21
+ search = pk.map {|k| attrs[k]}
22
+ search = search.first if search.length == 1
23
+ is_new = !self.class.exists?(search) # Look it up in the database to make sure because it may be a string PK (or composite PK)
24
+ end
25
+ else
26
+ is_new = false unless empty_key?(attrs, 'id')
27
+ end
28
+
29
+ # Get base attributes hash for later use
30
+ base_attrs = self.send(:attributes_from_column_definition)
31
+
32
+ if is_new
33
+ # Call initialize to populate everything for a new object
34
+ self.send(:initialize)
35
+
36
+ # Populate part of given primary key
37
+ pk.each do |k|
38
+ self.send("#{k}=", attrs[k]) unless empty_key?(attrs, k)
39
+ end
40
+ else
41
+ # Initialize with defaults so that changed properties will be marked dirty
42
+ pk_attrs = pk.inject({}) {|h, k| h[k] = attrs[k]; h}
43
+ base_attrs.merge!(pk_attrs)
44
+
45
+ if ::ActiveRecord::VERSION::MAJOR == 2
46
+ # if rails 2, use code from ActiveRecord::Base#instantiate (just copied it over)
47
+ object = self
48
+ object.instance_variable_set("@attributes", base_attrs)
49
+ object.instance_variable_set("@attributes_cache", Hash.new)
50
+
51
+ if object.respond_to_without_attributes?(:after_find)
52
+ object.send(:callback, :after_find)
53
+ end
54
+
55
+ if object.respond_to_without_attributes?(:after_initialize)
56
+ object.send(:callback, :after_initialize)
57
+ end
58
+ else
59
+ # if rails 3, use init_with('attributes' => attributes_hash)
60
+ self.init_with('attributes' => base_attrs)
61
+ end
62
+ end
63
+
64
+ # Delete pk from attrs as they have already been set
65
+ pk.each {|k| attrs.delete(k)}
66
+
67
+ # Set associations
68
+ (reflections = self.class.reflections).keys.each do |k|
69
+ if value = attrs.delete(k.to_s)
70
+ reflection_macro = reflections[k].macro
71
+ if ::ActiveRecord::VERSION::STRING < "3.1"
72
+ case reflection_macro
73
+ when :has_one
74
+ self.send("set_#{k}_target", value)
75
+ when :belongs_to
76
+ self.send("#{k}=", value)
77
+ when :has_many, :has_and_belongs_to_many
78
+ self.send("#{k}").target = value
79
+ when :composed_of
80
+ self.send("#{k}=", value) # this sets the attributes to the corresponding values
81
+ end
82
+ else
83
+ case reflection_macro
84
+ when :has_many, :has_and_belongs_to_many, :has_one
85
+ self.association(k).target = value
86
+ when :belongs_to, :composed_of
87
+ self.send("#{k}=", value) # this sets the attributes to the corresponding values
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ # Set attributes
94
+ rubyamf_set_non_attributes attrs, base_attrs
95
+ self.send(:attributes=, attrs)
96
+
97
+ self
98
+ end
99
+
100
+ def rubyamf_hash options=nil
101
+ return super(options) unless RubyAMF.configuration.check_for_associations
102
+
103
+ options ||= {}
104
+
105
+ # Iterate through assocations and check to see if they are loaded
106
+ auto_include = []
107
+ self.class.reflect_on_all_associations.each do |reflection|
108
+ next if reflection.macro == :belongs_to # Skip belongs_to to prevent recursion
109
+ is_loaded = if self.respond_to?(:association)
110
+ # Rails 3.1
111
+ self.association(reflection.name).loaded?
112
+ elsif self.respond_to?("loaded_#{reflection.name}?")
113
+ # Rails 2.3 and 3.0 for some types
114
+ self.send("loaded_#{reflection.name}?")
115
+ else
116
+ # Rails 2.3 and 3.0 for some types
117
+ self.send(reflection.name).loaded?
118
+ end
119
+ auto_include << reflection.name if is_loaded
120
+ end
121
+
122
+ # Add these assocations to the :include if they are not already there
123
+ if include_associations = options.delete(:include)
124
+ if include_associations.is_a?(Hash)
125
+ auto_include.each {|assoc| include_associations[assoc] ||= {}}
126
+ else
127
+ include_associations = Array.wrap(include_associations) | auto_include
128
+ end
129
+ options[:include] = include_associations
130
+ else
131
+ options[:include] = auto_include if auto_include.length > 0
132
+ end
133
+
134
+ super(options)
135
+ end
136
+
137
+ def rubyamf_retrieve_association association
138
+ case self.class.reflect_on_association(association).macro
139
+ when :has_many, :has_and_belongs_to_many
140
+ send(association).to_a
141
+ when :has_one, :belongs_to
142
+ send(association)
143
+ end
144
+ end
145
+
146
+ def empty_key? attrs, key
147
+ return true unless attrs.key?(key)
148
+ attrs[key] == 0 || attrs[key].nil?
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,73 @@
1
+ require 'rubyamf/rails/controller'
2
+ require 'rubyamf/rails/model'
3
+ require 'rubyamf/rails/request_processor'
4
+ require 'rubyamf/rails/routing'
5
+ require 'rubyamf/rails/time'
6
+ require 'action_controller'
7
+
8
+ # Hook up MIME type
9
+ Mime::Type.register RubyAMF::MIME_TYPE, :amf
10
+
11
+ # Hook routing into routes
12
+ ActionController::Routing::RouteSet::Mapper.send(:include, RubyAMF::Rails::Routing)
13
+
14
+ # Add some utility methods to ActionController
15
+ ActionController::Base.send(:include, RubyAMF::Rails::Controller)
16
+
17
+ # Hook up ActiveRecord Model extensions
18
+ if defined?(ActiveRecord)
19
+ ActiveRecord::Base.send(:include, RubyAMF::Rails::Model)
20
+ end
21
+
22
+ # Hook up rendering and hack in our custom error handling
23
+ module ActionController #:nodoc:
24
+ class Base #:nodoc:
25
+ def render_with_amf(options = nil, extra_options ={}, &block)
26
+ if options && options.is_a?(Hash) && options.has_key?(:amf)
27
+ @performed_render = true
28
+ @amf_response = options[:amf]
29
+ @mapping_scope = options[:class_mapping_scope] || options[:mapping_scope] || nil
30
+ else
31
+ render_without_amf(options, extra_options, &block)
32
+ end
33
+ end
34
+ alias_method_chain :render, :amf
35
+ end
36
+
37
+ module Rescue #:nodoc:
38
+ protected
39
+ # Re-raise the exception so that RubyAMF gets it if it's an AMF call. Otherwise
40
+ # RubyAMF doesn't know that the call failed and a "success" response is sent.
41
+ def rescue_action_with_amf(exception)
42
+ raise exception if respond_to?(:is_amf?) && is_amf?
43
+ rescue_action_without_amf exception
44
+ end
45
+ alias_method_chain :rescue_action, :amf
46
+ end
47
+ end
48
+
49
+ # Add middleware
50
+ add_middleware = Proc.new {
51
+ m = Rails.configuration.middleware
52
+ m.use RubyAMF::RequestParser
53
+ m.use RubyAMF::Rails::RequestProcessor
54
+ }
55
+ if Rails.initialized?
56
+ add_middleware.call
57
+ else
58
+ Rails.configuration.after_initialize &add_middleware
59
+ end
60
+
61
+ module RubyAMF
62
+ def self.configure
63
+ # Load legacy config if they have one
64
+ begin
65
+ RubyAMF.configuration.load_legacy
66
+ rescue
67
+ RubyAMF.logger.info "RubyAMF: Could not find legacy config file to load"
68
+ end
69
+
70
+ yield configuration
71
+ bootstrap
72
+ end
73
+ end