sequel 0.4.5 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5 -1
- data/README +5 -175
- data/Rakefile +11 -16
- data/lib/sequel/model.rb +3 -320
- data/spec/dataset_spec.rb +3 -4
- metadata +5 -24
- data/lib/sequel/model/base.rb +0 -97
- data/lib/sequel/model/caching.rb +0 -42
- data/lib/sequel/model/hooks.rb +0 -122
- data/lib/sequel/model/plugins.rb +0 -44
- data/lib/sequel/model/record.rb +0 -309
- data/lib/sequel/model/relations.rb +0 -107
- data/lib/sequel/model/schema.rb +0 -52
- data/lib/sequel/model/validations.rb +0 -117
- data/spec/model/base_spec.rb +0 -148
- data/spec/model/caching_spec.rb +0 -148
- data/spec/model/hooks_spec.rb +0 -105
- data/spec/model/plugins_spec.rb +0 -59
- data/spec/model/record_spec.rb +0 -360
- data/spec/model/relations_spec.rb +0 -148
- data/spec/model/schema_spec.rb +0 -80
- data/spec/model/validations_spec.rb +0 -292
- data/spec/model_spec.rb +0 -576
data/spec/dataset_spec.rb
CHANGED
@@ -136,15 +136,14 @@ context "A simple dataset" do
|
|
136
136
|
dbb = Sequel::Database.new
|
137
137
|
|
138
138
|
@c = Class.new(Sequel::Model) do
|
139
|
-
|
140
|
-
set_dataset Sequel::Dataset.new(dbb)
|
139
|
+
attr_accessor :values
|
141
140
|
end
|
142
141
|
|
143
|
-
v = @c.new
|
142
|
+
v = @c.new; v.values = {:a => 1}
|
144
143
|
|
145
144
|
@dataset.insert_sql(v).should == "INSERT INTO test (a) VALUES (1)"
|
146
145
|
|
147
|
-
v = @c.new
|
146
|
+
v = @c.new; v.values = {}
|
148
147
|
@dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
|
149
148
|
end
|
150
149
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: "0.5"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2007-12-
|
12
|
+
date: 2007-12-30 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: "0"
|
41
41
|
version:
|
42
|
-
description:
|
42
|
+
description: Database access for Ruby
|
43
43
|
email: ciconia@gmail.com
|
44
44
|
executables:
|
45
45
|
- sequel
|
@@ -67,16 +67,6 @@ files:
|
|
67
67
|
- spec/database_spec.rb
|
68
68
|
- spec/dataset_spec.rb
|
69
69
|
- spec/migration_spec.rb
|
70
|
-
- spec/model
|
71
|
-
- spec/model/base_spec.rb
|
72
|
-
- spec/model/caching_spec.rb
|
73
|
-
- spec/model/hooks_spec.rb
|
74
|
-
- spec/model/plugins_spec.rb
|
75
|
-
- spec/model/record_spec.rb
|
76
|
-
- spec/model/relations_spec.rb
|
77
|
-
- spec/model/schema_spec.rb
|
78
|
-
- spec/model/validations_spec.rb
|
79
|
-
- spec/model_spec.rb
|
80
70
|
- spec/pretty_table_spec.rb
|
81
71
|
- spec/rcov.opts
|
82
72
|
- spec/schema_generator_spec.rb
|
@@ -116,15 +106,6 @@ files:
|
|
116
106
|
- lib/sequel/exceptions.rb
|
117
107
|
- lib/sequel/informix.rb
|
118
108
|
- lib/sequel/migration.rb
|
119
|
-
- lib/sequel/model
|
120
|
-
- lib/sequel/model/base.rb
|
121
|
-
- lib/sequel/model/caching.rb
|
122
|
-
- lib/sequel/model/hooks.rb
|
123
|
-
- lib/sequel/model/plugins.rb
|
124
|
-
- lib/sequel/model/record.rb
|
125
|
-
- lib/sequel/model/relations.rb
|
126
|
-
- lib/sequel/model/schema.rb
|
127
|
-
- lib/sequel/model/validations.rb
|
128
109
|
- lib/sequel/model.rb
|
129
110
|
- lib/sequel/mysql.rb
|
130
111
|
- lib/sequel/odbc.rb
|
@@ -145,7 +126,7 @@ post_install_message:
|
|
145
126
|
rdoc_options:
|
146
127
|
- --quiet
|
147
128
|
- --title
|
148
|
-
- "Sequel:
|
129
|
+
- "Sequel: Database access for Ruby"
|
149
130
|
- --opname
|
150
131
|
- index.html
|
151
132
|
- --line-numbers
|
@@ -176,6 +157,6 @@ rubyforge_project: sequel
|
|
176
157
|
rubygems_version: 1.0.1
|
177
158
|
signing_key:
|
178
159
|
specification_version: 2
|
179
|
-
summary:
|
160
|
+
summary: Database access for Ruby
|
180
161
|
test_files: []
|
181
162
|
|
data/lib/sequel/model/base.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
class Model
|
3
|
-
# Returns the database associated with the Model class.
|
4
|
-
def self.db
|
5
|
-
@db ||= (superclass != Object) && superclass.db or
|
6
|
-
raise Error, "No database associated with #{self}"
|
7
|
-
end
|
8
|
-
|
9
|
-
# Sets the database associated with the Model class.
|
10
|
-
def self.db=(db)
|
11
|
-
@db = db
|
12
|
-
end
|
13
|
-
|
14
|
-
# Called when a database is opened in order to automatically associate the
|
15
|
-
# first opened database with model classes.
|
16
|
-
def self.database_opened(db)
|
17
|
-
@db = db if (self == Model) && !@db
|
18
|
-
end
|
19
|
-
|
20
|
-
# Returns the dataset associated with the Model class.
|
21
|
-
def self.dataset
|
22
|
-
@dataset || super_dataset or
|
23
|
-
raise Error, "No dataset associated with #{self}"
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.super_dataset # :nodoc:
|
27
|
-
superclass.dataset if superclass and superclass.respond_to? :dataset
|
28
|
-
end
|
29
|
-
|
30
|
-
# Returns the columns in the result set in their original order.
|
31
|
-
#
|
32
|
-
# See Dataset#columns for more information.
|
33
|
-
def self.columns
|
34
|
-
@columns ||= @dataset.columns or
|
35
|
-
raise Error, "Could not fetch columns for #{self}"
|
36
|
-
end
|
37
|
-
|
38
|
-
# Sets the dataset associated with the Model class.
|
39
|
-
def self.set_dataset(ds)
|
40
|
-
@db = ds.db
|
41
|
-
@dataset = ds
|
42
|
-
@dataset.set_model(self)
|
43
|
-
@dataset.transform(@transform) if @transform
|
44
|
-
end
|
45
|
-
|
46
|
-
# Returns the database assoiated with the object's Model class.
|
47
|
-
def db
|
48
|
-
@db ||= model.db
|
49
|
-
end
|
50
|
-
|
51
|
-
# Returns the dataset assoiated with the object's Model class.
|
52
|
-
#
|
53
|
-
# See Dataset for more information.
|
54
|
-
def dataset
|
55
|
-
model.dataset
|
56
|
-
end
|
57
|
-
|
58
|
-
# Returns the columns associated with the object's Model class.
|
59
|
-
def columns
|
60
|
-
model.columns
|
61
|
-
end
|
62
|
-
|
63
|
-
# Serializes column with YAML or through marshalling.
|
64
|
-
def self.serialize(*columns)
|
65
|
-
format = columns.pop[:format] if Hash === columns.last
|
66
|
-
format ||= :yaml
|
67
|
-
|
68
|
-
@transform = columns.inject({}) do |m, c|
|
69
|
-
m[c] = format
|
70
|
-
m
|
71
|
-
end
|
72
|
-
@dataset.transform(@transform) if @dataset
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Lets you create a Model class with its table name already set or reopen
|
77
|
-
# an existing Model.
|
78
|
-
#
|
79
|
-
# Makes given dataset inherited.
|
80
|
-
#
|
81
|
-
# === Example:
|
82
|
-
# class Comment < Sequel::Model(:comments)
|
83
|
-
# table_name # => :comments
|
84
|
-
#
|
85
|
-
# # ...
|
86
|
-
#
|
87
|
-
# end
|
88
|
-
def self.Model(source)
|
89
|
-
@models ||= {}
|
90
|
-
@models[source] ||= Class.new(Sequel::Model) do
|
91
|
-
meta_def(:inherited) do |c|
|
92
|
-
c.set_dataset(source.is_a?(Dataset) ? source : c.db[source])
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
data/lib/sequel/model/caching.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
class Model
|
3
|
-
def self.set_cache(store, opts = {})
|
4
|
-
@cache_store = store
|
5
|
-
if (ttl = opts[:ttl])
|
6
|
-
set_cache_ttl(ttl)
|
7
|
-
end
|
8
|
-
|
9
|
-
meta_def(:[]) do |*args|
|
10
|
-
if (args.size == 1) && (Hash === (h = args.first))
|
11
|
-
return dataset[h]
|
12
|
-
end
|
13
|
-
|
14
|
-
unless obj = @cache_store.get(cache_key_from_values(args))
|
15
|
-
obj = dataset[primary_key_hash((args.size == 1) ? args.first : args)]
|
16
|
-
@cache_store.set(cache_key_from_values(args), obj, cache_ttl)
|
17
|
-
end
|
18
|
-
obj
|
19
|
-
end
|
20
|
-
|
21
|
-
class_def(:set) {|v| store.delete(cache_key); super}
|
22
|
-
class_def(:save) {store.delete(cache_key); super}
|
23
|
-
class_def(:delete) {store.delete(cache_key); super}
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.set_cache_ttl(ttl)
|
27
|
-
@cache_ttl = ttl
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.cache_store
|
31
|
-
@cache_store
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.cache_ttl
|
35
|
-
@cache_ttl ||= 3600
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.cache_key_from_values(values)
|
39
|
-
"#{self}:#{values.join(',')}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/sequel/model/hooks.rb
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
class Model
|
3
|
-
# This Hash translates verbs to methodnames used in chain manipulation
|
4
|
-
# methods.
|
5
|
-
VERB_TO_METHOD = {:prepend => :unshift, :append => :push}
|
6
|
-
|
7
|
-
# Returns @hooks which is an instance of Hash with its hook identifier
|
8
|
-
# (Symbol) as key and the chain of hooks (Array) as value.
|
9
|
-
#
|
10
|
-
# If it is not already set it'll be with an empty set of hooks.
|
11
|
-
# This behaviour will change in the future to allow inheritance.
|
12
|
-
#
|
13
|
-
# For the time being, you should be able to do:
|
14
|
-
#
|
15
|
-
# class A < Sequel::Model(:a)
|
16
|
-
# before_save { 'Do something...' }
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# class B < A
|
20
|
-
# @hooks = superclass.hooks.clone
|
21
|
-
# before_save # => [#<Proc:0x0000c6e8@(example.rb):123>]
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# In this case you should remember that the clone doesn't create any new
|
25
|
-
# instances of your chains, so if you change the chain here it changes in
|
26
|
-
# its superclass, too.
|
27
|
-
def self.hooks
|
28
|
-
@hooks ||= Hash.new { |h, k| h[k] = [] }
|
29
|
-
end
|
30
|
-
|
31
|
-
# Adds block to chain of Hooks for <tt>:before_save</tt>.
|
32
|
-
# It can either be prepended (default) or appended.
|
33
|
-
#
|
34
|
-
# Returns the chain itself.
|
35
|
-
#
|
36
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
37
|
-
def self.before_save(verb = :prepend, &block)
|
38
|
-
hooks[:before_save].send VERB_TO_METHOD.fetch(verb), block if block
|
39
|
-
hooks[:before_save]
|
40
|
-
end
|
41
|
-
# Adds block to chain of Hooks for <tt>:before_create</tt>.
|
42
|
-
# It can either be prepended (default) or appended.
|
43
|
-
#
|
44
|
-
# Returns the chain itself.
|
45
|
-
#
|
46
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
47
|
-
def self.before_create(verb = :prepend, &block)
|
48
|
-
hooks[:before_create].send VERB_TO_METHOD.fetch(verb), block if block
|
49
|
-
hooks[:before_create]
|
50
|
-
end
|
51
|
-
# Adds block to chain of Hooks for <tt>:before_update</tt>.
|
52
|
-
# It can either be prepended (default) or appended.
|
53
|
-
#
|
54
|
-
# Returns the chain itself.
|
55
|
-
#
|
56
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
57
|
-
def self.before_update(verb = :prepend, &block)
|
58
|
-
hooks[:before_update].send VERB_TO_METHOD.fetch(verb), block if block
|
59
|
-
hooks[:before_update]
|
60
|
-
end
|
61
|
-
# Adds block to chain of Hooks for <tt>:before_destroy</tt>.
|
62
|
-
# It can either be prepended (default) or appended.
|
63
|
-
#
|
64
|
-
# Returns the chain itself.
|
65
|
-
#
|
66
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
67
|
-
def self.before_destroy(verb = :prepend, &block)
|
68
|
-
hooks[:before_destroy].send VERB_TO_METHOD.fetch(verb), block if block
|
69
|
-
hooks[:before_destroy]
|
70
|
-
end
|
71
|
-
|
72
|
-
# Adds block to chain of Hooks for <tt>:after_save</tt>.
|
73
|
-
# It can either be prepended or appended (default).
|
74
|
-
#
|
75
|
-
# Returns the chain itself.
|
76
|
-
#
|
77
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
78
|
-
def self.after_save(verb = :append, &block)
|
79
|
-
hooks[:after_save].send VERB_TO_METHOD.fetch(verb), block if block
|
80
|
-
hooks[:after_save]
|
81
|
-
end
|
82
|
-
# Adds block to chain of Hooks for <tt>:after_create</tt>.
|
83
|
-
# It can either be prepended or appended (default).
|
84
|
-
#
|
85
|
-
# Returns the chain itself.
|
86
|
-
#
|
87
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
88
|
-
def self.after_create(verb = :append, &block)
|
89
|
-
hooks[:after_create].send VERB_TO_METHOD.fetch(verb), block if block
|
90
|
-
hooks[:after_create]
|
91
|
-
end
|
92
|
-
# Adds block to chain of Hooks for <tt>:after_update</tt>.
|
93
|
-
# It can either be prepended or appended (default).
|
94
|
-
#
|
95
|
-
# Returns the chain itself.
|
96
|
-
#
|
97
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
98
|
-
def self.after_update(verb = :append, &block)
|
99
|
-
hooks[:after_update].send VERB_TO_METHOD.fetch(verb), block if block
|
100
|
-
hooks[:after_update]
|
101
|
-
end
|
102
|
-
# Adds block to chain of Hooks for <tt>:after_destroy</tt>.
|
103
|
-
# It can either be prepended or appended (default).
|
104
|
-
#
|
105
|
-
# Returns the chain itself.
|
106
|
-
#
|
107
|
-
# Valid verbs are <tt>:prepend</tt> and <tt>:append</tt>.
|
108
|
-
def self.after_destroy(verb = :append, &block)
|
109
|
-
hooks[:after_destroy].send VERB_TO_METHOD.fetch(verb), block if block
|
110
|
-
hooks[:after_destroy]
|
111
|
-
end
|
112
|
-
|
113
|
-
# Evaluates specified chain of Hooks through <tt>instance_eval</tt>.
|
114
|
-
def run_hooks(key)
|
115
|
-
model.hooks[key].each {|h| instance_eval(&h)}
|
116
|
-
end
|
117
|
-
|
118
|
-
def self.has_hooks?(key)
|
119
|
-
hooks[key] && !hooks[key].empty?
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
data/lib/sequel/model/plugins.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
module Plugins; end
|
3
|
-
|
4
|
-
class Model
|
5
|
-
class << self
|
6
|
-
# Loads a plugin for use with the model class, passing optional arguments
|
7
|
-
# to the plugin.
|
8
|
-
def is(plugin, *args)
|
9
|
-
m = plugin_module(plugin)
|
10
|
-
if m.respond_to?(:apply)
|
11
|
-
m.apply(self, *args)
|
12
|
-
end
|
13
|
-
if m.const_defined?("InstanceMethods")
|
14
|
-
class_def(:"#{plugin}_opts") {args.first}
|
15
|
-
include(m::InstanceMethods)
|
16
|
-
end
|
17
|
-
if m.const_defined?("ClassMethods")
|
18
|
-
meta_def(:"#{plugin}_opts") {args.first}
|
19
|
-
metaclass.send(:include, m::ClassMethods)
|
20
|
-
end
|
21
|
-
if m.const_defined?("DatasetMethods")
|
22
|
-
dataset.meta_def(:"#{plugin}_opts") {args.first}
|
23
|
-
dataset.metaclass.send(:include, m::DatasetMethods)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
alias_method :is_a, :is
|
27
|
-
|
28
|
-
# Returns the module for the specified plugin. If the module is not
|
29
|
-
# defined, the corresponding plugin gem is automatically loaded.
|
30
|
-
def plugin_module(plugin)
|
31
|
-
module_name = plugin.to_s.gsub(/(^|_)(.)/) {$2.upcase}
|
32
|
-
if not Sequel::Plugins.const_defined?(module_name)
|
33
|
-
require plugin_gem(plugin)
|
34
|
-
end
|
35
|
-
Sequel::Plugins.const_get(module_name)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Returns the gem name for the given plugin.
|
39
|
-
def plugin_gem(plugin)
|
40
|
-
"sequel_#{plugin}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
data/lib/sequel/model/record.rb
DELETED
@@ -1,309 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
class Model
|
3
|
-
attr_reader :values
|
4
|
-
attr_reader :changed_columns
|
5
|
-
|
6
|
-
# Returns value of attribute.
|
7
|
-
def [](column)
|
8
|
-
@values[column]
|
9
|
-
end
|
10
|
-
# Sets value of attribute and marks the column as changed.
|
11
|
-
def []=(column, value)
|
12
|
-
@values[column] = value
|
13
|
-
@changed_columns << column unless @changed_columns.include?(column)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Enumerates through all attributes.
|
17
|
-
#
|
18
|
-
# === Example:
|
19
|
-
# Ticket.find(7).each { |k, v| puts "#{k} => #{v}" }
|
20
|
-
def each(&block)
|
21
|
-
@values.each(&block)
|
22
|
-
end
|
23
|
-
# Returns attribute names.
|
24
|
-
def keys
|
25
|
-
@values.keys
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns value for <tt>:id</tt> attribute.
|
29
|
-
def id
|
30
|
-
@values[:id]
|
31
|
-
end
|
32
|
-
|
33
|
-
# Compares model instances by values.
|
34
|
-
def ==(obj)
|
35
|
-
(obj.class == model) && (obj.values == @values)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Compares model instances by pkey.
|
39
|
-
def ===(obj)
|
40
|
-
(obj.class == model) && (obj.pk == pk)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Returns key for primary key.
|
44
|
-
def self.primary_key
|
45
|
-
:id
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns primary key attribute hash.
|
49
|
-
def self.primary_key_hash(value)
|
50
|
-
{:id => value}
|
51
|
-
end
|
52
|
-
|
53
|
-
# Sets primary key, regular and composite are possible.
|
54
|
-
#
|
55
|
-
# == Example:
|
56
|
-
# class Tagging < Sequel::Model(:taggins)
|
57
|
-
# # composite key
|
58
|
-
# set_primary_key :taggable_id, :tag_id
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# class Person < Sequel::Model(:person)
|
62
|
-
# # regular key
|
63
|
-
# set_primary_key :person_id
|
64
|
-
# end
|
65
|
-
#
|
66
|
-
# <i>You can even set it to nil!</i>
|
67
|
-
def self.set_primary_key(*key)
|
68
|
-
# if k is nil, we go to no_primary_key
|
69
|
-
if key.empty? || (key.size == 1 && key.first == nil)
|
70
|
-
return no_primary_key
|
71
|
-
end
|
72
|
-
|
73
|
-
# backwards compat
|
74
|
-
key = (key.length == 1) ? key[0] : key.flatten
|
75
|
-
|
76
|
-
# redefine primary_key
|
77
|
-
meta_def(:primary_key) {key}
|
78
|
-
|
79
|
-
unless key.is_a? Array # regular primary key
|
80
|
-
class_def(:this) do
|
81
|
-
@this ||= dataset.filter(key => @values[key]).limit(1).naked
|
82
|
-
end
|
83
|
-
class_def(:pk) do
|
84
|
-
@pk ||= @values[key]
|
85
|
-
end
|
86
|
-
class_def(:pk_hash) do
|
87
|
-
@pk ||= {key => @values[key]}
|
88
|
-
end
|
89
|
-
class_def(:cache_key) do
|
90
|
-
pk = @values[key] || (raise Error, 'no primary key for this record')
|
91
|
-
@cache_key ||= "#{self.class}:#{pk}"
|
92
|
-
end
|
93
|
-
meta_def(:primary_key_hash) do |v|
|
94
|
-
{key => v}
|
95
|
-
end
|
96
|
-
else # composite key
|
97
|
-
exp_list = key.map {|k| "#{k.inspect} => @values[#{k.inspect}]"}
|
98
|
-
block = eval("proc {@this ||= self.class.dataset.filter(#{exp_list.join(',')}).limit(1).naked}")
|
99
|
-
class_def(:this, &block)
|
100
|
-
|
101
|
-
exp_list = key.map {|k| "@values[#{k.inspect}]"}
|
102
|
-
block = eval("proc {@pk ||= [#{exp_list.join(',')}]}")
|
103
|
-
class_def(:pk, &block)
|
104
|
-
|
105
|
-
exp_list = key.map {|k| "#{k.inspect} => @values[#{k.inspect}]"}
|
106
|
-
block = eval("proc {@this ||= {#{exp_list.join(',')}}}")
|
107
|
-
class_def(:pk_hash, &block)
|
108
|
-
|
109
|
-
exp_list = key.map {|k| '#{@values[%s]}' % k.inspect}.join(',')
|
110
|
-
block = eval('proc {@cache_key ||= "#{self.class}:%s"}' % exp_list)
|
111
|
-
class_def(:cache_key, &block)
|
112
|
-
|
113
|
-
meta_def(:primary_key_hash) do |v|
|
114
|
-
key.inject({}) {|m, i| m[i] = v.shift; m}
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def self.no_primary_key #:nodoc:
|
120
|
-
meta_def(:primary_key) {nil}
|
121
|
-
meta_def(:primary_key_hash) {|v| raise Error, "#{self} does not have a primary key"}
|
122
|
-
class_def(:this) {raise Error, "No primary key is associated with this model"}
|
123
|
-
class_def(:pk) {raise Error, "No primary key is associated with this model"}
|
124
|
-
class_def(:pk_hash) {raise Error, "No primary key is associated with this model"}
|
125
|
-
class_def(:cache_key) {raise Error, "No primary key is associated with this model"}
|
126
|
-
end
|
127
|
-
|
128
|
-
# Creates new instance with values set to passed-in Hash ensuring that
|
129
|
-
# new? returns true.
|
130
|
-
def self.create(values = {})
|
131
|
-
db.transaction do
|
132
|
-
obj = new(values, true)
|
133
|
-
obj.save
|
134
|
-
obj
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
class << self
|
139
|
-
def create_with_params(params)
|
140
|
-
create(params.reject {|k, v| !columns.include?(k.to_sym)})
|
141
|
-
end
|
142
|
-
alias_method :create_with, :create_with_params
|
143
|
-
end
|
144
|
-
|
145
|
-
# Returns (naked) dataset bound to current instance.
|
146
|
-
def this
|
147
|
-
@this ||= self.class.dataset.filter(:id => @values[:id]).limit(1).naked
|
148
|
-
end
|
149
|
-
|
150
|
-
# Returns a key unique to the underlying record for caching
|
151
|
-
def cache_key
|
152
|
-
pk = @values[:id] || (raise Error, 'no primary key for this record')
|
153
|
-
@cache_key ||= "#{self.class}:#{pk}"
|
154
|
-
end
|
155
|
-
|
156
|
-
# Returns primary key column(s) for object's Model class.
|
157
|
-
def primary_key
|
158
|
-
@primary_key ||= self.class.primary_key
|
159
|
-
end
|
160
|
-
|
161
|
-
# Returns value for primary key.
|
162
|
-
def pkey
|
163
|
-
warn "Model#pkey is deprecated. Please use Model#pk instead."
|
164
|
-
@pkey ||= @values[self.class.primary_key]
|
165
|
-
end
|
166
|
-
|
167
|
-
# Returns the primary key value identifying the model instance. Stock implementation.
|
168
|
-
def pk
|
169
|
-
@pk ||= @values[:id]
|
170
|
-
end
|
171
|
-
|
172
|
-
# Returns a hash identifying the model instance. Stock implementation.
|
173
|
-
def pk_hash
|
174
|
-
@pk_hash ||= {:id => @values[:id]}
|
175
|
-
end
|
176
|
-
|
177
|
-
# Creates new instance with values set to passed-in Hash.
|
178
|
-
#
|
179
|
-
# This method guesses whether the record exists when
|
180
|
-
# <tt>new_record</tt> is set to false.
|
181
|
-
def initialize(values = {}, new_record = false, &block)
|
182
|
-
@values = values
|
183
|
-
@changed_columns = []
|
184
|
-
|
185
|
-
@new = new_record
|
186
|
-
unless @new # determine if it's a new record
|
187
|
-
k = self.class.primary_key
|
188
|
-
# if there's no primary key for the model class, or
|
189
|
-
# @values doesn't contain a primary key value, then
|
190
|
-
# we regard this instance as new.
|
191
|
-
@new = (k == nil) || (!(Array === k) && !@values[k])
|
192
|
-
end
|
193
|
-
|
194
|
-
block[self] if block
|
195
|
-
end
|
196
|
-
|
197
|
-
# Returns true if the current instance represents a new record.
|
198
|
-
def new?
|
199
|
-
@new
|
200
|
-
end
|
201
|
-
alias :new_record? :new?
|
202
|
-
|
203
|
-
# Returns true when current instance exists, false otherwise.
|
204
|
-
def exists?
|
205
|
-
this.count > 0
|
206
|
-
end
|
207
|
-
|
208
|
-
# Creates or updates the associated record. This method can also
|
209
|
-
# accept a list of specific columns to update.
|
210
|
-
def save(*columns)
|
211
|
-
run_hooks(:before_save)
|
212
|
-
if @new
|
213
|
-
run_hooks(:before_create)
|
214
|
-
iid = model.dataset.insert(@values)
|
215
|
-
# if we have a regular primary key and it's not set in @values,
|
216
|
-
# we assume it's the last inserted id
|
217
|
-
if (pk = primary_key) && !(Array === pk) && !@values[pk]
|
218
|
-
@values[pk] = iid
|
219
|
-
end
|
220
|
-
if pk
|
221
|
-
@this = nil # remove memoized this dataset
|
222
|
-
refresh
|
223
|
-
end
|
224
|
-
@new = false
|
225
|
-
run_hooks(:after_create)
|
226
|
-
else
|
227
|
-
run_hooks(:before_update)
|
228
|
-
if columns.empty?
|
229
|
-
this.update(@values)
|
230
|
-
@changed_columns = []
|
231
|
-
else # update only the specified columns
|
232
|
-
this.update(@values.reject {|k, v| !columns.include?(k)})
|
233
|
-
@changed_columns.reject! {|c| columns.include?(c)}
|
234
|
-
end
|
235
|
-
run_hooks(:after_update)
|
236
|
-
end
|
237
|
-
run_hooks(:after_save)
|
238
|
-
self
|
239
|
-
end
|
240
|
-
|
241
|
-
# Saves only changed columns or does nothing if no columns are marked as
|
242
|
-
# chanaged.
|
243
|
-
def save_changes
|
244
|
-
save(*@changed_columns) unless @changed_columns.empty?
|
245
|
-
end
|
246
|
-
|
247
|
-
# Updates and saves values to database from the passed-in Hash.
|
248
|
-
def set(values)
|
249
|
-
this.update(values)
|
250
|
-
values.each {|k, v| @values[k] = v}
|
251
|
-
end
|
252
|
-
alias_method :update, :set
|
253
|
-
|
254
|
-
# Reloads values from database and returns self.
|
255
|
-
def refresh
|
256
|
-
@values = this.first || raise(Error, "Record not found")
|
257
|
-
self
|
258
|
-
end
|
259
|
-
|
260
|
-
# Like delete but runs hooks before and after delete.
|
261
|
-
def destroy
|
262
|
-
db.transaction do
|
263
|
-
run_hooks(:before_destroy)
|
264
|
-
delete
|
265
|
-
run_hooks(:after_destroy)
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
# Deletes and returns self.
|
270
|
-
def delete
|
271
|
-
this.delete
|
272
|
-
self
|
273
|
-
end
|
274
|
-
|
275
|
-
ATTR_RE = /^([a-zA-Z_]\w*)(=)?$/.freeze
|
276
|
-
|
277
|
-
def method_missing(m, *args) #:nodoc:
|
278
|
-
if m.to_s =~ ATTR_RE
|
279
|
-
att = $1.to_sym
|
280
|
-
write = $2 == '='
|
281
|
-
|
282
|
-
# check whether the column is legal
|
283
|
-
unless columns.include?(att)
|
284
|
-
# if read accessor and a value exists for the column, we return it
|
285
|
-
if !write && @values.has_key?(att)
|
286
|
-
return @values[att]
|
287
|
-
end
|
288
|
-
|
289
|
-
# otherwise, raise an error
|
290
|
-
raise Error, "Invalid column (#{att.inspect}) for #{self}"
|
291
|
-
end
|
292
|
-
|
293
|
-
# define the column accessor
|
294
|
-
Thread.exclusive do
|
295
|
-
if write
|
296
|
-
model.class_def(m) {|v| self[att] = v}
|
297
|
-
else
|
298
|
-
model.class_def(m) {self[att]}
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
# call the accessor
|
303
|
-
respond_to?(m) ? send(m, *args) : super(m, *args)
|
304
|
-
else
|
305
|
-
super(m, *args)
|
306
|
-
end
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|