activemodel 3.0.pre → 3.0.0.rc
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +44 -1
- data/MIT-LICENSE +1 -1
- data/README.rdoc +184 -0
- data/lib/active_model.rb +29 -19
- data/lib/active_model/attribute_methods.rb +167 -46
- data/lib/active_model/callbacks.rb +134 -0
- data/lib/active_model/conversion.rb +41 -1
- data/lib/active_model/deprecated_error_methods.rb +1 -1
- data/lib/active_model/dirty.rb +56 -12
- data/lib/active_model/errors.rb +205 -46
- data/lib/active_model/lint.rb +53 -17
- data/lib/active_model/locale/en.yml +26 -23
- data/lib/active_model/mass_assignment_security.rb +160 -0
- data/lib/active_model/mass_assignment_security/permission_set.rb +40 -0
- data/lib/active_model/mass_assignment_security/sanitizer.rb +23 -0
- data/lib/active_model/naming.rb +70 -5
- data/lib/active_model/observing.rb +40 -16
- data/lib/active_model/railtie.rb +2 -0
- data/lib/active_model/serialization.rb +59 -0
- data/lib/active_model/serializers/json.rb +17 -11
- data/lib/active_model/serializers/xml.rb +66 -123
- data/lib/active_model/test_case.rb +0 -2
- data/lib/active_model/translation.rb +64 -0
- data/lib/active_model/validations.rb +150 -68
- data/lib/active_model/validations/acceptance.rb +53 -33
- data/lib/active_model/validations/callbacks.rb +57 -0
- data/lib/active_model/validations/confirmation.rb +41 -23
- data/lib/active_model/validations/exclusion.rb +18 -13
- data/lib/active_model/validations/format.rb +28 -24
- data/lib/active_model/validations/inclusion.rb +18 -13
- data/lib/active_model/validations/length.rb +67 -65
- data/lib/active_model/validations/numericality.rb +83 -58
- data/lib/active_model/validations/presence.rb +10 -8
- data/lib/active_model/validations/validates.rb +110 -0
- data/lib/active_model/validations/with.rb +90 -23
- data/lib/active_model/validator.rb +186 -0
- data/lib/active_model/version.rb +3 -2
- metadata +79 -20
- data/README +0 -21
- data/lib/active_model/state_machine.rb +0 -70
- data/lib/active_model/state_machine/event.rb +0 -62
- data/lib/active_model/state_machine/machine.rb +0 -75
- data/lib/active_model/state_machine/state.rb +0 -47
- data/lib/active_model/state_machine/state_transition.rb +0 -40
- data/lib/active_model/validations_repair_helper.rb +0 -35
@@ -1,18 +1,16 @@
|
|
1
|
-
require 'observer'
|
2
1
|
require 'singleton'
|
3
2
|
require 'active_support/core_ext/array/wrap'
|
4
3
|
require 'active_support/core_ext/module/aliasing'
|
4
|
+
require 'active_support/core_ext/module/remove_method'
|
5
5
|
require 'active_support/core_ext/string/inflections'
|
6
6
|
|
7
7
|
module ActiveModel
|
8
8
|
module Observing
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
|
-
included do
|
12
|
-
extend Observable
|
13
|
-
end
|
14
|
-
|
15
11
|
module ClassMethods
|
12
|
+
# == Active Model Observers Activation
|
13
|
+
#
|
16
14
|
# Activates the observers assigned. Examples:
|
17
15
|
#
|
18
16
|
# # Calls PersonObserver.instance
|
@@ -24,8 +22,9 @@ module ActiveModel
|
|
24
22
|
# # Same as above, just using explicit class references
|
25
23
|
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
26
24
|
#
|
27
|
-
# Note: Setting this does not instantiate the observers yet.
|
28
|
-
# called during startup, and before
|
25
|
+
# Note: Setting this does not instantiate the observers yet.
|
26
|
+
# +instantiate_observers+ is called during startup, and before
|
27
|
+
# each development request.
|
29
28
|
def observers=(*values)
|
30
29
|
@observers = values.flatten
|
31
30
|
end
|
@@ -40,6 +39,26 @@ module ActiveModel
|
|
40
39
|
observers.each { |o| instantiate_observer(o) }
|
41
40
|
end
|
42
41
|
|
42
|
+
def add_observer(observer)
|
43
|
+
unless observer.respond_to? :update
|
44
|
+
raise ArgumentError, "observer needs to respond to `update'"
|
45
|
+
end
|
46
|
+
@observer_instances ||= []
|
47
|
+
@observer_instances << observer
|
48
|
+
end
|
49
|
+
|
50
|
+
def notify_observers(*arg)
|
51
|
+
if defined? @observer_instances
|
52
|
+
for observer in @observer_instances
|
53
|
+
observer.update(*arg)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def count_observers
|
59
|
+
@observer_instances.size
|
60
|
+
end
|
61
|
+
|
43
62
|
protected
|
44
63
|
def instantiate_observer(observer) #:nodoc:
|
45
64
|
# string/symbol
|
@@ -55,7 +74,6 @@ module ActiveModel
|
|
55
74
|
# Notify observers when the observed class is subclassed.
|
56
75
|
def inherited(subclass)
|
57
76
|
super
|
58
|
-
changed
|
59
77
|
notify_observers :observed_class_inherited, subclass
|
60
78
|
end
|
61
79
|
end
|
@@ -69,11 +87,12 @@ module ActiveModel
|
|
69
87
|
# notify_observers(:after_save)
|
70
88
|
# end
|
71
89
|
def notify_observers(method)
|
72
|
-
self.class.changed
|
73
90
|
self.class.notify_observers(method, self)
|
74
91
|
end
|
75
92
|
end
|
76
93
|
|
94
|
+
# == Active Model Observers
|
95
|
+
#
|
77
96
|
# Observer classes respond to lifecycle callbacks to implement trigger-like
|
78
97
|
# behavior outside the original class. This is a great way to reduce the
|
79
98
|
# clutter that normally comes when the model class is burdened with
|
@@ -102,10 +121,12 @@ module ActiveModel
|
|
102
121
|
#
|
103
122
|
# == Observing a class that can't be inferred
|
104
123
|
#
|
105
|
-
# Observers will by default be mapped to the class with which they share a
|
106
|
-
# be tied to observing Comment, ProductManagerObserver
|
107
|
-
#
|
108
|
-
#
|
124
|
+
# Observers will by default be mapped to the class with which they share a
|
125
|
+
# name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
|
126
|
+
# to ProductManager, and so on. If you want to name your observer differently than
|
127
|
+
# the class you're interested in observing, you can use the Observer.observe class
|
128
|
+
# method which takes either the concrete class (Product) or a symbol for that
|
129
|
+
# class (:product):
|
109
130
|
#
|
110
131
|
# class AuditObserver < ActiveModel::Observer
|
111
132
|
# observe :account
|
@@ -115,7 +136,8 @@ module ActiveModel
|
|
115
136
|
# end
|
116
137
|
# end
|
117
138
|
#
|
118
|
-
# If the audit observer needs to watch more than one kind of object, this can be
|
139
|
+
# If the audit observer needs to watch more than one kind of object, this can be
|
140
|
+
# specified with multiple arguments:
|
119
141
|
#
|
120
142
|
# class AuditObserver < ActiveModel::Observer
|
121
143
|
# observe :account, :balance
|
@@ -125,7 +147,8 @@ module ActiveModel
|
|
125
147
|
# end
|
126
148
|
# end
|
127
149
|
#
|
128
|
-
# The AuditObserver will now act on both updates to Account and Balance by treating
|
150
|
+
# The AuditObserver will now act on both updates to Account and Balance by treating
|
151
|
+
# them both as records.
|
129
152
|
#
|
130
153
|
class Observer
|
131
154
|
include Singleton
|
@@ -135,6 +158,7 @@ module ActiveModel
|
|
135
158
|
def observe(*models)
|
136
159
|
models.flatten!
|
137
160
|
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
|
161
|
+
remove_possible_method(:observed_classes)
|
138
162
|
define_method(:observed_classes) { models }
|
139
163
|
end
|
140
164
|
|
@@ -144,7 +168,7 @@ module ActiveModel
|
|
144
168
|
#
|
145
169
|
# class AuditObserver < ActiveModel::Observer
|
146
170
|
# def self.observed_classes
|
147
|
-
# [
|
171
|
+
# [Account, Balance]
|
148
172
|
# end
|
149
173
|
# end
|
150
174
|
def observed_classes
|
@@ -2,6 +2,65 @@ require 'active_support/core_ext/hash/except'
|
|
2
2
|
require 'active_support/core_ext/hash/slice'
|
3
3
|
|
4
4
|
module ActiveModel
|
5
|
+
# == Active Model Serialization
|
6
|
+
#
|
7
|
+
# Provides a basic serialization to a serializable_hash for your object.
|
8
|
+
#
|
9
|
+
# A minimal implementation could be:
|
10
|
+
#
|
11
|
+
# class Person
|
12
|
+
#
|
13
|
+
# include ActiveModel::Serialization
|
14
|
+
#
|
15
|
+
# attr_accessor :name
|
16
|
+
#
|
17
|
+
# def attributes
|
18
|
+
# @attributes ||= {'name' => 'nil'}
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Which would provide you with:
|
24
|
+
#
|
25
|
+
# person = Person.new
|
26
|
+
# person.serializable_hash # => {"name"=>nil}
|
27
|
+
# person.name = "Bob"
|
28
|
+
# person.serializable_hash # => {"name"=>"Bob"}
|
29
|
+
#
|
30
|
+
# You need to declare some sort of attributes hash which contains the attributes
|
31
|
+
# you want to serialize and their current value.
|
32
|
+
#
|
33
|
+
# Most of the time though, you will want to include the JSON or XML
|
34
|
+
# serializations. Both of these modules automatically include the
|
35
|
+
# ActiveModel::Serialization module, so there is no need to explicitly
|
36
|
+
# include it.
|
37
|
+
#
|
38
|
+
# So a minimal implementation including XML and JSON would be:
|
39
|
+
#
|
40
|
+
# class Person
|
41
|
+
#
|
42
|
+
# include ActiveModel::Serializers::JSON
|
43
|
+
# include ActiveModel::Serializers::Xml
|
44
|
+
#
|
45
|
+
# attr_accessor :name
|
46
|
+
#
|
47
|
+
# def attributes
|
48
|
+
# @attributes ||= {'name' => 'nil'}
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Which would provide you with:
|
54
|
+
#
|
55
|
+
# person = Person.new
|
56
|
+
# person.serializable_hash # => {"name"=>nil}
|
57
|
+
# person.to_json # => "{\"name\":null}"
|
58
|
+
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
59
|
+
#
|
60
|
+
# person.name = "Bob"
|
61
|
+
# person.serializable_hash # => {"name"=>"Bob"}
|
62
|
+
# person.to_json # => "{\"name\":\"Bob\"}"
|
63
|
+
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
5
64
|
module Serialization
|
6
65
|
def serializable_hash(options = nil)
|
7
66
|
options ||= {}
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'active_support/json'
|
2
|
-
require 'active_support/core_ext/class/
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
3
|
|
4
4
|
module ActiveModel
|
5
|
+
# == Active Model JSON Serializer
|
5
6
|
module Serializers
|
6
7
|
module JSON
|
7
8
|
extend ActiveSupport::Concern
|
@@ -10,19 +11,18 @@ module ActiveModel
|
|
10
11
|
included do
|
11
12
|
extend ActiveModel::Naming
|
12
13
|
|
13
|
-
|
14
|
+
class_attribute :include_root_in_json
|
15
|
+
self.include_root_in_json = true
|
14
16
|
end
|
15
17
|
|
16
|
-
# Returns a JSON string representing the model. Some configuration
|
17
|
-
#
|
18
|
+
# Returns a JSON string representing the model. Some configuration can be
|
19
|
+
# passed through +options+.
|
18
20
|
#
|
19
|
-
# The option <tt>
|
20
|
-
# top-level behavior of to_json
|
21
|
-
# <tt>
|
22
|
-
# to_json will emit a single root node named after the object's type. For example:
|
21
|
+
# The option <tt>ActiveModel::Base.include_root_in_json</tt> controls the
|
22
|
+
# top-level behavior of <tt>to_json</tt>. It is <tt>true</tt> by default. When it is <tt>true</tt>,
|
23
|
+
# <tt>to_json</tt> will emit a single root node named after the object's type. For example:
|
23
24
|
#
|
24
25
|
# konata = User.find(1)
|
25
|
-
# ActiveRecord::Base.include_root_in_json = true
|
26
26
|
# konata.to_json
|
27
27
|
# # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
|
28
28
|
# "created_at": "2006/08/01", "awesome": true} }
|
@@ -81,7 +81,11 @@ module ActiveModel
|
|
81
81
|
# "title": "So I was thinking"}]}
|
82
82
|
def encode_json(encoder)
|
83
83
|
hash = serializable_hash(encoder.options)
|
84
|
-
|
84
|
+
if include_root_in_json
|
85
|
+
custom_root = encoder.options && encoder.options[:root]
|
86
|
+
hash = { custom_root || self.class.model_name.element => hash }
|
87
|
+
end
|
88
|
+
|
85
89
|
ActiveSupport::JSON.encode(hash)
|
86
90
|
end
|
87
91
|
|
@@ -90,7 +94,9 @@ module ActiveModel
|
|
90
94
|
end
|
91
95
|
|
92
96
|
def from_json(json)
|
93
|
-
|
97
|
+
hash = ActiveSupport::JSON.decode(json)
|
98
|
+
hash = hash.values.first if include_root_in_json
|
99
|
+
self.attributes = hash
|
94
100
|
self
|
95
101
|
end
|
96
102
|
end
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
1
2
|
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
+
require 'active_support/core_ext/array/conversions'
|
2
4
|
require 'active_support/core_ext/hash/conversions'
|
5
|
+
require 'active_support/core_ext/hash/slice'
|
3
6
|
|
4
7
|
module ActiveModel
|
8
|
+
# == Active Model XML Serializer
|
5
9
|
module Serializers
|
6
10
|
module Xml
|
7
11
|
extend ActiveSupport::Concern
|
@@ -11,68 +15,31 @@ module ActiveModel
|
|
11
15
|
class Attribute #:nodoc:
|
12
16
|
attr_reader :name, :value, :type
|
13
17
|
|
14
|
-
def initialize(name, serializable)
|
18
|
+
def initialize(name, serializable, raw_value=nil)
|
15
19
|
@name, @serializable = name, serializable
|
20
|
+
@value = value || @serializable.send(name)
|
16
21
|
@type = compute_type
|
17
|
-
@value = compute_value
|
18
22
|
end
|
19
23
|
|
20
|
-
|
21
|
-
# does not need to be escaped, as <tt>tag!</tt> escapes all values
|
22
|
-
# to ensure that valid XML is generated. For known binary
|
23
|
-
# values, it is at least an order of magnitude faster to
|
24
|
-
# Base64 encode binary values and directly put them in the
|
25
|
-
# output XML than to pass the original value or the Base64
|
26
|
-
# encoded value to the <tt>tag!</tt> method. It definitely makes
|
27
|
-
# no sense to Base64 encode the value and then give it to
|
28
|
-
# <tt>tag!</tt>, since that just adds additional overhead.
|
29
|
-
def needs_encoding?
|
30
|
-
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
|
31
|
-
end
|
32
|
-
|
33
|
-
def decorations(include_types = true)
|
24
|
+
def decorations
|
34
25
|
decorations = {}
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
if include_types && type != :string
|
41
|
-
decorations[:type] = type
|
42
|
-
end
|
43
|
-
|
44
|
-
if value.nil?
|
45
|
-
decorations[:nil] = true
|
46
|
-
end
|
47
|
-
|
26
|
+
decorations[:encoding] = 'base64' if type == :binary
|
27
|
+
decorations[:type] = type unless type == :string
|
28
|
+
decorations[:nil] = true if value.nil?
|
48
29
|
decorations
|
49
30
|
end
|
50
31
|
|
51
|
-
|
52
|
-
def compute_type
|
53
|
-
value = @serializable.send(name)
|
54
|
-
type = Hash::XML_TYPE_NAMES[value.class.name]
|
55
|
-
type ||= :string if value.respond_to?(:to_str)
|
56
|
-
type ||= :yaml
|
57
|
-
type
|
58
|
-
end
|
59
|
-
|
60
|
-
def compute_value
|
61
|
-
value = @serializable.send(name)
|
32
|
+
protected
|
62
33
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
34
|
+
def compute_type
|
35
|
+
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
|
36
|
+
type ||= :string if value.respond_to?(:to_str)
|
37
|
+
type ||= :yaml
|
38
|
+
type
|
39
|
+
end
|
69
40
|
end
|
70
41
|
|
71
42
|
class MethodAttribute < Attribute #:nodoc:
|
72
|
-
protected
|
73
|
-
def compute_type
|
74
|
-
Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string
|
75
|
-
end
|
76
43
|
end
|
77
44
|
|
78
45
|
attr_reader :options
|
@@ -85,112 +52,88 @@ module ActiveModel
|
|
85
52
|
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
|
86
53
|
end
|
87
54
|
|
88
|
-
# To replicate the behavior in ActiveRecord#attributes,
|
89
|
-
#
|
55
|
+
# To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
|
56
|
+
# takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
|
90
57
|
# for a N level model but is set for the N+1 level models,
|
91
58
|
# then because <tt>:except</tt> is set to a default value, the second
|
92
59
|
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
|
93
60
|
# <tt>:only</tt> is set, always delete <tt>:except</tt>.
|
94
|
-
def
|
95
|
-
|
96
|
-
|
61
|
+
def attributes_hash
|
62
|
+
attributes = @serializable.attributes
|
97
63
|
if options[:only].any?
|
98
|
-
|
64
|
+
attributes.slice(*options[:only])
|
99
65
|
elsif options[:except].any?
|
100
|
-
|
66
|
+
attributes.except(*options[:except])
|
67
|
+
else
|
68
|
+
attributes
|
101
69
|
end
|
102
|
-
|
103
|
-
attribute_names
|
104
70
|
end
|
105
71
|
|
106
72
|
def serializable_attributes
|
107
|
-
|
73
|
+
attributes_hash.map do |name, value|
|
74
|
+
self.class::Attribute.new(name, @serializable, value)
|
75
|
+
end
|
108
76
|
end
|
109
77
|
|
110
|
-
def
|
111
|
-
Array(options[:methods]).inject([]) do |methods, name|
|
112
|
-
methods << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
|
78
|
+
def serializable_methods
|
79
|
+
Array.wrap(options[:methods]).inject([]) do |methods, name|
|
80
|
+
methods << self.class::MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
|
113
81
|
methods
|
114
82
|
end
|
115
83
|
end
|
116
84
|
|
117
85
|
def serialize
|
118
|
-
|
119
|
-
|
120
|
-
if options[:namespace]
|
121
|
-
args << {:xmlns => options[:namespace]}
|
122
|
-
end
|
86
|
+
require 'builder' unless defined? ::Builder
|
123
87
|
|
124
|
-
|
125
|
-
|
126
|
-
end
|
88
|
+
options[:indent] ||= 2
|
89
|
+
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
|
127
90
|
|
128
|
-
builder
|
129
|
-
|
130
|
-
procs = options.delete(:procs)
|
131
|
-
options[:procs] = procs
|
132
|
-
add_procs
|
133
|
-
yield builder if block_given?
|
134
|
-
end
|
135
|
-
end
|
91
|
+
@builder = options[:builder]
|
92
|
+
@builder.instruct! unless options[:skip_instruct]
|
136
93
|
|
137
|
-
|
138
|
-
|
139
|
-
@builder ||= begin
|
140
|
-
require 'builder' unless defined? ::Builder
|
141
|
-
options[:indent] ||= 2
|
142
|
-
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
|
94
|
+
root = (options[:root] || @serializable.class.model_name.element).to_s
|
95
|
+
root = ActiveSupport::XmlMini.rename_key(root, options)
|
143
96
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end
|
148
|
-
|
149
|
-
builder
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def root
|
154
|
-
root = (options[:root] || @serializable.class.model_name.singular).to_s
|
155
|
-
reformat_name(root)
|
156
|
-
end
|
97
|
+
args = [root]
|
98
|
+
args << {:xmlns => options[:namespace]} if options[:namespace]
|
99
|
+
args << {:type => options[:type]} if options[:type] && !options[:skip_types]
|
157
100
|
|
158
|
-
|
159
|
-
|
101
|
+
@builder.tag!(*args) do
|
102
|
+
add_attributes_and_methods
|
103
|
+
add_extra_behavior
|
104
|
+
add_procs
|
105
|
+
yield @builder if block_given?
|
160
106
|
end
|
107
|
+
end
|
161
108
|
|
162
|
-
|
163
|
-
options.has_key?(:camelize) && options[:camelize]
|
164
|
-
end
|
109
|
+
private
|
165
110
|
|
166
|
-
|
167
|
-
|
168
|
-
dasherize? ? name.dasherize : name
|
169
|
-
end
|
111
|
+
def add_extra_behavior
|
112
|
+
end
|
170
113
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
attribute.decorations(!options[:skip_types])
|
177
|
-
)
|
178
|
-
end
|
114
|
+
def add_attributes_and_methods
|
115
|
+
(serializable_attributes + serializable_methods).each do |attribute|
|
116
|
+
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
|
117
|
+
ActiveSupport::XmlMini.to_tag(key, attribute.value,
|
118
|
+
options.merge(attribute.decorations))
|
179
119
|
end
|
120
|
+
end
|
180
121
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
end
|
122
|
+
def add_procs
|
123
|
+
if procs = options.delete(:procs)
|
124
|
+
Array.wrap(procs).each do |proc|
|
125
|
+
if proc.arity == 1
|
126
|
+
proc.call(options)
|
127
|
+
else
|
128
|
+
proc.call(options, @serializable)
|
189
129
|
end
|
190
130
|
end
|
191
131
|
end
|
132
|
+
end
|
192
133
|
end
|
193
134
|
|
135
|
+
# Returns XML representing the model. Configuration can be
|
136
|
+
# passed through +options+.
|
194
137
|
def to_xml(options = {}, &block)
|
195
138
|
Serializer.new(self, options).serialize(&block)
|
196
139
|
end
|