activeobject 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/CHANGE +10 -0
  2. data/Interface_desc +21 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +72 -0
  5. data/Rakefile.rb +9 -0
  6. data/active-object.gemspec +50 -0
  7. data/examples/account.rb +69 -0
  8. data/examples/data.tch +0 -0
  9. data/examples/light_cloud.yml +18 -0
  10. data/examples/test.rb +3 -0
  11. data/examples/user.rb +112 -0
  12. data/init.rb +4 -0
  13. data/lib/active-object.rb +23 -0
  14. data/lib/active_object/adapters/light_cloud.rb +40 -0
  15. data/lib/active_object/adapters/tokyo_cabinet.rb +48 -0
  16. data/lib/active_object/adapters/tokyo_tyrant.rb +14 -0
  17. data/lib/active_object/associations.rb +200 -0
  18. data/lib/active_object/base.rb +415 -0
  19. data/lib/active_object/callbacks.rb +180 -0
  20. data/lib/active_object/observer.rb +180 -0
  21. data/lib/active_object/serialization.rb +99 -0
  22. data/lib/active_object/serializers/json_serializer.rb +75 -0
  23. data/lib/active_object/serializers/xml_serializer.rb +325 -0
  24. data/lib/active_object/validations.rb +687 -0
  25. data/lib/active_support/callbacks.rb +303 -0
  26. data/lib/active_support/core_ext/array/access.rb +53 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +183 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +20 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +106 -0
  30. data/lib/active_support/core_ext/array/random_access.rb +12 -0
  31. data/lib/active_support/core_ext/array.rb +13 -0
  32. data/lib/active_support/core_ext/blank.rb +58 -0
  33. data/lib/active_support/core_ext/class/attribute_accessors.rb +54 -0
  34. data/lib/active_support/core_ext/class/inheritable_attributes.rb +140 -0
  35. data/lib/active_support/core_ext/class/removal.rb +50 -0
  36. data/lib/active_support/core_ext/class.rb +3 -0
  37. data/lib/active_support/core_ext/duplicable.rb +43 -0
  38. data/lib/active_support/core_ext/enumerable.rb +72 -0
  39. data/lib/active_support/core_ext/hash/conversions.rb +259 -0
  40. data/lib/active_support/core_ext/hash/keys.rb +52 -0
  41. data/lib/active_support/core_ext/hash.rb +8 -0
  42. data/lib/active_support/core_ext/module/aliasing.rb +74 -0
  43. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  44. data/lib/active_support/core_ext/module/attribute_accessors.rb +58 -0
  45. data/lib/active_support/core_ext/module.rb +16 -0
  46. data/lib/active_support/core_ext/object/conversions.rb +14 -0
  47. data/lib/active_support/core_ext/object/extending.rb +80 -0
  48. data/lib/active_support/core_ext/object/instance_variables.rb +74 -0
  49. data/lib/active_support/core_ext/object/metaclass.rb +13 -0
  50. data/lib/active_support/core_ext/object/misc.rb +43 -0
  51. data/lib/active_support/core_ext/object.rb +5 -0
  52. data/lib/active_support/core_ext/string/inflections.rb +167 -0
  53. data/lib/active_support/core_ext/string.rb +7 -0
  54. data/lib/active_support/core_ext.rb +4 -0
  55. data/lib/active_support/inflections.rb +55 -0
  56. data/lib/active_support/inflector.rb +348 -0
  57. data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
  58. data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
  59. data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
  60. data/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb +115 -0
  61. data/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb +139 -0
  62. data/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +63 -0
  63. data/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb +328 -0
  64. data/lib/active_support/vendor/builder-2.1.2/builder.rb +13 -0
  65. data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +1021 -0
  66. data/lib/active_support/vendor.rb +14 -0
  67. data/lib/active_support.rb +6 -0
  68. data/spec/case/association_test.rb +97 -0
  69. data/spec/case/base_test.rb +74 -0
  70. data/spec/case/callbacks_observers_test.rb +38 -0
  71. data/spec/case/callbacks_test.rb +424 -0
  72. data/spec/case/serialization_test.rb +87 -0
  73. data/spec/case/validations_test.rb +1482 -0
  74. data/spec/data.tch +0 -0
  75. data/spec/helper.rb +15 -0
  76. data/spec/light_cloud.yml +18 -0
  77. data/spec/model/account.rb +4 -0
  78. data/spec/model/topic.rb +26 -0
  79. data/spec/model/user.rb +8 -0
  80. metadata +173 -0
@@ -0,0 +1,200 @@
1
+ module ActiveObject
2
+
3
+ module Associations
4
+ def self.included(base)
5
+ base.send :include, HasOneAssociation
6
+ base.send :include, HasManyAssociation
7
+ end
8
+
9
+ module HasOneAssociation
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ # has_one用于指明两个对象之间的关联。
17
+ #
18
+ # 一旦使用has_one,下列方法将被添加:
19
+ #
20
+ # [association]
21
+ # 返回关联对象. 如果没有找到则返回+nil+。
22
+ # [association=(associate)]
23
+ # 对关联对象赋值。
24
+ # [association.nil?]
25
+ # 如果没有关联对象,则返回+true+。
26
+ #
27
+ # (用第一个参数替换+association+,例如:
28
+ # <tt>has_one :manager</tt> 将添加 <tt>manager.nil?</tt>.)
29
+ #
30
+ # === 示例
31
+ #
32
+ # Account类定义了<tt>has_one :beneficiary</tt>,将添加:
33
+ # * <tt>Account#beneficiary</tt>
34
+ # * <tt>Account#beneficiary=(beneficiary)</tt>
35
+ # * <tt>Account#beneficiary.nil?</tt>
36
+ #
37
+ # === 选项
38
+ #
39
+ # 通过hash选项指定关联的行为
40
+ #
41
+ # 选项:
42
+ # [:class_name]
43
+ # 指定关联的类名。默认情况类名是有关联推断出来的,如果无法推断则需要显性指定。
44
+ #
45
+ # 选项示例:
46
+ # has_one :icon,:class_name=>"Photo"
47
+ #
48
+ def has_one(association, options = {})
49
+
50
+ class_name = options.delete(:class_name) || association.to_s.camelize
51
+ write_inheritable_attribute "#{association}_class",class_name # 保存关联的类名
52
+ attr_accessor "#{association}"
53
+ merge_attributes "#{association}_id" # 将关联id作为持久化属性
54
+
55
+ define_method("#{association}_id") do
56
+ self.instance_variable_get("@#{association}") ? self.instance_variable_get("@#{association}").id : nil
57
+ end
58
+
59
+ define_method("#{association}_id=") do |value|
60
+ self.instance_variable_set("@#{association}",self.class.read_inheritable_attribute("#{association}_class").constantize.find(value))
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+
67
+ module HasManyAssociation
68
+ def self.included(base)
69
+ base.extend ClassMethods
70
+ end
71
+
72
+ class Collection
73
+ def initialize()
74
+ @objects = []
75
+ end
76
+ # 遍历关联对象集合
77
+ def each
78
+ @objects.each do |object|
79
+ yield object
80
+ end
81
+ end
82
+
83
+ def each_with_index
84
+ @objects.each_with_index do |object,index|
85
+ yield object,index
86
+ end
87
+ end
88
+
89
+ # 添加一个关联对象
90
+ def append(object)
91
+ @objects << object
92
+ end
93
+
94
+ # 插入一个关联对象
95
+ def insert(index,object)
96
+ @objects.insert(index,object)
97
+ end
98
+
99
+ # 删除一个关联对象
100
+ def delete(object)
101
+ @objects.delete(object)
102
+ end
103
+
104
+ def size
105
+ @objects.size
106
+ end
107
+
108
+ def clear
109
+ @objects.clear
110
+ end
111
+
112
+ def empty?
113
+ @objects.empty?
114
+ end
115
+
116
+ def object_ids
117
+ @objects.collect{|object| object.id}
118
+ end
119
+
120
+ def objects
121
+ @objects
122
+ end
123
+ end
124
+
125
+ module ClassMethods
126
+
127
+ # has_many用于指明两个对象之间的关联。
128
+ #
129
+ # 一旦使用has_many,下列方法将被添加:
130
+ # [associations.each]
131
+ # 遍历对象集合
132
+ # [associations]
133
+ # 返回关联的对象集合. 如果没有找到则返回空数组。
134
+ # [associations.append(object)]
135
+ # 添加一个关联对象。
136
+ # [associations.insert(object,index)]
137
+ # 插入一个关联对象。
138
+ # [associations.delete(object)]
139
+ # 删除一个关联对象。
140
+ # [associations.clear]
141
+ # 清除所有关联对象。
142
+ # [associations.size]
143
+ # 关联对象的数量。
144
+ # [association.empty?]
145
+ # 如果没有关联对象,则返回+true+。
146
+ #
147
+ # (用第一个参数替换+associations+,例如:
148
+ # <tt>has_many :managers</tt> 将添加 <tt>managers.empty?</tt>.)
149
+ #
150
+ # === 示例
151
+ #
152
+ # Account类定义了<tt>has_many :friends</tt>,将添加:
153
+ # * <tt>Account#friends</tt>
154
+ # * <tt>Account#friends.append(user)</tt>
155
+ # * <tt>Account#friends.delete(user)</tt>
156
+ # * <tt>Account#friends.clear</tt>
157
+ # * <tt>Account#friends.size</tt>
158
+ # * <tt>Account#friends.empty?</tt>
159
+ #
160
+ # === 选项
161
+ #
162
+ # 通过hash选项指定关联的行为
163
+ #
164
+ # 选项:
165
+ # [:class_name]
166
+ # 指定关联的类名。默认情况类名是有关联推断出来的,如果无法推断则需要显性指定。
167
+ #
168
+ # 选项示例:
169
+ # has_many :friends,:class_name=>"User"
170
+ #
171
+ def has_many(associations, options = {})
172
+
173
+ class_name = options.delete(:class_name) || associations.to_s.singularize.camelize
174
+ write_inheritable_attribute "#{associations}_class",class_name # 保存关联的类名
175
+ merge_attributes "#{associations}_ids" # 将关联ids作为持久化属性
176
+
177
+ define_method("#{associations}_ids") do
178
+ self.instance_variable_get("@#{associations}") ? self.instance_variable_get("@#{associations}").object_ids : []
179
+ end
180
+
181
+ define_method("#{associations}_ids=") do |value|
182
+ self.instance_variable_set("@#{associations}",Collection.new)
183
+ value.each do |object_id|
184
+ self.instance_variable_get("@#{associations}").append(self.class.read_inheritable_attribute("#{associations}_class").constantize.find(object_id))
185
+ end
186
+ end
187
+
188
+ define_method("#{associations}") do
189
+ self.instance_variable_set("@#{associations}",Collection.new) unless self.instance_variable_get("@#{associations}")
190
+ self.instance_variable_get("@#{associations}")
191
+ end
192
+
193
+ end
194
+
195
+ end
196
+ end
197
+
198
+ end
199
+
200
+ end
@@ -0,0 +1,415 @@
1
+ require 'yaml'
2
+ require 'uuid'
3
+ require 'json'
4
+ require 'active_object/adapters/light_cloud'
5
+ require 'active_object/adapters/tokyo_cabinet'
6
+ require 'active_object/adapters/tokyo_tyrant'
7
+
8
+
9
+ # 实现访问LightCloud的通用方法
10
+ module ActiveObject
11
+ class ActiveObjectError < StandardError;end
12
+
13
+ class ObjectNotFound < ActiveObjectError;end
14
+
15
+ class ObjectNotSaved < ActiveObjectError;end
16
+
17
+ class ConfigError < ActiveObjectError;end
18
+
19
+ # ActiveObject内建id属性,采用uuid格式,对象的key由对象类和uuid组成,中间用下划线相连。通过attribute指定需要持久化的属性。
20
+ #
21
+ # == 描述类
22
+ # ActiveObject所有类都是从Base类派生的,在派生新类时需要指定持久化存储的属性.就像下面一样:
23
+ #
24
+ # class User < ActiveObject::Base
25
+ # attribute :name,:email,:encrypt_password
26
+ # end
27
+ #
28
+ # 每个对象有默认的ID,如果想指定主关键字,可以通过primary_key指定
29
+ #
30
+ # class User < ActiveObject::Base
31
+ # primary_key :email
32
+ # attribute :name,:email,:encrypt_password
33
+ # end
34
+ #
35
+ # == 新建
36
+ # 可以通过hash参数新建对象,例如:
37
+ # user = User.new(:name => "David", :occupation => "Code Artist")
38
+ # user.name # => "David"
39
+ #
40
+ # 也可以使用块初始化对象:
41
+ #
42
+ # user = User.new do |u|
43
+ # u.name = "David"
44
+ # u.occupation = "Code Artist"
45
+ # end
46
+ #
47
+ # 当然,也可以在新建后对特定的属性赋值:
48
+ #
49
+ # user = User.new
50
+ # user.name = "David"
51
+ # user.occupation = "Code Artist"
52
+ #
53
+ # == 删除
54
+ # 删除主关键字与参数 +id+ 匹配的对象。
55
+ #
56
+ # ==== 参数
57
+ #
58
+ # * +id+ - 对象的主关键字.
59
+ #
60
+ # ==== 例子
61
+ #
62
+ # User.delete('aaron@nonobo.com')
63
+
64
+
65
+ class Base
66
+ write_inheritable_attribute :attributes,[:id]
67
+ write_inheritable_attribute :primary_key,nil
68
+
69
+ @@configurations = nil
70
+ @@database_model = nil
71
+
72
+ @@connection = nil
73
+
74
+ class << self
75
+
76
+ # 定义需要持久化的属性
77
+ # 示例:
78
+ # class User < ActiveObject::Base
79
+ # attribute :email,:name,:password # 表示email,name,password需要持久化存储
80
+ # end
81
+ def attribute(*options)
82
+ options.each do |attribute|
83
+ attr_accessor attribute
84
+ end
85
+ merge_attributes options
86
+ end
87
+
88
+ def attributes
89
+ read_inheritable_attribute(:attributes)
90
+ end
91
+
92
+ def primary_key(value=nil)
93
+ if value
94
+ write_inheritable_attribute :primary_key,value
95
+ else
96
+ read_inheritable_attribute(:primary_key)
97
+ end
98
+ end
99
+
100
+
101
+ # 配置ActiveObject访问的后台数据库支撑,目前支持LightCloud/TokyoTyrant/TokyoCabinet。
102
+ # 参数 +model+ 用于描述后台数据库的类型。
103
+ # :TC => 表示TokyoCabinet
104
+ # :TT => 表示TokyoTyrant
105
+ # :LC => 表示LightCloud
106
+ #
107
+ # 参数 +config+ 用于接收后台数据库的配置信息。
108
+ #
109
+ # 当model指定为TokyoCabinet数据库时,config为指定的文件名,例如:
110
+ # ActiveObject::Base.configure :TC, File.join(File.dirname(__FILE__),'act.tch')
111
+ # 这里需要注意的是指定的文件名后缀符合TokyoCabinet的约定规则。更多信息请参看:http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
112
+ #
113
+ #
114
+ #
115
+ # 当model指定为TokyoTyrant数据库时, config为指定的远程服务器地址,例如:
116
+ # ActiveObject::Base.configure :TT, '127.0.0.1:54321'
117
+ #
118
+ # 当model指定为LightCloud数据库时, config可以接收符合配置规则的hash:
119
+ # ActiveObject::Base.configure :LC, {'user'=>{'lookup_one'=>['127.0.0.1:30001','127.0.0.1:33001'],'storage_one'=>['127.0.0.1:20001','127.0.0.1:24001']}}
120
+ # 也可以是配置文件的文件名。
121
+ # ActiveObject::Base.configure :LC, File.join(File.dirname(__FILE__),'config.yml')
122
+ #
123
+ def configure(model,config)
124
+ @@configurations = config
125
+ @@database_model = model
126
+ case @@database_model
127
+ when :TC
128
+ ActiveObject::Base.send :include,ActiveObjectTokyoCabinet
129
+ when :TT
130
+ ActiveObject::Base.send :include,ActiveObjectTokyoTyrant
131
+ when :LC
132
+ ActiveObject::Base.send :include,ActiveObjectLightCloud
133
+ else
134
+ raise ConfigError,'没有指定数据库类型!'
135
+ end
136
+ end
137
+
138
+ def configurations
139
+ @@configurations
140
+ end
141
+
142
+ # 返回当前所使用的数据库模型
143
+ # :TC表示采用的是TokyoCabinet
144
+ # :TT表示采用的是TokyoTyrant
145
+ # :LC表示采用的是LightCloud
146
+ def database_model
147
+ @@database_model
148
+ end
149
+
150
+ # 通过主关键字查找对象
151
+ #
152
+ # +id+ 参数表示需要找回的主关键字.
153
+ #
154
+ # ==== Example
155
+ # User.find('aaron@nonobo.com')
156
+ #
157
+ def find(id)
158
+ return nil if id.blank?
159
+ record = load(id)
160
+ record ? instance(id,record) : nil
161
+ end
162
+
163
+ # 查找该类的所有对象
164
+ # User.find_all => 取出所有的用户
165
+ #def find_all
166
+ #end
167
+
168
+ # 新建对象(或多个对象)并保存到数据库(假设都通过验证)
169
+ # 不管是否保存到数据库,都将返回对象.
170
+ #
171
+ # 参数 +attributes+ 可以是Hash或Hash数组.
172
+ #
173
+ # ==== 示例
174
+ # # 创建单个新对象
175
+ # User.create(:first_name => 'Jamie')
176
+ #
177
+ # # 创建多个对象
178
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
179
+ #
180
+ # # 创建对象,并通过块进行其它属性的赋值.
181
+ # User.create(:first_name => 'Jamie') do |u|
182
+ # u.is_admin = false
183
+ # end
184
+ #
185
+ # # 创建多个对象,并通过块进行其它属性的赋值,每个对象都会执行块:
186
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
187
+ # u.is_admin = false
188
+ # end
189
+ def create(attributes = nil, &block)
190
+ if attributes.is_a?(Array)
191
+ attributes.collect { |attr| create(attr, &block) }
192
+ else
193
+ object = new(attributes)
194
+ yield(object) if block_given?
195
+ object.save
196
+ object
197
+ end
198
+ end
199
+
200
+
201
+ # 删除主关键字与参数 +id+ 匹配的对象,并且在删除之前执行所有的回调和过滤。
202
+ #
203
+ # ==== 参数
204
+ #
205
+ # * +id+ - 对象的主关键字.
206
+ #
207
+ # ==== 示例
208
+ # User.destroy('aaron@nonobo.com')
209
+ def destroy(id)
210
+ find(id).destroy
211
+ end
212
+
213
+ # 在适配器中重写
214
+ #def delete(id)
215
+ #end
216
+
217
+ def base_class
218
+ class_of_active_object_descendant(self)
219
+ end
220
+
221
+ def self_and_descendents_from_active_object#nodoc:
222
+ klass = self
223
+ classes = [klass]
224
+ while klass != klass.base_class
225
+ classes << klass = klass.superclass
226
+ end
227
+ classes
228
+ rescue
229
+ [self]
230
+ end
231
+
232
+ # 通过id转换成存取用的key
233
+ def id_to_key(id)
234
+ "#{self.to_s}_#{id}"
235
+ end
236
+
237
+ # 将key转换成id
238
+ def key_to_id(key)
239
+ key[key.index("_")+1..-1]
240
+ end
241
+
242
+ def deserialize_attributes(raw_record)
243
+ Marshal.load(raw_record)
244
+ end
245
+
246
+ def serialize_attributes(record)
247
+ Marshal.dump(record)
248
+ end
249
+
250
+ protected
251
+ def class_of_active_object_descendant(klass)
252
+ if klass.superclass == Base
253
+ klass
254
+ elsif klass.superclass.nil?
255
+ raise ActiveObjectError, "#{name} doesn't belong in a hierarchy descending from ActiveObject"
256
+ else
257
+ class_of_active_object_descendant(klass.superclass)
258
+ end
259
+ end
260
+
261
+ def configurations=(value)
262
+ @@configurations= value
263
+ end
264
+
265
+ def database_model=(value)
266
+ @@database_model = value
267
+ end
268
+
269
+ # 合并属性
270
+ def merge_attributes(options)
271
+ attrs = read_inheritable_attribute :attributes
272
+ attrs << options
273
+ write_inheritable_attribute :attributes, attrs.flatten.uniq
274
+ end
275
+
276
+ private
277
+ # 在设配器中重写
278
+ #def load(id)
279
+ #end
280
+
281
+ # 构建一个对象实例
282
+ def instance(id,record)
283
+ object = new(record)
284
+ object.send "id=",id
285
+ object.instance_variable_set(:@new_record,false)
286
+ object
287
+ end
288
+
289
+ end
290
+
291
+ def initialize(attributes = nil)
292
+ self.id = UUID.new.generate
293
+ @new_record = true
294
+ assign_attributes(attributes)
295
+ object = yield self if block_given?
296
+ object
297
+ end
298
+
299
+ def attributes
300
+ self.class.attributes
301
+ end
302
+
303
+ # id用来标识对象
304
+ # 为了能够通过前缀访问对象,在uuid之前加入对象的类名作为存取的key
305
+ def id=(value)
306
+ @key = self.class.id_to_key(value)
307
+ end
308
+
309
+ def id
310
+ self.class.key_to_id(@key)
311
+ end
312
+
313
+ # 如果对象没有被存储,表明对象还是新记录,返回true,否则返回false.
314
+ def new_record?
315
+ @new_record
316
+ end
317
+
318
+ # 如果对象已经被删除,返回true,否则返回false。
319
+ def destroyed?
320
+ @destroyed || false
321
+ end
322
+
323
+ # 从数据库中删除对象
324
+ # 不像 #destroy, 这个方法不运行 +before_delete+ 和 +after_delete+ 回调.
325
+ def delete
326
+ self.class.delete(id) unless new_record?
327
+ @destroyed = true
328
+ end
329
+
330
+ def destroy
331
+ delete
332
+ end
333
+
334
+ # 从数据库中重新加载对象的属性
335
+ def reload
336
+ #raise "Object is deleted" if destroyed?
337
+ unless new_record?
338
+ begin
339
+ attributes = self.class.send :load, self.id
340
+ assign_attributes(attributes)
341
+ true
342
+ rescue
343
+ false
344
+ end
345
+ end
346
+ end
347
+
348
+ # :call-seq:
349
+ # save(perform_validation = true)
350
+ #
351
+ # 保存对象.
352
+ #
353
+ # 如果对象是新对象,那么在数据库中创建。否则,如果对象是数据库中已经存在的对象,那么更新数据库
354
+ #
355
+ # 如果 +perform_validation+ 是true,将执行验证。如果验证失败,那么动作被取消,并且返回false。
356
+ #
357
+ # +save+ 相关联的一系列回调. 如果任何一个<tt>before_*</tt>回调返回 +false+,那么+save+动作被取消并返回 +false+.
358
+ def save
359
+ create_or_update
360
+ end
361
+
362
+ # 保存对象.
363
+ #
364
+ # 如果对象是新对象,那么在数据库中创建。否则,如果对象是数据库中已经存在的对象,那么更新数据库
365
+ #
366
+ # 执行<tt>save!</tt>时,验证总是被运行。只要有任意一个执行失败都将抛出 ActiveObject::ObjectInvalid异常。
367
+ #
368
+ # +save+ 相关联的一系列回调. 如果任何一个<tt>before_*</tt>回调返回 +false+,那么+save+动作被取消并返回 +false+,并且抛出ActiveObject::RecordNotSaved.
369
+ def save!
370
+ create_or_update || raise(ObjectNotSaved)
371
+ end
372
+
373
+
374
+ private
375
+ def attribute_present?(attribute)
376
+ value = read_attribute(attribute)
377
+ !value.blank?
378
+ end
379
+
380
+ def assign_attributes(attributes=nil)
381
+ attributes.each do |key,value|
382
+ self.send "#{key}=",value
383
+ end unless attributes.nil?
384
+ end
385
+
386
+ def create_or_update
387
+ result = new_record? ? create : update
388
+ @destroyed = false
389
+ @new_record = false
390
+ result != false
391
+ end
392
+
393
+ def serialize_attributes
394
+ record = {}
395
+ self.class.attributes.each do |attribute|
396
+ record.merge!({attribute=>(self.send attribute)})
397
+ end
398
+ self.class.serialize_attributes(record)
399
+ end
400
+
401
+ def create
402
+ self.id = self.send self.class.primary_key if self.class.primary_key
403
+ set
404
+ end
405
+
406
+ def update
407
+ set
408
+ end
409
+
410
+ # 保存记录,在适配器中重写
411
+ #def set
412
+ #end
413
+ end
414
+
415
+ end