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