activemodel 3.0.0.beta4 → 3.0.pre
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.
- data/CHANGELOG +1 -39
- data/MIT-LICENSE +1 -1
- data/README +16 -200
- data/lib/active_model.rb +19 -28
- data/lib/active_model/attribute_methods.rb +27 -142
- data/lib/active_model/conversion.rb +1 -37
- data/lib/active_model/dirty.rb +12 -51
- data/lib/active_model/errors.rb +22 -146
- data/lib/active_model/lint.rb +14 -48
- data/lib/active_model/locale/en.yml +23 -26
- data/lib/active_model/naming.rb +5 -41
- data/lib/active_model/observing.rb +16 -35
- data/lib/active_model/serialization.rb +0 -57
- data/lib/active_model/serializers/json.rb +8 -13
- data/lib/active_model/serializers/xml.rb +123 -63
- data/lib/active_model/state_machine.rb +70 -0
- data/lib/active_model/state_machine/event.rb +62 -0
- data/lib/active_model/state_machine/machine.rb +75 -0
- data/lib/active_model/state_machine/state.rb +47 -0
- data/lib/active_model/state_machine/state_transition.rb +40 -0
- data/lib/active_model/test_case.rb +2 -0
- data/lib/active_model/validations.rb +62 -125
- data/lib/active_model/validations/acceptance.rb +18 -23
- data/lib/active_model/validations/confirmation.rb +10 -14
- data/lib/active_model/validations/exclusion.rb +13 -15
- data/lib/active_model/validations/format.rb +24 -26
- data/lib/active_model/validations/inclusion.rb +13 -15
- data/lib/active_model/validations/length.rb +65 -61
- data/lib/active_model/validations/numericality.rb +58 -76
- data/lib/active_model/validations/presence.rb +8 -8
- data/lib/active_model/validations/with.rb +22 -90
- data/lib/active_model/validations_repair_helper.rb +35 -0
- data/lib/active_model/version.rb +2 -3
- metadata +19 -63
- data/lib/active_model/callbacks.rb +0 -134
- data/lib/active_model/railtie.rb +0 -2
- data/lib/active_model/translation.rb +0 -60
- data/lib/active_model/validations/validates.rb +0 -108
- data/lib/active_model/validator.rb +0 -183
@@ -1,27 +1,24 @@
|
|
1
1
|
en:
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
less_than_or_equal_to: "must be less than or equal to %{count}"
|
26
|
-
odd: "must be odd"
|
27
|
-
even: "must be even"
|
2
|
+
activemodel:
|
3
|
+
errors:
|
4
|
+
# The values :model, :attribute and :value are always available for interpolation
|
5
|
+
# The value :count is available when applicable. Can be used for pluralization.
|
6
|
+
messages:
|
7
|
+
inclusion: "is not included in the list"
|
8
|
+
exclusion: "is reserved"
|
9
|
+
invalid: "is invalid"
|
10
|
+
confirmation: "doesn't match confirmation"
|
11
|
+
accepted: "must be accepted"
|
12
|
+
empty: "can't be empty"
|
13
|
+
blank: "can't be blank"
|
14
|
+
too_long: "is too long (maximum is {{count}} characters)"
|
15
|
+
too_short: "is too short (minimum is {{count}} characters)"
|
16
|
+
wrong_length: "is the wrong length (should be {{count}} characters)"
|
17
|
+
not_a_number: "is not a number"
|
18
|
+
greater_than: "must be greater than {{count}}"
|
19
|
+
greater_than_or_equal_to: "must be greater than or equal to {{count}}"
|
20
|
+
equal_to: "must be equal to {{count}}"
|
21
|
+
less_than: "must be less than {{count}}"
|
22
|
+
less_than_or_equal_to: "must be less than or equal to {{count}}"
|
23
|
+
odd: "must be odd"
|
24
|
+
even: "must be even"
|
data/lib/active_model/naming.rb
CHANGED
@@ -1,62 +1,26 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
3
|
module ActiveModel
|
4
|
-
|
5
4
|
class Name < String
|
6
|
-
attr_reader :singular, :plural, :element, :collection, :partial_path
|
5
|
+
attr_reader :singular, :plural, :element, :collection, :partial_path, :human
|
7
6
|
alias_method :cache_key, :collection
|
8
7
|
|
9
|
-
def initialize(
|
10
|
-
super
|
11
|
-
@klass = klass
|
8
|
+
def initialize(name)
|
9
|
+
super
|
12
10
|
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
|
13
11
|
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
|
14
12
|
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
|
15
|
-
@human =
|
13
|
+
@human = @element.gsub(/_/, " ")
|
16
14
|
@collection = ActiveSupport::Inflector.tableize(self).freeze
|
17
15
|
@partial_path = "#{@collection}/#{@element}".freeze
|
18
16
|
end
|
19
|
-
|
20
|
-
# Transform the model name into a more humane format, using I18n. By default,
|
21
|
-
# it will underscore then humanize the class name (BlogPost.model_name.human #=> "Blog post").
|
22
|
-
# Specify +options+ with additional translating options.
|
23
|
-
def human(options={})
|
24
|
-
return @human unless @klass.respond_to?(:lookup_ancestors) &&
|
25
|
-
@klass.respond_to?(:i18n_scope)
|
26
|
-
|
27
|
-
defaults = @klass.lookup_ancestors.map do |klass|
|
28
|
-
klass.model_name.underscore.to_sym
|
29
|
-
end
|
30
|
-
|
31
|
-
defaults << options.delete(:default) if options[:default]
|
32
|
-
defaults << @human
|
33
|
-
|
34
|
-
options.reverse_merge! :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults
|
35
|
-
I18n.translate(defaults.shift, options)
|
36
|
-
end
|
37
17
|
end
|
38
18
|
|
39
|
-
# ActiveModel::Naming is a module that creates a +model_name+ method on your
|
40
|
-
# object.
|
41
|
-
#
|
42
|
-
# To implement, just extend ActiveModel::Naming in your object:
|
43
|
-
#
|
44
|
-
# class BookCover
|
45
|
-
# extend ActiveModel::Naming
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# BookCover.model_name #=> "BookCover"
|
49
|
-
# BookCover.model_name.human #=> "Book cover"
|
50
|
-
#
|
51
|
-
# Providing the functionality that ActiveModel::Naming provides in your object
|
52
|
-
# is required to pass the ActiveModel Lint test. So either extending the provided
|
53
|
-
# method below, or rolling your own is required..
|
54
19
|
module Naming
|
55
20
|
# Returns an ActiveModel::Name object for module. It can be
|
56
21
|
# used to retrieve all kinds of naming-related information.
|
57
22
|
def model_name
|
58
|
-
@_model_name ||= ActiveModel::Name.new(
|
23
|
+
@_model_name ||= ActiveModel::Name.new(name)
|
59
24
|
end
|
60
25
|
end
|
61
|
-
|
62
26
|
end
|
@@ -1,13 +1,17 @@
|
|
1
|
+
require 'observer'
|
1
2
|
require 'singleton'
|
2
3
|
require 'active_support/core_ext/array/wrap'
|
3
4
|
require 'active_support/core_ext/module/aliasing'
|
4
5
|
require 'active_support/core_ext/string/inflections'
|
5
|
-
require 'active_support/core_ext/string/conversions'
|
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
|
+
|
11
15
|
module ClassMethods
|
12
16
|
# Activates the observers assigned. Examples:
|
13
17
|
#
|
@@ -20,9 +24,8 @@ module ActiveModel
|
|
20
24
|
# # Same as above, just using explicit class references
|
21
25
|
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
22
26
|
#
|
23
|
-
# Note: Setting this does not instantiate the observers yet.
|
24
|
-
#
|
25
|
-
# each development request.
|
27
|
+
# Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
|
28
|
+
# called during startup, and before each development request.
|
26
29
|
def observers=(*values)
|
27
30
|
@observers = values.flatten
|
28
31
|
end
|
@@ -37,26 +40,6 @@ module ActiveModel
|
|
37
40
|
observers.each { |o| instantiate_observer(o) }
|
38
41
|
end
|
39
42
|
|
40
|
-
def add_observer(observer)
|
41
|
-
unless observer.respond_to? :update
|
42
|
-
raise ArgumentError, "observer needs to respond to `update'"
|
43
|
-
end
|
44
|
-
@observer_instances ||= []
|
45
|
-
@observer_instances << observer
|
46
|
-
end
|
47
|
-
|
48
|
-
def notify_observers(*arg)
|
49
|
-
if defined? @observer_instances
|
50
|
-
for observer in @observer_instances
|
51
|
-
observer.update(*arg)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def count_observers
|
57
|
-
@observer_instances.size
|
58
|
-
end
|
59
|
-
|
60
43
|
protected
|
61
44
|
def instantiate_observer(observer) #:nodoc:
|
62
45
|
# string/symbol
|
@@ -72,6 +55,7 @@ module ActiveModel
|
|
72
55
|
# Notify observers when the observed class is subclassed.
|
73
56
|
def inherited(subclass)
|
74
57
|
super
|
58
|
+
changed
|
75
59
|
notify_observers :observed_class_inherited, subclass
|
76
60
|
end
|
77
61
|
end
|
@@ -85,6 +69,7 @@ module ActiveModel
|
|
85
69
|
# notify_observers(:after_save)
|
86
70
|
# end
|
87
71
|
def notify_observers(method)
|
72
|
+
self.class.changed
|
88
73
|
self.class.notify_observers(method, self)
|
89
74
|
end
|
90
75
|
end
|
@@ -117,12 +102,10 @@ module ActiveModel
|
|
117
102
|
#
|
118
103
|
# == Observing a class that can't be inferred
|
119
104
|
#
|
120
|
-
# Observers will by default be mapped to the class with which they share a
|
121
|
-
#
|
122
|
-
#
|
123
|
-
# the class
|
124
|
-
# method which takes either the concrete class (Product) or a symbol for that
|
125
|
-
# class (:product):
|
105
|
+
# Observers will by default be mapped to the class with which they share a name. So CommentObserver will
|
106
|
+
# be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
|
107
|
+
# differently than the class you're interested in observing, you can use the Observer.observe class method which takes
|
108
|
+
# either the concrete class (Product) or a symbol for that class (:product):
|
126
109
|
#
|
127
110
|
# class AuditObserver < ActiveModel::Observer
|
128
111
|
# observe :account
|
@@ -132,8 +115,7 @@ module ActiveModel
|
|
132
115
|
# end
|
133
116
|
# end
|
134
117
|
#
|
135
|
-
# If the audit observer needs to watch more than one kind of object, this can be
|
136
|
-
# specified with multiple arguments:
|
118
|
+
# If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
|
137
119
|
#
|
138
120
|
# class AuditObserver < ActiveModel::Observer
|
139
121
|
# observe :account, :balance
|
@@ -143,8 +125,7 @@ module ActiveModel
|
|
143
125
|
# end
|
144
126
|
# end
|
145
127
|
#
|
146
|
-
# The AuditObserver will now act on both updates to Account and Balance by treating
|
147
|
-
# them both as records.
|
128
|
+
# The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
|
148
129
|
#
|
149
130
|
class Observer
|
150
131
|
include Singleton
|
@@ -163,7 +144,7 @@ module ActiveModel
|
|
163
144
|
#
|
164
145
|
# class AuditObserver < ActiveModel::Observer
|
165
146
|
# def self.observed_classes
|
166
|
-
# [
|
147
|
+
# [AccountObserver, BalanceObserver]
|
167
148
|
# end
|
168
149
|
# end
|
169
150
|
def observed_classes
|
@@ -2,63 +2,6 @@ require 'active_support/core_ext/hash/except'
|
|
2
2
|
require 'active_support/core_ext/hash/slice'
|
3
3
|
|
4
4
|
module ActiveModel
|
5
|
-
# Provides a basic serialization to a serializable_hash for your object.
|
6
|
-
#
|
7
|
-
# A minimal implementation could be:
|
8
|
-
#
|
9
|
-
# class Person
|
10
|
-
#
|
11
|
-
# include ActiveModel::Serialization
|
12
|
-
#
|
13
|
-
# attr_accessor :name
|
14
|
-
#
|
15
|
-
# def attributes
|
16
|
-
# @attributes ||= {'name' => 'nil'}
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# Which would provide you with:
|
22
|
-
#
|
23
|
-
# person = Person.new
|
24
|
-
# person.serializable_hash # => {"name"=>nil}
|
25
|
-
# person.name = "Bob"
|
26
|
-
# person.serializable_hash # => {"name"=>"Bob"}
|
27
|
-
#
|
28
|
-
# You need to declare some sort of attributes hash which contains the attributes
|
29
|
-
# you want to serialize and their current value.
|
30
|
-
#
|
31
|
-
# Most of the time though, you will want to include the JSON or XML
|
32
|
-
# serializations. Both of these modules automatically include the
|
33
|
-
# ActiveModel::Serialization module, so there is no need to explicitly
|
34
|
-
# include it.
|
35
|
-
#
|
36
|
-
# So a minimal implementation including XML and JSON would be:
|
37
|
-
#
|
38
|
-
# class Person
|
39
|
-
#
|
40
|
-
# include ActiveModel::Serializers::JSON
|
41
|
-
# include ActiveModel::Serializers::Xml
|
42
|
-
#
|
43
|
-
# attr_accessor :name
|
44
|
-
#
|
45
|
-
# def attributes
|
46
|
-
# @attributes ||= {'name' => 'nil'}
|
47
|
-
# end
|
48
|
-
#
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# Which would provide you with:
|
52
|
-
#
|
53
|
-
# person = Person.new
|
54
|
-
# person.serializable_hash # => {"name"=>nil}
|
55
|
-
# person.to_json # => "{\"name\":null}"
|
56
|
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
57
|
-
#
|
58
|
-
# person.name = "Bob"
|
59
|
-
# person.serializable_hash # => {"name"=>"Bob"}
|
60
|
-
# person.to_json # => "{\"name\":\"Bob\"}"
|
61
|
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
62
5
|
module Serialization
|
63
6
|
def serializable_hash(options = nil)
|
64
7
|
options ||= {}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'active_support/json'
|
2
|
-
require 'active_support/core_ext/class/
|
2
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
3
3
|
|
4
4
|
module ActiveModel
|
5
5
|
module Serializers
|
@@ -10,18 +10,19 @@ module ActiveModel
|
|
10
10
|
included do
|
11
11
|
extend ActiveModel::Naming
|
12
12
|
|
13
|
-
|
14
|
-
self.include_root_in_json = true
|
13
|
+
cattr_accessor :include_root_in_json, :instance_writer => false
|
15
14
|
end
|
16
15
|
|
17
16
|
# Returns a JSON string representing the model. Some configuration is
|
18
17
|
# available through +options+.
|
19
18
|
#
|
20
|
-
# The option <tt>
|
21
|
-
# top-level behavior of to_json.
|
19
|
+
# The option <tt>ActiveRecord::Base.include_root_in_json</tt> controls the
|
20
|
+
# top-level behavior of to_json. In a new Rails application, it is set to
|
21
|
+
# <tt>true</tt> in initializers/new_rails_defaults.rb. When it is <tt>true</tt>,
|
22
22
|
# to_json will emit a single root node named after the object's type. For example:
|
23
23
|
#
|
24
24
|
# konata = User.find(1)
|
25
|
+
# ActiveRecord::Base.include_root_in_json = true
|
25
26
|
# konata.to_json
|
26
27
|
# # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
|
27
28
|
# "created_at": "2006/08/01", "awesome": true} }
|
@@ -80,11 +81,7 @@ module ActiveModel
|
|
80
81
|
# "title": "So I was thinking"}]}
|
81
82
|
def encode_json(encoder)
|
82
83
|
hash = serializable_hash(encoder.options)
|
83
|
-
if include_root_in_json
|
84
|
-
custom_root = encoder.options && encoder.options[:root]
|
85
|
-
hash = { custom_root || self.class.model_name.element => hash }
|
86
|
-
end
|
87
|
-
|
84
|
+
hash = { self.class.model_name.element => hash } if include_root_in_json
|
88
85
|
ActiveSupport::JSON.encode(hash)
|
89
86
|
end
|
90
87
|
|
@@ -93,9 +90,7 @@ module ActiveModel
|
|
93
90
|
end
|
94
91
|
|
95
92
|
def from_json(json)
|
96
|
-
|
97
|
-
hash = hash.values.first if include_root_in_json
|
98
|
-
self.attributes = hash
|
93
|
+
self.attributes = ActiveSupport::JSON.decode(json)
|
99
94
|
self
|
100
95
|
end
|
101
96
|
end
|
@@ -1,8 +1,5 @@
|
|
1
|
-
require 'active_support/core_ext/array/wrap'
|
2
1
|
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
-
require 'active_support/core_ext/array/conversions'
|
4
2
|
require 'active_support/core_ext/hash/conversions'
|
5
|
-
require 'active_support/core_ext/hash/slice'
|
6
3
|
|
7
4
|
module ActiveModel
|
8
5
|
module Serializers
|
@@ -14,31 +11,68 @@ module ActiveModel
|
|
14
11
|
class Attribute #:nodoc:
|
15
12
|
attr_reader :name, :value, :type
|
16
13
|
|
17
|
-
def initialize(name, serializable
|
14
|
+
def initialize(name, serializable)
|
18
15
|
@name, @serializable = name, serializable
|
19
|
-
@value = value || @serializable.send(name)
|
20
16
|
@type = compute_type
|
17
|
+
@value = compute_value
|
21
18
|
end
|
22
19
|
|
23
|
-
|
20
|
+
# There is a significant speed improvement if the value
|
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
34
|
decorations = {}
|
25
|
-
|
26
|
-
|
27
|
-
|
35
|
+
|
36
|
+
if type == :binary
|
37
|
+
decorations[:encoding] = 'base64'
|
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
|
+
|
28
48
|
decorations
|
29
49
|
end
|
30
50
|
|
31
|
-
|
51
|
+
protected
|
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
|
32
59
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
60
|
+
def compute_value
|
61
|
+
value = @serializable.send(name)
|
62
|
+
|
63
|
+
if formatter = Hash::XML_FORMATTING[type.to_s]
|
64
|
+
value ? formatter.call(value) : nil
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
end
|
39
69
|
end
|
40
70
|
|
41
71
|
class MethodAttribute < Attribute #:nodoc:
|
72
|
+
protected
|
73
|
+
def compute_type
|
74
|
+
Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string
|
75
|
+
end
|
42
76
|
end
|
43
77
|
|
44
78
|
attr_reader :options
|
@@ -51,84 +85,110 @@ module ActiveModel
|
|
51
85
|
@options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
|
52
86
|
end
|
53
87
|
|
54
|
-
# To replicate the behavior in ActiveRecord#attributes,
|
55
|
-
# takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
|
88
|
+
# To replicate the behavior in ActiveRecord#attributes,
|
89
|
+
# <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
|
56
90
|
# for a N level model but is set for the N+1 level models,
|
57
91
|
# then because <tt>:except</tt> is set to a default value, the second
|
58
92
|
# level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
|
59
93
|
# <tt>:only</tt> is set, always delete <tt>:except</tt>.
|
60
|
-
def
|
61
|
-
|
94
|
+
def serializable_attribute_names
|
95
|
+
attribute_names = @serializable.attributes.keys.sort
|
96
|
+
|
62
97
|
if options[:only].any?
|
63
|
-
|
98
|
+
attribute_names &= options[:only]
|
64
99
|
elsif options[:except].any?
|
65
|
-
|
66
|
-
else
|
67
|
-
attributes
|
100
|
+
attribute_names -= options[:except]
|
68
101
|
end
|
102
|
+
|
103
|
+
attribute_names
|
69
104
|
end
|
70
105
|
|
71
106
|
def serializable_attributes
|
72
|
-
|
73
|
-
self.class::Attribute.new(name, @serializable, value)
|
74
|
-
end
|
107
|
+
serializable_attribute_names.collect { |name| Attribute.new(name, @serializable) }
|
75
108
|
end
|
76
109
|
|
77
|
-
def
|
78
|
-
Array
|
79
|
-
methods <<
|
110
|
+
def serializable_method_attributes
|
111
|
+
Array(options[:methods]).inject([]) do |methods, name|
|
112
|
+
methods << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
|
80
113
|
methods
|
81
114
|
end
|
82
115
|
end
|
83
116
|
|
84
117
|
def serialize
|
85
|
-
|
86
|
-
|
87
|
-
options[:indent] ||= 2
|
88
|
-
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
|
89
|
-
|
90
|
-
@builder = options[:builder]
|
91
|
-
@builder.instruct! unless options[:skip_instruct]
|
118
|
+
args = [root]
|
92
119
|
|
93
|
-
|
94
|
-
|
120
|
+
if options[:namespace]
|
121
|
+
args << {:xmlns => options[:namespace]}
|
122
|
+
end
|
95
123
|
|
96
|
-
|
97
|
-
|
98
|
-
|
124
|
+
if options[:type]
|
125
|
+
args << {:type => options[:type]}
|
126
|
+
end
|
99
127
|
|
100
|
-
|
101
|
-
|
102
|
-
|
128
|
+
builder.tag!(*args) do
|
129
|
+
add_attributes
|
130
|
+
procs = options.delete(:procs)
|
131
|
+
options[:procs] = procs
|
103
132
|
add_procs
|
104
|
-
yield
|
133
|
+
yield builder if block_given?
|
105
134
|
end
|
106
135
|
end
|
107
136
|
|
108
|
-
|
137
|
+
private
|
138
|
+
def builder
|
139
|
+
@builder ||= begin
|
140
|
+
require 'builder' unless defined? ::Builder
|
141
|
+
options[:indent] ||= 2
|
142
|
+
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
|
109
143
|
|
110
|
-
|
111
|
-
|
144
|
+
unless options[:skip_instruct]
|
145
|
+
builder.instruct!
|
146
|
+
options[:skip_instruct] = true
|
147
|
+
end
|
112
148
|
|
113
|
-
|
114
|
-
|
115
|
-
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
|
116
|
-
ActiveSupport::XmlMini.to_tag(key, attribute.value,
|
117
|
-
options.merge(attribute.decorations))
|
149
|
+
builder
|
150
|
+
end
|
118
151
|
end
|
119
|
-
end
|
120
152
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
153
|
+
def root
|
154
|
+
root = (options[:root] || @serializable.class.model_name.singular).to_s
|
155
|
+
reformat_name(root)
|
156
|
+
end
|
157
|
+
|
158
|
+
def dasherize?
|
159
|
+
!options.has_key?(:dasherize) || options[:dasherize]
|
160
|
+
end
|
161
|
+
|
162
|
+
def camelize?
|
163
|
+
options.has_key?(:camelize) && options[:camelize]
|
164
|
+
end
|
165
|
+
|
166
|
+
def reformat_name(name)
|
167
|
+
name = name.camelize if camelize?
|
168
|
+
dasherize? ? name.dasherize : name
|
169
|
+
end
|
170
|
+
|
171
|
+
def add_attributes
|
172
|
+
(serializable_attributes + serializable_method_attributes).each do |attribute|
|
173
|
+
builder.tag!(
|
174
|
+
reformat_name(attribute.name),
|
175
|
+
attribute.value.to_s,
|
176
|
+
attribute.decorations(!options[:skip_types])
|
177
|
+
)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_procs
|
182
|
+
if procs = options.delete(:procs)
|
183
|
+
[ *procs ].each do |proc|
|
184
|
+
if proc.arity > 1
|
185
|
+
proc.call(options, @serializable)
|
186
|
+
else
|
187
|
+
proc.call(options)
|
188
|
+
end
|
128
189
|
end
|
129
190
|
end
|
130
191
|
end
|
131
|
-
end
|
132
192
|
end
|
133
193
|
|
134
194
|
def to_xml(options = {}, &block)
|