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.
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