sequel_model 0.5.0.2 → 3.8.0
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.
- metadata +22 -69
- data/CHANGELOG +0 -111
- data/COPYING +0 -18
- data/README +0 -251
- data/Rakefile +0 -152
- data/lib/sequel_model.rb +0 -323
- data/lib/sequel_model/associations.rb +0 -325
- data/lib/sequel_model/base.rb +0 -119
- data/lib/sequel_model/caching.rb +0 -42
- data/lib/sequel_model/hooks.rb +0 -55
- data/lib/sequel_model/plugins.rb +0 -47
- data/lib/sequel_model/pretty_table.rb +0 -73
- data/lib/sequel_model/record.rb +0 -330
- data/lib/sequel_model/schema.rb +0 -48
- data/lib/sequel_model/validations.rb +0 -15
- data/spec/associations_spec.rb +0 -627
- data/spec/base_spec.rb +0 -239
- data/spec/caching_spec.rb +0 -150
- data/spec/deprecated_relations_spec.rb +0 -153
- data/spec/hooks_spec.rb +0 -269
- data/spec/model_spec.rb +0 -543
- data/spec/plugins_spec.rb +0 -74
- data/spec/rcov.opts +0 -4
- data/spec/record_spec.rb +0 -575
- data/spec/schema_spec.rb +0 -69
- data/spec/spec.opts +0 -5
- data/spec/spec_helper.rb +0 -43
- data/spec/validations_spec.rb +0 -246
data/lib/sequel_model/hooks.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
class Model
|
3
|
-
HOOKS = [
|
4
|
-
:after_initialize,
|
5
|
-
:before_create,
|
6
|
-
:after_create,
|
7
|
-
:before_update,
|
8
|
-
:after_update,
|
9
|
-
:before_save,
|
10
|
-
:after_save,
|
11
|
-
:before_destroy,
|
12
|
-
:after_destroy
|
13
|
-
]
|
14
|
-
|
15
|
-
# Some fancy code generation here in order to define the hook class methods...
|
16
|
-
HOOK_METHOD_STR = %Q{
|
17
|
-
def self.%s(method = nil, &block)
|
18
|
-
unless block
|
19
|
-
(raise SequelError, 'No hook method specified') unless method
|
20
|
-
block = proc {send method}
|
21
|
-
end
|
22
|
-
add_hook(%s, &block)
|
23
|
-
end
|
24
|
-
}
|
25
|
-
|
26
|
-
def self.def_hook_method(m) #:nodoc:
|
27
|
-
instance_eval(HOOK_METHOD_STR % [m.to_s, m.inspect])
|
28
|
-
end
|
29
|
-
|
30
|
-
HOOKS.each {|h| define_method(h) {}}
|
31
|
-
HOOKS.each {|h| def_hook_method(h)}
|
32
|
-
|
33
|
-
# Returns the hooks hash for the model class.
|
34
|
-
def self.hooks #:nodoc:
|
35
|
-
@hooks ||= Hash.new {|h, k| h[k] = []}
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.add_hook(hook, &block) #:nodoc:
|
39
|
-
chain = hooks[hook]
|
40
|
-
chain << block
|
41
|
-
define_method(hook) do
|
42
|
-
return false if super == false
|
43
|
-
chain.each {|h| break false if instance_eval(&h) == false}
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns true if the model class or any of its ancestors have defined
|
48
|
-
# hooks for the given hook key. Notice that this method cannot detect
|
49
|
-
# hooks defined using overridden methods.
|
50
|
-
def self.has_hooks?(key)
|
51
|
-
has = hooks[key] && !hooks[key].empty?
|
52
|
-
has || ((self != Model) && superclass.has_hooks?(key))
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/sequel_model/plugins.rb
DELETED
@@ -1,47 +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
|
-
unless @dataset
|
23
|
-
raise Sequel::Error, "Plugin cannot be applied because the model class has no dataset"
|
24
|
-
end
|
25
|
-
dataset.meta_def(:"#{plugin}_opts") {args.first}
|
26
|
-
dataset.metaclass.send(:include, m::DatasetMethods)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
alias_method :is_a, :is
|
30
|
-
|
31
|
-
# Returns the module for the specified plugin. If the module is not
|
32
|
-
# defined, the corresponding plugin gem is automatically loaded.
|
33
|
-
def plugin_module(plugin)
|
34
|
-
module_name = plugin.to_s.gsub(/(^|_)(.)/) {$2.upcase}
|
35
|
-
if not Sequel::Plugins.const_defined?(module_name)
|
36
|
-
require plugin_gem(plugin)
|
37
|
-
end
|
38
|
-
Sequel::Plugins.const_get(module_name)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Returns the gem name for the given plugin.
|
42
|
-
def plugin_gem(plugin)
|
43
|
-
"sequel_#{plugin}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
module Sequel
|
2
|
-
# Prints nice-looking plain-text tables
|
3
|
-
# +--+-------+
|
4
|
-
# |id|name |
|
5
|
-
# |--+-------|
|
6
|
-
# |1 |fasdfas|
|
7
|
-
# |2 |test |
|
8
|
-
# +--+-------+
|
9
|
-
module PrettyTable
|
10
|
-
def self.records_columns(records)
|
11
|
-
columns = []
|
12
|
-
records.each do |r|
|
13
|
-
if Array === r && (k = r.keys)
|
14
|
-
return k
|
15
|
-
elsif Hash === r
|
16
|
-
r.keys.each {|k| columns << k unless columns.include?(k)}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
columns
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.column_sizes(records, columns)
|
23
|
-
sizes = Hash.new {0}
|
24
|
-
columns.each do |c|
|
25
|
-
s = c.to_s.size
|
26
|
-
sizes[c.to_sym] = s if s > sizes[c.to_sym]
|
27
|
-
end
|
28
|
-
records.each do |r|
|
29
|
-
columns.each do |c|
|
30
|
-
s = r[c].to_s.size
|
31
|
-
sizes[c.to_sym] = s if s > sizes[c.to_sym]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
sizes
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.separator_line(columns, sizes)
|
38
|
-
l = ''
|
39
|
-
'+' + columns.map {|c| '-' * sizes[c]}.join('+') + '+'
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.format_cell(size, v)
|
43
|
-
case v
|
44
|
-
when Bignum, Fixnum
|
45
|
-
"%#{size}d" % v
|
46
|
-
when Float
|
47
|
-
"%#{size}g" % v
|
48
|
-
else
|
49
|
-
"%-#{size}s" % v.to_s
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.data_line(columns, sizes, record)
|
54
|
-
'|' + columns.map {|c| format_cell(sizes[c], record[c])}.join('|') + '|'
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.header_line(columns, sizes)
|
58
|
-
'|' + columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') + '|'
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.print(records, columns = nil) # records is an array of hashes
|
62
|
-
columns ||= records_columns(records)
|
63
|
-
sizes = column_sizes(records, columns)
|
64
|
-
|
65
|
-
puts separator_line(columns, sizes)
|
66
|
-
puts header_line(columns, sizes)
|
67
|
-
puts separator_line(columns, sizes)
|
68
|
-
records.each {|r| puts data_line(columns, sizes, r)}
|
69
|
-
puts separator_line(columns, sizes)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
data/lib/sequel_model/record.rb
DELETED
@@ -1,330 +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
|
-
@changed_columns << column unless @changed_columns.include?(column)
|
13
|
-
@values[column] = value
|
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
|
57
|
-
# # composite key
|
58
|
-
# set_primary_key :taggable_id, :tag_id
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# class Person < Sequel::Model
|
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 = {}, &block)
|
131
|
-
db.transaction do
|
132
|
-
obj = new(values, &block)
|
133
|
-
obj.save
|
134
|
-
obj
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# Updates the instance with the supplied values with support for virtual
|
139
|
-
# attributes, ignoring any values for which no setter method is available.
|
140
|
-
def update_with_params(values)
|
141
|
-
c = columns
|
142
|
-
values.each do |k, v| m = :"#{k}="
|
143
|
-
send(m, v) if c.include?(k) || respond_to?(m)
|
144
|
-
end
|
145
|
-
save_changes
|
146
|
-
end
|
147
|
-
alias_method :update_with, :update_with_params
|
148
|
-
|
149
|
-
class << self
|
150
|
-
def create_with_params(params)
|
151
|
-
record = new
|
152
|
-
record.update_with_params(params)
|
153
|
-
record
|
154
|
-
end
|
155
|
-
alias_method :create_with, :create_with_params
|
156
|
-
end
|
157
|
-
|
158
|
-
# Returns (naked) dataset bound to current instance.
|
159
|
-
def this
|
160
|
-
@this ||= self.class.dataset.filter(:id => @values[:id]).limit(1).naked
|
161
|
-
end
|
162
|
-
|
163
|
-
# Returns a key unique to the underlying record for caching
|
164
|
-
def cache_key
|
165
|
-
pk = @values[:id] || (raise Error, 'no primary key for this record')
|
166
|
-
@cache_key ||= "#{self.class}:#{pk}"
|
167
|
-
end
|
168
|
-
|
169
|
-
# Returns primary key column(s) for object's Model class.
|
170
|
-
def primary_key
|
171
|
-
@primary_key ||= self.class.primary_key
|
172
|
-
end
|
173
|
-
|
174
|
-
# Returns the primary key value identifying the model instance. If the
|
175
|
-
# model's primary key is changed (using #set_primary_key or #no_primary_key)
|
176
|
-
# this method is redefined accordingly.
|
177
|
-
def pk
|
178
|
-
@pk ||= @values[:id]
|
179
|
-
end
|
180
|
-
|
181
|
-
# Returns a hash identifying the model instance. Stock implementation.
|
182
|
-
def pk_hash
|
183
|
-
@pk_hash ||= {:id => @values[:id]}
|
184
|
-
end
|
185
|
-
|
186
|
-
# Creates new instance with values set to passed-in Hash.
|
187
|
-
#
|
188
|
-
# This method guesses whether the record exists when
|
189
|
-
# <tt>new_record</tt> is set to false.
|
190
|
-
def initialize(values = nil, from_db = false, &block)
|
191
|
-
@changed_columns = []
|
192
|
-
unless from_db
|
193
|
-
@values = {}
|
194
|
-
if values
|
195
|
-
values.each do |k, v| m = :"#{k}="
|
196
|
-
if respond_to?(m)
|
197
|
-
send(m, v)
|
198
|
-
values.delete(k)
|
199
|
-
end
|
200
|
-
end
|
201
|
-
values.inject(@values) {|m, kv| m[kv[0].to_sym] = kv[1]; m}
|
202
|
-
# @values.merge!(values)
|
203
|
-
end
|
204
|
-
else
|
205
|
-
@values = values || {}
|
206
|
-
end
|
207
|
-
|
208
|
-
k = primary_key
|
209
|
-
@new = !from_db
|
210
|
-
|
211
|
-
block[self] if block
|
212
|
-
after_initialize
|
213
|
-
end
|
214
|
-
|
215
|
-
# Initializes a model instance as an existing record. This constructor is
|
216
|
-
# used by Sequel to initialize model instances when fetching records.
|
217
|
-
def self.load(values)
|
218
|
-
new(values, true)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Returns true if the current instance represents a new record.
|
222
|
-
def new?
|
223
|
-
@new
|
224
|
-
end
|
225
|
-
alias :new_record? :new?
|
226
|
-
|
227
|
-
# Returns true when current instance exists, false otherwise.
|
228
|
-
def exists?
|
229
|
-
this.count > 0
|
230
|
-
end
|
231
|
-
|
232
|
-
# Creates or updates the associated record. This method can also
|
233
|
-
# accept a list of specific columns to update.
|
234
|
-
def save(*columns)
|
235
|
-
before_save
|
236
|
-
if @new
|
237
|
-
before_create
|
238
|
-
iid = model.dataset.insert(@values)
|
239
|
-
# if we have a regular primary key and it's not set in @values,
|
240
|
-
# we assume it's the last inserted id
|
241
|
-
if (pk = primary_key) && !(Array === pk) && !@values[pk]
|
242
|
-
@values[pk] = iid
|
243
|
-
end
|
244
|
-
if pk
|
245
|
-
@this = nil # remove memoized this dataset
|
246
|
-
refresh
|
247
|
-
end
|
248
|
-
@new = false
|
249
|
-
after_create
|
250
|
-
else
|
251
|
-
before_update
|
252
|
-
if columns.empty?
|
253
|
-
this.update(@values)
|
254
|
-
@changed_columns = []
|
255
|
-
else # update only the specified columns
|
256
|
-
this.update(@values.reject {|k, v| !columns.include?(k)})
|
257
|
-
@changed_columns.reject! {|c| columns.include?(c)}
|
258
|
-
end
|
259
|
-
after_update
|
260
|
-
end
|
261
|
-
after_save
|
262
|
-
self
|
263
|
-
end
|
264
|
-
|
265
|
-
# Saves only changed columns or does nothing if no columns are marked as
|
266
|
-
# chanaged.
|
267
|
-
def save_changes
|
268
|
-
save(*@changed_columns) unless @changed_columns.empty?
|
269
|
-
end
|
270
|
-
|
271
|
-
# Updates and saves values to database from the passed-in Hash.
|
272
|
-
def set(values)
|
273
|
-
v = values.inject({}) {|m, kv| m[kv[0].to_sym] = kv[1]; m}
|
274
|
-
this.update(v)
|
275
|
-
v.each {|k, v| @values[k] = v}
|
276
|
-
end
|
277
|
-
alias_method :update, :set
|
278
|
-
|
279
|
-
# Reloads values from database and returns self.
|
280
|
-
def refresh
|
281
|
-
@values = this.first || raise(Error, "Record not found")
|
282
|
-
self
|
283
|
-
end
|
284
|
-
alias_method :reload, :refresh
|
285
|
-
|
286
|
-
# Like delete but runs hooks before and after delete.
|
287
|
-
def destroy
|
288
|
-
db.transaction do
|
289
|
-
before_destroy
|
290
|
-
delete
|
291
|
-
after_destroy
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
# Deletes and returns self.
|
296
|
-
def delete
|
297
|
-
this.delete
|
298
|
-
self
|
299
|
-
end
|
300
|
-
|
301
|
-
ATTR_RE = /^([a-zA-Z_]\w*)(=)?$/.freeze
|
302
|
-
EQUAL_SIGN = '='.freeze
|
303
|
-
|
304
|
-
def method_missing(m, *args) #:nodoc:
|
305
|
-
if m.to_s =~ ATTR_RE
|
306
|
-
att = $1.to_sym
|
307
|
-
write = $2 == EQUAL_SIGN
|
308
|
-
|
309
|
-
# check whether the column is legal
|
310
|
-
unless @values.has_key?(att) || columns.include?(att)
|
311
|
-
raise Error, "Invalid column (#{att.inspect}) for #{self}"
|
312
|
-
end
|
313
|
-
|
314
|
-
# define the column accessor
|
315
|
-
Thread.exclusive do
|
316
|
-
if write
|
317
|
-
model.class_def(m) {|v| self[att] = v}
|
318
|
-
else
|
319
|
-
model.class_def(m) {self[att]}
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
# call the accessor
|
324
|
-
respond_to?(m) ? send(m, *args) : super(m, *args)
|
325
|
-
else
|
326
|
-
super(m, *args)
|
327
|
-
end
|
328
|
-
end
|
329
|
-
end
|
330
|
-
end
|