ant-mapper 0.0.2

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 (52) hide show
  1. data/CHANGE +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +25 -0
  4. data/ant.gemspec +48 -0
  5. data/data.tch +0 -0
  6. data/examples/account.rb +71 -0
  7. data/examples/data.tch +0 -0
  8. data/examples/light_cloud.yml +18 -0
  9. data/examples/user.rb +84 -0
  10. data/init.rb +4 -0
  11. data/lib/ant.rb +7 -0
  12. data/lib/ant_mapper.rb +10 -0
  13. data/lib/ant_mapper/adapters/light_cloud.rb +59 -0
  14. data/lib/ant_mapper/adapters/tokyo_cabinet.rb +42 -0
  15. data/lib/ant_mapper/adapters/tokyo_tyrant.rb +14 -0
  16. data/lib/ant_mapper/base.rb +367 -0
  17. data/lib/ant_mapper/callbacks.rb +180 -0
  18. data/lib/ant_mapper/observer.rb +180 -0
  19. data/lib/ant_mapper/validations.rb +687 -0
  20. data/lib/ant_support.rb +4 -0
  21. data/lib/ant_support/callbacks.rb +303 -0
  22. data/lib/ant_support/core_ext.rb +4 -0
  23. data/lib/ant_support/core_ext/array.rb +5 -0
  24. data/lib/ant_support/core_ext/array/extract_options.rb +20 -0
  25. data/lib/ant_support/core_ext/blank.rb +58 -0
  26. data/lib/ant_support/core_ext/class.rb +3 -0
  27. data/lib/ant_support/core_ext/class/attribute_accessors.rb +54 -0
  28. data/lib/ant_support/core_ext/class/inheritable_attributes.rb +140 -0
  29. data/lib/ant_support/core_ext/class/removal.rb +50 -0
  30. data/lib/ant_support/core_ext/duplicable.rb +43 -0
  31. data/lib/ant_support/core_ext/enumerable.rb +72 -0
  32. data/lib/ant_support/core_ext/hash.rb +6 -0
  33. data/lib/ant_support/core_ext/hash/keys.rb +52 -0
  34. data/lib/ant_support/core_ext/module.rb +16 -0
  35. data/lib/ant_support/core_ext/module/aliasing.rb +74 -0
  36. data/lib/ant_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  37. data/lib/ant_support/core_ext/module/attribute_accessors.rb +58 -0
  38. data/lib/ant_support/core_ext/object.rb +1 -0
  39. data/lib/ant_support/core_ext/object/extending.rb +80 -0
  40. data/lib/ant_support/core_ext/string.rb +7 -0
  41. data/lib/ant_support/core_ext/string/inflections.rb +51 -0
  42. data/spec/case/callbacks_observers_test.rb +38 -0
  43. data/spec/case/callbacks_test.rb +417 -0
  44. data/spec/case/create_object_test.rb +56 -0
  45. data/spec/case/set_class_name_test.rb +17 -0
  46. data/spec/case/validations_test.rb +1482 -0
  47. data/spec/helper.rb +15 -0
  48. data/spec/light_cloud.yml +18 -0
  49. data/spec/model/account.rb +3 -0
  50. data/spec/model/topic.rb +28 -0
  51. data/spec/model/user.rb +4 -0
  52. metadata +125 -0
data/CHANGE ADDED
@@ -0,0 +1,6 @@
1
+ v0.0.1 2009-8-21
2
+ - initial release
3
+
4
+ v0.0.2 2009-8-24
5
+ * 支持Tokyo Cabinet数据库
6
+ * 支持Tokyo Tyrant数据库
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Aaron Zhang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,25 @@
1
+ == Ant是什么?
2
+
3
+ Ant是用来访问LightCloud/TokyoCabinet/TokyoTyrant的工具,实现了持久化数据与对象的映射。
4
+
5
+ == 特性
6
+ * 访问LightCloud/TokyoCabinet/TokyoTyrant。提供一组定义实体类的方法以及访问LightCloud/TokyoCabinet/TokyoTyrant的方法
7
+ * 提供验证规则。对新的或已经存在的实体提供不同的验证规则
8
+ * 提供回调方法。在操作对象(create,save,update等)时,可以使用回调函数
9
+ * 提供观察器模式。
10
+
11
+
12
+ == 示例
13
+
14
+ 在Rails中应用Ant
15
+
16
+ 在环境文件的最后加入下列代码(假设LightCloud配置文件与环境文件在同一个目录下):
17
+
18
+ require 'ant'
19
+ AntMapper::Base.configurations :LC,File.join(File.dirname(__FILE__),'light_cloud.yml')
20
+
21
+ 在项目中使用观察器
22
+
23
+ 在环境文件的最后加入下列代码:
24
+ AntMapper::Base.observers = :account_observer
25
+ AntMapper::Base.instantiate_observers
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module DirExt
3
+ # 根据指定的目录获取该目录下所有的文件路径(不包含隐藏文件)
4
+ # 例如:
5
+ # files_path_to_s('/home/Aaron/sample') => 会列出sample目录下所有文件的路径
6
+ #
7
+ def files_path_to_s(dirname)
8
+ path_array = files_path(dirname)
9
+ path_array.flatten!
10
+ end
11
+
12
+ protected
13
+ def files_path(dirname,path_array=[])
14
+ files = Dir.entries(dirname)
15
+ files.delete('.')
16
+ files.delete('..')
17
+ files.delete_if{|file| file =~ /^\./}
18
+ files.each do |f|
19
+ if File.directory?(File.join(dirname,f))
20
+ path_array << files_path(File.join(dirname,f))
21
+ else
22
+ path_array << File.join(dirname,f)
23
+ end
24
+ end
25
+ path_array
26
+ end
27
+ end
28
+
29
+ Gem::Specification::Class.send :include,DirExt
30
+
31
+ Gem::Specification.new do |s|
32
+ s.name = %{ant-mapper}
33
+ s.version = '0.0.2'
34
+ s.description = 'Ant是用来访问LightCloud/TokyoCabinet/TokyoTyrant的工具,实现了持久化数据与对象的映射。 它类似于ActiveRecord,提供一组访问LightCloud/TokyoCabinet/TokyoTyrant的方法以及验证规则、回调函数和观察器。'
35
+ s.homepage = "http://www.tokyocabinet.com"
36
+ s.rubyforge_project = %q{ant-mapper}
37
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
38
+ s.summary = 'Ant是用来访问LightCloud/TokyoCabinet/TokyoTyrant的工具,实现了持久化数据与对象的映射。 它类似于ActiveRecord,提供一组访问LightCloud/TokyoCabinet/TokyoTyrant的方法以及验证规则、回调函数和观察器。'
39
+
40
+ s.email = ["yalong1976@gmail.com"]
41
+ s.authors = ["yalong zhang"]
42
+ s.files = files_path_to_s(File.dirname(__FILE__))
43
+ s.require_paths = ["lib"]
44
+ s.required_ruby_version = Gem::Requirement.new(">= 1.8.6")
45
+ s.rubygems_version = %q{1.3.4}
46
+ s.add_dependency(%q<mitchellh-lightcloud>)
47
+ s.add_dependency(%q<rufus-tokyo>)
48
+ end
Binary file
@@ -0,0 +1,71 @@
1
+ class Account < AntMapper::Base
2
+ #self.class_name = 'User'
3
+
4
+ set_primary_key :email
5
+ set_attributes :email,:name,:encrypted_password,:salt
6
+
7
+ attr_accessor :password
8
+
9
+ validates_presence_of :email, :message=>'电子邮箱不能为空!'
10
+ validates_format_of :email, :with => /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i, :message => '无效的电子邮件格式'
11
+ validates_presence_of :password,:message =>'密码不能为空'
12
+ validates_length_of :password, :within => 6..40, :too_short => '密码太短', :too_long =>'密码过长'
13
+ validates_confirmation_of :password, :message => '两次输入的密码不匹配!'
14
+
15
+
16
+ before_save :encrypt_password
17
+ before_save :default_name
18
+
19
+ # 通过账号和未加密的密码验证用户. 返回用户或者nil.
20
+ def self.authenticate(email, password)
21
+ u = find(email)
22
+ u && u.authenticated?(password) ? u : nil
23
+ end
24
+
25
+ # 根据salt加密数据.
26
+ def self.encrypt(password, salt)
27
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
28
+ end
29
+
30
+ # 根据用户salt加密用户的密码
31
+ def encrypt(password)
32
+ self.class.encrypt(password, salt)
33
+ end
34
+
35
+ # 校验密码认证是否通过
36
+ def authenticated?(password)
37
+ encrypted_password == encrypt(password)
38
+ end
39
+
40
+ def change_password(password,password_confirmation)
41
+ self.password= password
42
+ self.password_confirmation = password_confirmation
43
+ self.save
44
+ end
45
+
46
+ # 让系统记住当前账号,使下次登录是不需要输入账号和密码。
47
+ def remember_me
48
+ remember_me_for 2.weeks
49
+ end
50
+
51
+ def remember_me_for(time)
52
+ remember_me_until time.from_now.utc
53
+ end
54
+
55
+ def remember_me_until(time)
56
+ Token.create(:email=>self.email,:token=>encrypt("#{email}--#{time}"),:expires_at=>time)
57
+ end
58
+
59
+ protected
60
+ # before filter
61
+ def encrypt_password
62
+ return if password.blank?
63
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
64
+ self.encrypted_password = encrypt(password)
65
+ end
66
+
67
+ def default_name
68
+ return if email.blank?
69
+ self.name = email[0,email.index('@')] if self.name.blank? && email.index('@')
70
+ end
71
+ end
Binary file
@@ -0,0 +1,18 @@
1
+ # LightCloud的配置文件,用于初始化LightCloud
2
+ # 示例
3
+ # user:
4
+ # lookup_one:
5
+ # - 127.0.0.1:41401
6
+ # - 127.0.0.1:41402
7
+ # storage_one:
8
+ # - 127.0.0.1:44401
9
+ # - 127.0.0.1:44402
10
+ #
11
+
12
+ user:
13
+ lookup_one:
14
+ - 127.0.0.1:30001
15
+ - 127.0.0.1:33001
16
+ storage_one:
17
+ - 127.0.0.1:20001
18
+ - 127.0.0.1:24001
@@ -0,0 +1,84 @@
1
+ require File.join(File.dirname(__FILE__),'..','lib','ant')
2
+ require 'pathname'
3
+
4
+
5
+ #AntMapper::Base.configurations = File.join(File.dirname(__FILE__),'light_cloud.yml')
6
+ #AntMapper::Base.configure :TC,File.join(File.dirname(Pathname.new(__FILE__).realpath),'data.tch')
7
+ #AntMapper::Base.configure :LC,File.join(File.dirname(Pathname.new(__FILE__).realpath),'light_cloud.yml')
8
+ AntMapper::Base.configure :TT,'http://127.0.0.1:55555'
9
+ class Account < AntMapper::Base
10
+ #self.class_name = 'User'
11
+
12
+ set_primary_key :email
13
+ set_attributes :email,:name,:encrypted_password,:salt
14
+
15
+ attr_accessor :password
16
+
17
+ validates_presence_of :email, :message=>'电子邮箱不能为空!'
18
+ validates_format_of :email, :with => /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i, :message => '无效的电子邮件格式'
19
+ validates_presence_of :password,:message =>'密码不能为空'
20
+ validates_length_of :password, :within => 6..40, :too_short => '密码太短', :too_long =>'密码过长'
21
+ validates_confirmation_of :password, :message => '两次输入的密码不匹配!'
22
+
23
+
24
+ before_save :encrypt_password
25
+ before_save :default_name
26
+
27
+ # 通过账号和未加密的密码验证用户. 返回用户或者nil.
28
+ def self.authenticate(email, password)
29
+ u = find(email)
30
+ u && u.authenticated?(password) ? u : nil
31
+ end
32
+
33
+ # 根据salt加密数据.
34
+ def self.encrypt(password, salt)
35
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
36
+ end
37
+
38
+ # 根据用户salt加密用户的密码
39
+ def encrypt(password)
40
+ self.class.encrypt(password, salt)
41
+ end
42
+
43
+ # 校验密码认证是否通过
44
+ def authenticated?(password)
45
+ encrypted_password == encrypt(password)
46
+ end
47
+
48
+ def change_password(password,password_confirmation)
49
+ self.password= password
50
+ self.password_confirmation = password_confirmation
51
+ self.save
52
+ end
53
+
54
+ # 让系统记住当前账号,使下次登录是不需要输入账号和密码。
55
+ def remember_me
56
+ remember_me_for 2.weeks
57
+ end
58
+
59
+ def remember_me_for(time)
60
+ remember_me_until time.from_now.utc
61
+ end
62
+
63
+ def remember_me_until(time)
64
+ Token.create(:email=>self.email,:token=>encrypt("#{email}--#{time}"),:expires_at=>time)
65
+ end
66
+
67
+ protected
68
+ # before filter
69
+ def encrypt_password
70
+ return if password.blank?
71
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
72
+ self.encrypted_password = encrypt(password)
73
+ end
74
+
75
+ def default_name
76
+ return if email.blank?
77
+ self.name = email[0,email.index('@')] if self.name.blank? && email.index('@')
78
+ end
79
+ end
80
+
81
+ u = Account.new(:email=>'aaron@nonobo.com',:password=>'111111',:password_confirmation=>'111111')
82
+ u.save
83
+
84
+ p Account.find('aaron@nonobo.com')
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'ant'
@@ -0,0 +1,7 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'lightcloud'
6
+ require 'ant_support'
7
+ require 'ant_mapper'
@@ -0,0 +1,10 @@
1
+ require 'ant_mapper/base'
2
+ require 'ant_mapper/validations'
3
+ require 'ant_mapper/callbacks'
4
+ require 'ant_mapper/observer'
5
+
6
+ AntMapper::Base.class_eval do
7
+ include AntMapper::Validations
8
+ include AntMapper::Callbacks
9
+ include AntMapper::Observing
10
+ end
@@ -0,0 +1,59 @@
1
+ # 访问LightCloud的加载模块
2
+ module AntLightCloud
3
+
4
+ def self.included(base)
5
+ require 'lightcloud'
6
+ base.send "configurations=", YAML.load_file(base.configurations) if base.configurations.is_a?(String)
7
+ base.configurations.each do |key,value|
8
+ lookup_nodes, storage_nodes = LightCloud.generate_nodes(value)
9
+ LightCloud.init(lookup_nodes, storage_nodes,key)
10
+ end
11
+
12
+ base.extend ClassMethods
13
+ base.send :include, InstanceMethods
14
+ end
15
+
16
+ module ClassMethods
17
+ def delete(key)
18
+ LightCloud.delete(key,self.lightcloud_system)
19
+ end
20
+
21
+ # 指定类名,默认是class定义的名字
22
+ def class_name=(value)
23
+ write_inheritable_attribute :class_name,value
24
+ end
25
+
26
+ def class_name
27
+ read_inheritable_attribute(:class_name) || self.to_s
28
+ end
29
+
30
+ # LightCloud系统名,用于存储时指定LightCloud系统
31
+ def lightcloud_system
32
+ class_name.downcase
33
+ end
34
+
35
+ private
36
+ def load(key)
37
+ raw_record = LightCloud.get(key,self.lightcloud_system)
38
+ raise AntMapper::ObjectNotFound unless raw_record
39
+ Marshal.load(raw_record)
40
+ end
41
+ end
42
+
43
+ module InstanceMethods
44
+ private
45
+ def set
46
+ record = {}
47
+ self.class.attributes.each do |attribute|
48
+ record.merge!({attribute=>(self.send attribute)})
49
+ end
50
+ begin
51
+ LightCloud.set(@object_key,Marshal.dump(record),self.class.lightcloud_system)
52
+ true
53
+ rescue
54
+ false
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,42 @@
1
+
2
+ # 访问TokyoCabinet的加载模块
3
+ module AntTokyoCabinet
4
+ def self.included(base)
5
+ require 'rufus/tokyo'
6
+
7
+ base.extend ClassMethods
8
+ base.send :include, InstanceMethods
9
+
10
+ base.send :adb=, Rufus::Tokyo::Cabinet.new(base.configurations)
11
+ end
12
+
13
+ module ClassMethods
14
+ mattr_accessor :adb
15
+ def delete(key)
16
+ adb.delete(key)
17
+ end
18
+
19
+ private
20
+ def load(key)
21
+ raw_record = adb[key]
22
+ raise AntMapper::ObjectNotFound unless raw_record
23
+ Marshal.load(raw_record)
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ private
29
+ def set
30
+ record = {}
31
+ self.class.attributes.each do |attribute|
32
+ record.merge!({attribute=>(self.send attribute)})
33
+ end
34
+ begin
35
+ self.class.adb[@object_key]=Marshal.dump(record)
36
+ true
37
+ rescue
38
+ false
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ # 访问TokyoTyrant的加载模块
2
+ module AntTokyoTyrant
3
+
4
+ def self.included(base)
5
+ require 'uri'
6
+ require 'rufus/tokyo'
7
+ uri = URI.parse(base.configurations)
8
+
9
+ base.extend AntTokyoCabinet::ClassMethods
10
+ base.send :include, AntTokyoCabinet::InstanceMethods
11
+
12
+ base.send :adb=, Rufus::Tokyo::Tyrant.new(uri.host,uri.port)
13
+ end
14
+ end
@@ -0,0 +1,367 @@
1
+ require 'yaml'
2
+ require 'ant_mapper/adapters/light_cloud'
3
+ require 'ant_mapper/adapters/tokyo_cabinet'
4
+ require 'ant_mapper/adapters/tokyo_tyrant'
5
+
6
+
7
+ # 实现访问LightCloud的通用方法
8
+ module AntMapper
9
+ class AntMapperError < StandardError;end
10
+
11
+ class ObjectNotFound < AntMapperError;end
12
+
13
+ class ObjectNotSaved < AntMapperError;end
14
+
15
+ class ConfigError < AntMapperError;end
16
+
17
+ # AntMapper通过set_attributes指定需要持久化的属性,通过set_primary_key指定存取的主键
18
+ #
19
+ # == 描述类
20
+ # AntMapper所有类都是从Base类派生的,在派生新类时需要指定用于存取对象的主关键字和持久化存储的属性.就像下面一样:
21
+ #
22
+ # class User < AntMapper::Base
23
+ # set_primary_key :email
24
+ # set_attributes :name,:email,:encrypt_password
25
+ # end
26
+ # 主关键字可以是由一个属性,也可以是多个属性组成。
27
+ #
28
+ # 如果类名与LightCloud配置文件(RAILS_ROOT/config/light_cloud.yml)中配置的类名不一致时,可以通过class_name属性指定存储的类,例如:
29
+ # class Account < User
30
+ # self.class_name = 'User'
31
+ # end
32
+ #
33
+ # == 新建
34
+ # 可以通过hash参数新建对象,例如:
35
+ # user = User.new(:name => "David", :occupation => "Code Artist")
36
+ # user.name # => "David"
37
+ #
38
+ # 也可以使用块初始化对象:
39
+ #
40
+ # user = User.new do |u|
41
+ # u.name = "David"
42
+ # u.occupation = "Code Artist"
43
+ # end
44
+ #
45
+ # 当然,也可以在新建后对特定的属性赋值:
46
+ #
47
+ # user = User.new
48
+ # user.name = "David"
49
+ # user.occupation = "Code Artist"
50
+ #
51
+ # == 删除
52
+ # 删除主关键字与参数 +key+ 匹配的对象。
53
+ #
54
+ # ==== 参数
55
+ #
56
+ # * +key+ - 对象的主关键字.
57
+ #
58
+ # ==== 例子
59
+ #
60
+ # User.delete('aaron@nonobo.com')
61
+
62
+
63
+ class Base
64
+ write_inheritable_attribute :primary_key,nil
65
+ write_inheritable_attribute :attributes,[]
66
+ write_inheritable_attribute :class_name,nil
67
+
68
+ @@configurations = nil
69
+ @@database_model = nil
70
+
71
+ @@connection = nil
72
+
73
+ class << self
74
+
75
+ # 设置类的主关键字
76
+ # 对象根据设置的主关键字进行持久化存储
77
+ # 示例:
78
+ # class User < AntMapper::Base
79
+ # set_primary_key :email # 表示User对象以email作为主关键字
80
+ # end
81
+ def set_primary_key(*options)
82
+ write_inheritable_attribute :primary_key,options
83
+ end
84
+
85
+ # 定义需要持久化的属性
86
+ # 示例:
87
+ # class User < AntMapper::Base
88
+ # set_attributes :email,:name,:password # 表示email,name,password需要持久化存储
89
+ # end
90
+ def set_attributes(*options)
91
+ options.each do |attribute|
92
+ attr_accessor attribute
93
+ end
94
+ write_inheritable_attribute :attributes,options
95
+ end
96
+
97
+
98
+ def primary_key
99
+ read_inheritable_attribute(:primary_key)
100
+ end
101
+
102
+ def attributes
103
+ read_inheritable_attribute(:attributes)
104
+ end
105
+
106
+ # 配置Ant访问的后台数据库支撑,目前支持LightCloud/TokyoTyrant/TokyoCabinet。
107
+ # 参数 +model+ 用于描述后台数据库的类型。
108
+ # :TC => 表示TokyoCabinet
109
+ # :TT => 表示TokyoTyrant
110
+ # :LC => 表示LightCloud
111
+ #
112
+ # 参数 +config+ 用于接收后台数据库的配置信息。
113
+ #
114
+ # 当model指定为TokyoCabinet数据库时,config为指定的文件名,例如:
115
+ # AntMapper::Base.configure :TC, File.join(File.dirname(__FILE__),'act.tch')
116
+ # 这里需要注意的是指定的文件名后缀符合TokyoCabinet的约定规则。更多信息请参看:http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
117
+ #
118
+ #
119
+ #
120
+ # 当model指定为TokyoTyrant数据库时, config为指定的远程服务器地址,例如:
121
+ # AntMapper::Base.configure :TT, '127.0.0.1:54321'
122
+ #
123
+ # 当model指定为LightCloud数据库时, config可以接收符合配置规则的hash:
124
+ # AntMapper::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']}}
125
+ # 也可以是配置文件的文件名。
126
+ # AntMapper::Base.configure :LC, File.join(File.dirname(__FILE__),'config.yml')
127
+ #
128
+ def configure(model,config)
129
+ @@configurations = config
130
+ @@database_model = model
131
+ case @@database_model
132
+ when :TC
133
+ AntMapper::Base.send :include,AntTokyoCabinet
134
+ when :TT
135
+ AntMapper::Base.send :include,AntTokyoTyrant
136
+ when :LC
137
+ AntMapper::Base.send :include,AntLightCloud
138
+ else
139
+ raise ConfigError,'没有指定数据库类型!'
140
+ end
141
+ end
142
+
143
+ def configurations
144
+ @@configurations
145
+ end
146
+
147
+ def database_model
148
+ @@database_model
149
+ end
150
+
151
+ # 通过主关键字查找对象
152
+ #
153
+ # +key+ 参数表示需要找回的主关键字.
154
+ #
155
+ # ==== Example
156
+ # User.find('aaron@nonobo.com')
157
+ #
158
+ def find(key)
159
+ return nil if key.blank?
160
+ record = load(key)
161
+ record ? instance(key,record) : nil
162
+ end
163
+
164
+ # 新建对象(或多个对象)并保存到数据库(假设都通过验证)
165
+ # 不管是否保存到数据库,都将返回对象.
166
+ #
167
+ # 参数 +attributes+ 可以是Hash或Hash数组.
168
+ #
169
+ # ==== 示例
170
+ # # 创建单个新对象
171
+ # User.create(:first_name => 'Jamie')
172
+ #
173
+ # # 创建多个对象
174
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
175
+ #
176
+ # # 创建对象,并通过块进行其它属性的赋值.
177
+ # User.create(:first_name => 'Jamie') do |u|
178
+ # u.is_admin = false
179
+ # end
180
+ #
181
+ # # 创建多个对象,并通过块进行其它属性的赋值,每个对象都会执行块:
182
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
183
+ # u.is_admin = false
184
+ # end
185
+ def create(attributes = nil, &block)
186
+ if attributes.is_a?(Array)
187
+ attributes.collect { |attr| create(attr, &block) }
188
+ else
189
+ object = new(attributes)
190
+ yield(object) if block_given?
191
+ object.save
192
+ object
193
+ end
194
+ end
195
+
196
+
197
+ # 删除主关键字与参数 +key+ 匹配的对象,并且在删除之前执行所有的回调和过滤。
198
+ #
199
+ # ==== 参数
200
+ #
201
+ # * +key+ - 对象的主关键字.
202
+ #
203
+ # ==== 示例
204
+ # User.destroy('aaron@nonobo.com')
205
+ def destroy(key)
206
+ find(key).destroy
207
+ end
208
+
209
+ def base_class
210
+ class_of_ant_mapper_descendant(self)
211
+ end
212
+
213
+ def self_and_descendents_from_ant_mapper#nodoc:
214
+ klass = self
215
+ classes = [klass]
216
+ while klass != klass.base_class
217
+ classes << klass = klass.superclass
218
+ end
219
+ classes
220
+ rescue
221
+ [self]
222
+ end
223
+
224
+ protected
225
+
226
+
227
+ def class_of_ant_mapper_descendant(klass)
228
+ if klass.superclass == Base
229
+ klass
230
+ elsif klass.superclass.nil?
231
+ raise AntMapperError, "#{name} doesn't belong in a hierarchy descending from AntMapper"
232
+ else
233
+ class_of_ant_mapper_descendant(klass.superclass)
234
+ end
235
+ end
236
+
237
+ def configurations=(value)
238
+ @@configurations= value
239
+ end
240
+
241
+ def database_model=(value)
242
+ @@database_model = value
243
+ end
244
+
245
+ private
246
+ # 构建一个对象实例
247
+ def instance(object_key,record)
248
+ result = new(record)
249
+ result.send "object_key=",object_key
250
+ result
251
+ end
252
+
253
+ end
254
+
255
+ def initialize(attributes = nil)
256
+ assign_attributes(attributes)
257
+ result = yield self if block_given?
258
+ result
259
+ end
260
+
261
+ # 如果对象没有被存储,表明对象还是新记录,返回true,否则返回false.
262
+ def new_record?
263
+ @object_key ? false : true
264
+ end
265
+
266
+ # 如果对象已经被删除,返回true,否则返回false。
267
+ def destroyed?
268
+ @destroyed || false
269
+ end
270
+
271
+ def object_key
272
+ @object_key
273
+ end
274
+
275
+ # 从数据库中删除对象
276
+ # 不像 #destroy, 这个方法不运行 +before_delete+ 和 +after_delete+ 回调.
277
+ def delete
278
+ self.class.delete(object_key) unless new_record?
279
+ @destroyed = true
280
+ end
281
+
282
+ def destroy
283
+ delete
284
+ end
285
+
286
+ # 从数据库中重新加载对象的属性
287
+ def reload
288
+ #raise "Object is deleted" if destroyed?
289
+ unless new_record?
290
+ begin
291
+ attributes = self.class.send :load, self.object_key
292
+ assign_attributes(attributes)
293
+ true
294
+ rescue
295
+ false
296
+ end
297
+ end
298
+ end
299
+
300
+ # :call-seq:
301
+ # save(perform_validation = true)
302
+ #
303
+ # 保存对象.
304
+ #
305
+ # 如果对象是新对象,那么在数据库中创建。否则,如果对象是数据库中已经存在的对象,那么更新数据库
306
+ #
307
+ # 如果 +perform_validation+ 是true,将执行验证。如果验证失败,那么动作被取消,并且返回false。
308
+ #
309
+ # +save+ 相关联的一系列回调. 如果任何一个<tt>before_*</tt>回调返回 +false+,那么+save+动作被取消并返回 +false+.
310
+ def save
311
+ create_or_update
312
+ end
313
+
314
+ # 保存对象.
315
+ #
316
+ # 如果对象是新对象,那么在数据库中创建。否则,如果对象是数据库中已经存在的对象,那么更新数据库
317
+ #
318
+ # 执行<tt>save!</tt>时,验证总是被运行。只要有任意一个执行失败都将抛出 ActiveObject::ObjectInvalid异常。
319
+ #
320
+ # +save+ 相关联的一系列回调. 如果任何一个<tt>before_*</tt>回调返回 +false+,那么+save+动作被取消并返回 +false+,并且抛出ActiveObject::RecordNotSaved.
321
+ def save!
322
+ create_or_update || raise(ObjectNotSaved)
323
+ end
324
+
325
+
326
+ private
327
+ def primary_key_to_s
328
+ keys = self.class.primary_key.is_a?(Array) ? self.class.primary_key : [self.class.primary_key]
329
+ key_string = ""
330
+ keys.each do |key|
331
+ key_string << self.send(key).to_s
332
+ end
333
+ key_string
334
+ end
335
+
336
+ def attribute_present?(attribute)
337
+ value = read_attribute(attribute)
338
+ !value.blank?
339
+ end
340
+
341
+ def object_key=(value)
342
+ @object_key = value
343
+ end
344
+
345
+ def assign_attributes(attributes=nil)
346
+ attributes.each do |key,value|
347
+ self.send "#{key}=",value
348
+ end unless attributes.nil?
349
+ end
350
+
351
+ def create_or_update
352
+ result = new_record? ? create : update
353
+ @destroyed = false
354
+ result != false
355
+ end
356
+
357
+ def create
358
+ @object_key = primary_key_to_s
359
+ set
360
+ end
361
+
362
+ def update
363
+ set
364
+ end
365
+ end
366
+
367
+ end