activeobject 0.0.3
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/CHANGE +10 -0
- data/Interface_desc +21 -0
- data/MIT-LICENSE +20 -0
- data/README +72 -0
- data/Rakefile.rb +9 -0
- data/active-object.gemspec +50 -0
- data/examples/account.rb +69 -0
- data/examples/data.tch +0 -0
- data/examples/light_cloud.yml +18 -0
- data/examples/test.rb +3 -0
- data/examples/user.rb +112 -0
- data/init.rb +4 -0
- data/lib/active-object.rb +23 -0
- data/lib/active_object/adapters/light_cloud.rb +40 -0
- data/lib/active_object/adapters/tokyo_cabinet.rb +48 -0
- data/lib/active_object/adapters/tokyo_tyrant.rb +14 -0
- data/lib/active_object/associations.rb +200 -0
- data/lib/active_object/base.rb +415 -0
- data/lib/active_object/callbacks.rb +180 -0
- data/lib/active_object/observer.rb +180 -0
- data/lib/active_object/serialization.rb +99 -0
- data/lib/active_object/serializers/json_serializer.rb +75 -0
- data/lib/active_object/serializers/xml_serializer.rb +325 -0
- data/lib/active_object/validations.rb +687 -0
- data/lib/active_support/callbacks.rb +303 -0
- data/lib/active_support/core_ext/array/access.rb +53 -0
- data/lib/active_support/core_ext/array/conversions.rb +183 -0
- data/lib/active_support/core_ext/array/extract_options.rb +20 -0
- data/lib/active_support/core_ext/array/grouping.rb +106 -0
- data/lib/active_support/core_ext/array/random_access.rb +12 -0
- data/lib/active_support/core_ext/array.rb +13 -0
- data/lib/active_support/core_ext/blank.rb +58 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +54 -0
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +140 -0
- data/lib/active_support/core_ext/class/removal.rb +50 -0
- data/lib/active_support/core_ext/class.rb +3 -0
- data/lib/active_support/core_ext/duplicable.rb +43 -0
- data/lib/active_support/core_ext/enumerable.rb +72 -0
- data/lib/active_support/core_ext/hash/conversions.rb +259 -0
- data/lib/active_support/core_ext/hash/keys.rb +52 -0
- data/lib/active_support/core_ext/hash.rb +8 -0
- data/lib/active_support/core_ext/module/aliasing.rb +74 -0
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +58 -0
- data/lib/active_support/core_ext/module.rb +16 -0
- data/lib/active_support/core_ext/object/conversions.rb +14 -0
- data/lib/active_support/core_ext/object/extending.rb +80 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +74 -0
- data/lib/active_support/core_ext/object/metaclass.rb +13 -0
- data/lib/active_support/core_ext/object/misc.rb +43 -0
- data/lib/active_support/core_ext/object.rb +5 -0
- data/lib/active_support/core_ext/string/inflections.rb +167 -0
- data/lib/active_support/core_ext/string.rb +7 -0
- data/lib/active_support/core_ext.rb +4 -0
- data/lib/active_support/inflections.rb +55 -0
- data/lib/active_support/inflector.rb +348 -0
- data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb +115 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb +139 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +63 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb +328 -0
- data/lib/active_support/vendor/builder-2.1.2/builder.rb +13 -0
- data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +1021 -0
- data/lib/active_support/vendor.rb +14 -0
- data/lib/active_support.rb +6 -0
- data/spec/case/association_test.rb +97 -0
- data/spec/case/base_test.rb +74 -0
- data/spec/case/callbacks_observers_test.rb +38 -0
- data/spec/case/callbacks_test.rb +424 -0
- data/spec/case/serialization_test.rb +87 -0
- data/spec/case/validations_test.rb +1482 -0
- data/spec/data.tch +0 -0
- data/spec/helper.rb +15 -0
- data/spec/light_cloud.yml +18 -0
- data/spec/model/account.rb +4 -0
- data/spec/model/topic.rb +26 -0
- data/spec/model/user.rb +8 -0
- metadata +173 -0
@@ -0,0 +1,303 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
|
3
|
+
# before or after an alteration of the object state.
|
4
|
+
#
|
5
|
+
# Mixing in this module allows you to define callbacks in your class.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# class Storage
|
9
|
+
# include ActiveSupport::Callbacks
|
10
|
+
#
|
11
|
+
# define_callbacks :before_save, :after_save
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class ConfigStorage < Storage
|
15
|
+
# before_save :saving_message
|
16
|
+
# def saving_message
|
17
|
+
# puts "saving..."
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# after_save do |object|
|
21
|
+
# puts "saved"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def save
|
25
|
+
# run_callbacks(:before_save)
|
26
|
+
# puts "- save"
|
27
|
+
# run_callbacks(:after_save)
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# config = ConfigStorage.new
|
32
|
+
# config.save
|
33
|
+
#
|
34
|
+
# Output:
|
35
|
+
# saving...
|
36
|
+
# - save
|
37
|
+
# saved
|
38
|
+
#
|
39
|
+
# Callbacks from parent classes are inherited.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
# class Storage
|
43
|
+
# include ActiveSupport::Callbacks
|
44
|
+
#
|
45
|
+
# define_callbacks :before_save, :after_save
|
46
|
+
#
|
47
|
+
# before_save :prepare
|
48
|
+
# def prepare
|
49
|
+
# puts "preparing save"
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# class ConfigStorage < Storage
|
54
|
+
# before_save :saving_message
|
55
|
+
# def saving_message
|
56
|
+
# puts "saving..."
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# after_save do |object|
|
60
|
+
# puts "saved"
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def save
|
64
|
+
# run_callbacks(:before_save)
|
65
|
+
# puts "- save"
|
66
|
+
# run_callbacks(:after_save)
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# config = ConfigStorage.new
|
71
|
+
# config.save
|
72
|
+
#
|
73
|
+
# Output:
|
74
|
+
# preparing save
|
75
|
+
# saving...
|
76
|
+
# - save
|
77
|
+
# saved
|
78
|
+
module Callbacks
|
79
|
+
class CallbackChain < Array
|
80
|
+
def self.build(kind, *methods, &block)
|
81
|
+
methods, options = extract_options(*methods, &block)
|
82
|
+
methods.map! { |method| Callback.new(kind, method, options) }
|
83
|
+
new(methods)
|
84
|
+
end
|
85
|
+
|
86
|
+
def run(object, options = {}, &terminator)
|
87
|
+
enumerator = options[:enumerator] || :each
|
88
|
+
|
89
|
+
unless block_given?
|
90
|
+
send(enumerator) { |callback| callback.call(object) }
|
91
|
+
else
|
92
|
+
send(enumerator) do |callback|
|
93
|
+
result = callback.call(object)
|
94
|
+
break result if terminator.call(result, object)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO: Decompose into more Array like behavior
|
100
|
+
def replace_or_append!(chain)
|
101
|
+
if index = index(chain)
|
102
|
+
self[index] = chain
|
103
|
+
else
|
104
|
+
self << chain
|
105
|
+
end
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def find(callback, &block)
|
110
|
+
select { |c| c == callback && (!block_given? || yield(c)) }.first
|
111
|
+
end
|
112
|
+
|
113
|
+
def delete(callback)
|
114
|
+
super(callback.is_a?(Callback) ? callback : find(callback))
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def self.extract_options(*methods, &block)
|
119
|
+
methods.flatten!
|
120
|
+
options = methods.extract_options!
|
121
|
+
methods << block if block_given?
|
122
|
+
return methods, options
|
123
|
+
end
|
124
|
+
|
125
|
+
def extract_options(*methods, &block)
|
126
|
+
self.class.extract_options(*methods, &block)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Callback
|
131
|
+
attr_reader :kind, :method, :identifier, :options
|
132
|
+
|
133
|
+
def initialize(kind, method, options = {})
|
134
|
+
@kind = kind
|
135
|
+
@method = method
|
136
|
+
@identifier = options[:identifier]
|
137
|
+
@options = options
|
138
|
+
end
|
139
|
+
|
140
|
+
def ==(other)
|
141
|
+
case other
|
142
|
+
when Callback
|
143
|
+
(self.identifier && self.identifier == other.identifier) || self.method == other.method
|
144
|
+
else
|
145
|
+
(self.identifier && self.identifier == other) || self.method == other
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def eql?(other)
|
150
|
+
self == other
|
151
|
+
end
|
152
|
+
|
153
|
+
def dup
|
154
|
+
self.class.new(@kind, @method, @options.dup)
|
155
|
+
end
|
156
|
+
|
157
|
+
def hash
|
158
|
+
if @identifier
|
159
|
+
@identifier.hash
|
160
|
+
else
|
161
|
+
@method.hash
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def call(*args, &block)
|
166
|
+
evaluate_method(method, *args, &block) if should_run_callback?(*args)
|
167
|
+
rescue LocalJumpError
|
168
|
+
raise ArgumentError,
|
169
|
+
"Cannot yield from a Proc type filter. The Proc must take two " +
|
170
|
+
"arguments and execute #call on the second argument."
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
def evaluate_method(method, *args, &block)
|
175
|
+
case method
|
176
|
+
when Symbol
|
177
|
+
object = args.shift
|
178
|
+
object.send(method, *args, &block)
|
179
|
+
when String
|
180
|
+
eval(method, args.first.instance_eval { binding })
|
181
|
+
when Proc, Method
|
182
|
+
method.call(*args, &block)
|
183
|
+
else
|
184
|
+
if method.respond_to?(kind)
|
185
|
+
method.send(kind, *args, &block)
|
186
|
+
else
|
187
|
+
raise ArgumentError,
|
188
|
+
"Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
|
189
|
+
"a block to be invoked, or an object responding to the callback method."
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def should_run_callback?(*args)
|
195
|
+
if options[:if]
|
196
|
+
evaluate_method(options[:if], *args)
|
197
|
+
elsif options[:unless]
|
198
|
+
!evaluate_method(options[:unless], *args)
|
199
|
+
else
|
200
|
+
true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.included(base)
|
206
|
+
base.extend ClassMethods
|
207
|
+
end
|
208
|
+
|
209
|
+
module ClassMethods
|
210
|
+
# 定义回调
|
211
|
+
#
|
212
|
+
# 例如:
|
213
|
+
# callbacks = %w(before_save)
|
214
|
+
# base.define_callbacks *callbacks
|
215
|
+
# 等价于
|
216
|
+
# class << base
|
217
|
+
# def before_save(*methods,&block)
|
218
|
+
# callbacks= CallbackChain.build(:before_save,*methods,&block)
|
219
|
+
# (@before_save_callbacks ||= CallbackChain.new).concat callbacks
|
220
|
+
# end
|
221
|
+
#
|
222
|
+
# def before_save_callback_chain
|
223
|
+
# @before_save_callbacks ||= CallbackChain.new
|
224
|
+
# if superclass.respond_to?(:before_save_callback_chain)
|
225
|
+
# CallbackChain.new(superclass.before_save_callback_chain+@before_save_callbacks)
|
226
|
+
# else
|
227
|
+
# @before_save_callbacks
|
228
|
+
# end
|
229
|
+
# end
|
230
|
+
# end
|
231
|
+
def define_callbacks(*callbacks)
|
232
|
+
callbacks.each do |callback|
|
233
|
+
class_eval <<-"end_eval"
|
234
|
+
def self.#{callback}(*methods, &block)
|
235
|
+
callbacks = CallbackChain.build(:#{callback}, *methods, &block)
|
236
|
+
(@#{callback}_callbacks ||= CallbackChain.new).concat callbacks
|
237
|
+
end
|
238
|
+
|
239
|
+
def self.#{callback}_callback_chain
|
240
|
+
@#{callback}_callbacks ||= CallbackChain.new
|
241
|
+
|
242
|
+
if superclass.respond_to?(:#{callback}_callback_chain)
|
243
|
+
CallbackChain.new(superclass.#{callback}_callback_chain + @#{callback}_callbacks)
|
244
|
+
else
|
245
|
+
@#{callback}_callbacks
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end_eval
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Runs all the callbacks defined for the given options.
|
254
|
+
#
|
255
|
+
# If a block is given it will be called after each callback receiving as arguments:
|
256
|
+
#
|
257
|
+
# * the result from the callback
|
258
|
+
# * the object which has the callback
|
259
|
+
#
|
260
|
+
# If the result from the block evaluates to false, the callback chain is stopped.
|
261
|
+
#
|
262
|
+
# Example:
|
263
|
+
# class Storage
|
264
|
+
# include ActiveSupport::Callbacks
|
265
|
+
#
|
266
|
+
# define_callbacks :before_save, :after_save
|
267
|
+
# end
|
268
|
+
#
|
269
|
+
# class ConfigStorage < Storage
|
270
|
+
# before_save :pass
|
271
|
+
# before_save :pass
|
272
|
+
# before_save :stop
|
273
|
+
# before_save :pass
|
274
|
+
#
|
275
|
+
# def pass
|
276
|
+
# puts "pass"
|
277
|
+
# end
|
278
|
+
#
|
279
|
+
# def stop
|
280
|
+
# puts "stop"
|
281
|
+
# return false
|
282
|
+
# end
|
283
|
+
#
|
284
|
+
# def save
|
285
|
+
# result = run_callbacks(:before_save) { |result, object| result == false }
|
286
|
+
# puts "- save" if result
|
287
|
+
# end
|
288
|
+
# end
|
289
|
+
#
|
290
|
+
# config = ConfigStorage.new
|
291
|
+
# config.save
|
292
|
+
#
|
293
|
+
# Output:
|
294
|
+
# pass
|
295
|
+
# pass
|
296
|
+
# stop
|
297
|
+
def run_callbacks(kind, options = {}, &block)
|
298
|
+
# self.class.send("#{kind}_callback_chain")
|
299
|
+
#
|
300
|
+
self.class.send("#{kind}_callback_chain").run(self, options, &block)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ActiveSupport #:nodoc:
|
2
|
+
module CoreExtensions #:nodoc:
|
3
|
+
module Array #:nodoc:
|
4
|
+
# Makes it easier to access parts of an array.
|
5
|
+
module Access
|
6
|
+
# Returns the tail of the array from +position+.
|
7
|
+
#
|
8
|
+
# %w( a b c d ).from(0) # => %w( a b c d )
|
9
|
+
# %w( a b c d ).from(2) # => %w( c d )
|
10
|
+
# %w( a b c d ).from(10) # => nil
|
11
|
+
# %w().from(0) # => nil
|
12
|
+
def from(position)
|
13
|
+
self[position..-1]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the beginning of the array up to +position+.
|
17
|
+
#
|
18
|
+
# %w( a b c d ).to(0) # => %w( a )
|
19
|
+
# %w( a b c d ).to(2) # => %w( a b c )
|
20
|
+
# %w( a b c d ).to(10) # => %w( a b c d )
|
21
|
+
# %w().to(0) # => %w()
|
22
|
+
def to(position)
|
23
|
+
self[0..position]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Equal to <tt>self[1]</tt>.
|
27
|
+
def second
|
28
|
+
self[1]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Equal to <tt>self[2]</tt>.
|
32
|
+
def third
|
33
|
+
self[2]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Equal to <tt>self[3]</tt>.
|
37
|
+
def fourth
|
38
|
+
self[3]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Equal to <tt>self[4]</tt>.
|
42
|
+
def fifth
|
43
|
+
self[4]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
|
47
|
+
def forty_two
|
48
|
+
self[41]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module ActiveSupport #:nodoc:
|
4
|
+
module CoreExtensions #:nodoc:
|
5
|
+
module Array #:nodoc:
|
6
|
+
module Conversions
|
7
|
+
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
|
8
|
+
# * <tt>:connector</tt> - The word used to join the last element in arrays with two or more elements (default: "and")
|
9
|
+
# * <tt>:skip_last_comma</tt> - Set to true to return "a, b and c" instead of "a, b, and c".
|
10
|
+
def to_sentence(options = {})
|
11
|
+
options.assert_valid_keys(:connector, :skip_last_comma, :locale)
|
12
|
+
|
13
|
+
default = I18n.translate(:'support.array.sentence_connector', :locale => options[:locale])
|
14
|
+
default_skip_last_comma = I18n.translate(:'support.array.skip_last_comma', :locale => options[:locale])
|
15
|
+
options.reverse_merge! :connector => default, :skip_last_comma => default_skip_last_comma
|
16
|
+
options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == ''
|
17
|
+
|
18
|
+
case length
|
19
|
+
when 0
|
20
|
+
""
|
21
|
+
when 1
|
22
|
+
self[0].to_s
|
23
|
+
when 2
|
24
|
+
"#{self[0]} #{options[:connector]}#{self[1]}"
|
25
|
+
else
|
26
|
+
"#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]}#{self[-1]}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
32
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
33
|
+
def to_param
|
34
|
+
collect { |e| e.to_param }.join '/'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Converts an array into a string suitable for use as a URL query string,
|
38
|
+
# using the given +key+ as the param name.
|
39
|
+
#
|
40
|
+
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
41
|
+
def to_query(key)
|
42
|
+
prefix = "#{key}[]"
|
43
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.included(base) #:nodoc:
|
47
|
+
base.class_eval do
|
48
|
+
alias_method :to_default_s, :to_s
|
49
|
+
alias_method :to_s, :to_formatted_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Converts a collection of elements into a formatted string by calling
|
54
|
+
# <tt>to_s</tt> on all elements and joining them:
|
55
|
+
#
|
56
|
+
# Blog.find(:all).to_formatted_s # => "First PostSecond PostThird Post"
|
57
|
+
#
|
58
|
+
# Adding in the <tt>:db</tt> argument as the format yields a prettier
|
59
|
+
# output:
|
60
|
+
#
|
61
|
+
# Blog.find(:all).to_formatted_s(:db) # => "First Post,Second Post,Third Post"
|
62
|
+
def to_formatted_s(format = :default)
|
63
|
+
case format
|
64
|
+
when :db
|
65
|
+
if respond_to?(:empty?) && self.empty?
|
66
|
+
"null"
|
67
|
+
else
|
68
|
+
collect { |element| element.id }.join(",")
|
69
|
+
end
|
70
|
+
else
|
71
|
+
to_default_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a string that represents this array in XML by sending +to_xml+
|
76
|
+
# to each element. Active Record collections delegate their representation
|
77
|
+
# in XML to this method.
|
78
|
+
#
|
79
|
+
# All elements are expected to respond to +to_xml+, if any of them does
|
80
|
+
# not an exception is raised.
|
81
|
+
#
|
82
|
+
# The root node reflects the class name of the first element in plural
|
83
|
+
# if all elements belong to the same type and that's not Hash:
|
84
|
+
#
|
85
|
+
# customer.projects.to_xml
|
86
|
+
#
|
87
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
88
|
+
# <projects type="array">
|
89
|
+
# <project>
|
90
|
+
# <amount type="decimal">20000.0</amount>
|
91
|
+
# <customer-id type="integer">1567</customer-id>
|
92
|
+
# <deal-date type="date">2008-04-09</deal-date>
|
93
|
+
# ...
|
94
|
+
# </project>
|
95
|
+
# <project>
|
96
|
+
# <amount type="decimal">57230.0</amount>
|
97
|
+
# <customer-id type="integer">1567</customer-id>
|
98
|
+
# <deal-date type="date">2008-04-15</deal-date>
|
99
|
+
# ...
|
100
|
+
# </project>
|
101
|
+
# </projects>
|
102
|
+
#
|
103
|
+
# Otherwise the root element is "records":
|
104
|
+
#
|
105
|
+
# [{:foo => 1, :bar => 2}, {:baz => 3}].to_xml
|
106
|
+
#
|
107
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
108
|
+
# <records type="array">
|
109
|
+
# <record>
|
110
|
+
# <bar type="integer">2</bar>
|
111
|
+
# <foo type="integer">1</foo>
|
112
|
+
# </record>
|
113
|
+
# <record>
|
114
|
+
# <baz type="integer">3</baz>
|
115
|
+
# </record>
|
116
|
+
# </records>
|
117
|
+
#
|
118
|
+
# If the collection is empty the root element is "nil-classes" by default:
|
119
|
+
#
|
120
|
+
# [].to_xml
|
121
|
+
#
|
122
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
123
|
+
# <nil-classes type="array"/>
|
124
|
+
#
|
125
|
+
# To ensure a meaningful root element use the <tt>:root</tt> option:
|
126
|
+
#
|
127
|
+
# customer_with_no_projects.projects.to_xml(:root => "projects")
|
128
|
+
#
|
129
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
130
|
+
# <projects type="array"/>
|
131
|
+
#
|
132
|
+
# By default root children have as node name the one of the root
|
133
|
+
# singularized. You can change it with the <tt>:children</tt> option.
|
134
|
+
#
|
135
|
+
# The +options+ hash is passed downwards:
|
136
|
+
#
|
137
|
+
# Message.all.to_xml(:skip_types => true)
|
138
|
+
#
|
139
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
140
|
+
# <messages>
|
141
|
+
# <message>
|
142
|
+
# <created-at>2008-03-07T09:58:18+01:00</created-at>
|
143
|
+
# <id>1</id>
|
144
|
+
# <name>1</name>
|
145
|
+
# <updated-at>2008-03-07T09:58:18+01:00</updated-at>
|
146
|
+
# <user-id>1</user-id>
|
147
|
+
# </message>
|
148
|
+
# </messages>
|
149
|
+
#
|
150
|
+
def to_xml(options = {})
|
151
|
+
raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
|
152
|
+
|
153
|
+
options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"
|
154
|
+
options[:children] ||= options[:root].singularize
|
155
|
+
options[:indent] ||= 2
|
156
|
+
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
157
|
+
|
158
|
+
root = options.delete(:root).to_s
|
159
|
+
children = options.delete(:children)
|
160
|
+
|
161
|
+
if !options.has_key?(:dasherize) || options[:dasherize]
|
162
|
+
root = root.dasherize
|
163
|
+
end
|
164
|
+
|
165
|
+
options[:builder].instruct! unless options.delete(:skip_instruct)
|
166
|
+
|
167
|
+
opts = options.merge({ :root => children })
|
168
|
+
|
169
|
+
xml = options[:builder]
|
170
|
+
if empty?
|
171
|
+
xml.tag!(root, options[:skip_types] ? {} : {:type => "array"})
|
172
|
+
else
|
173
|
+
xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) {
|
174
|
+
yield xml if block_given?
|
175
|
+
each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) }
|
176
|
+
}
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveSupport #:nodoc:
|
2
|
+
module CoreExtensions #:nodoc:
|
3
|
+
module Array #:nodoc:
|
4
|
+
module ExtractOptions
|
5
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
6
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
7
|
+
#
|
8
|
+
# def options(*args)
|
9
|
+
# args.extract_options!
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# options(1, 2) # => {}
|
13
|
+
# options(1, 2, :a => :b) # => {:a=>:b}
|
14
|
+
def extract_options!
|
15
|
+
last.is_a?(::Hash) ? pop : {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
module ActiveSupport #:nodoc:
|
4
|
+
module CoreExtensions #:nodoc:
|
5
|
+
module Array #:nodoc:
|
6
|
+
module Grouping
|
7
|
+
# Splits or iterates over the array in groups of size +number+,
|
8
|
+
# padding any remaining slots with +fill_with+ unless it is +false+.
|
9
|
+
#
|
10
|
+
# %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
|
11
|
+
# ["1", "2", "3"]
|
12
|
+
# ["4", "5", "6"]
|
13
|
+
# ["7", nil, nil]
|
14
|
+
#
|
15
|
+
# %w(1 2 3).in_groups_of(2, ' ') {|group| p group}
|
16
|
+
# ["1", "2"]
|
17
|
+
# ["3", " "]
|
18
|
+
#
|
19
|
+
# %w(1 2 3).in_groups_of(2, false) {|group| p group}
|
20
|
+
# ["1", "2"]
|
21
|
+
# ["3"]
|
22
|
+
def in_groups_of(number, fill_with = nil)
|
23
|
+
if fill_with == false
|
24
|
+
collection = self
|
25
|
+
else
|
26
|
+
# size % number gives how many extra we have;
|
27
|
+
# subtracting from number gives how many to add;
|
28
|
+
# modulo number ensures we don't add group of just fill.
|
29
|
+
padding = (number - size % number) % number
|
30
|
+
collection = dup.concat([fill_with] * padding)
|
31
|
+
end
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
collection.each_slice(number) { |slice| yield(slice) }
|
35
|
+
else
|
36
|
+
returning [] do |groups|
|
37
|
+
collection.each_slice(number) { |group| groups << group }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Splits or iterates over the array in +number+ of groups, padding any
|
43
|
+
# remaining slots with +fill_with+ unless it is +false+.
|
44
|
+
#
|
45
|
+
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
|
46
|
+
# ["1", "2", "3", "4"]
|
47
|
+
# ["5", "6", "7", nil]
|
48
|
+
# ["8", "9", "10", nil]
|
49
|
+
#
|
50
|
+
# %w(1 2 3 4 5 6 7).in_groups(3, ' ') {|group| p group}
|
51
|
+
# ["1", "2", "3"]
|
52
|
+
# ["4", "5", " "]
|
53
|
+
# ["6", "7", " "]
|
54
|
+
#
|
55
|
+
# %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
|
56
|
+
# ["1", "2", "3"]
|
57
|
+
# ["4", "5"]
|
58
|
+
# ["6", "7"]
|
59
|
+
def in_groups(number, fill_with = nil)
|
60
|
+
# size / number gives minor group size;
|
61
|
+
# size % number gives how many objects need extra accomodation;
|
62
|
+
# each group hold either division or division + 1 items.
|
63
|
+
division = size / number
|
64
|
+
modulo = size % number
|
65
|
+
|
66
|
+
# create a new array avoiding dup
|
67
|
+
groups = []
|
68
|
+
start = 0
|
69
|
+
|
70
|
+
number.times do |index|
|
71
|
+
length = division + (modulo > 0 && modulo > index ? 1 : 0)
|
72
|
+
padding = fill_with != false &&
|
73
|
+
modulo > 0 && length == division ? 1 : 0
|
74
|
+
groups << slice(start, length).concat([fill_with] * padding)
|
75
|
+
start += length
|
76
|
+
end
|
77
|
+
|
78
|
+
if block_given?
|
79
|
+
groups.each{|g| yield(g) }
|
80
|
+
else
|
81
|
+
groups
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Divides the array into one or more subarrays based on a delimiting +value+
|
86
|
+
# or the result of an optional block.
|
87
|
+
#
|
88
|
+
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
|
89
|
+
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
|
90
|
+
def split(value = nil)
|
91
|
+
using_block = block_given?
|
92
|
+
|
93
|
+
inject([[]]) do |results, element|
|
94
|
+
if (using_block && yield(element)) || (value == element)
|
95
|
+
results << []
|
96
|
+
else
|
97
|
+
results.last << element
|
98
|
+
end
|
99
|
+
|
100
|
+
results
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_support/core_ext/array/access'
|
2
|
+
require 'active_support/core_ext/array/conversions'
|
3
|
+
require 'active_support/core_ext/array/extract_options'
|
4
|
+
require 'active_support/core_ext/array/grouping'
|
5
|
+
require 'active_support/core_ext/array/random_access'
|
6
|
+
|
7
|
+
class Array #:nodoc:
|
8
|
+
include ActiveSupport::CoreExtensions::Array::Access
|
9
|
+
include ActiveSupport::CoreExtensions::Array::Conversions
|
10
|
+
include ActiveSupport::CoreExtensions::Array::ExtractOptions
|
11
|
+
include ActiveSupport::CoreExtensions::Array::Grouping
|
12
|
+
include ActiveSupport::CoreExtensions::Array::RandomAccess
|
13
|
+
end
|