sequel_model 0.5.0.2 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,325 +0,0 @@
|
|
1
|
-
# Associations are used in order to specify relationships between model classes
|
2
|
-
# that reflect relations between tables in the database using foreign keys.
|
3
|
-
#
|
4
|
-
# Each kind of association adds a number of methods to the model class which
|
5
|
-
# are specialized according to the association type and optional parameters
|
6
|
-
# given in the definition. Example:
|
7
|
-
#
|
8
|
-
# class Project < Sequel::Model
|
9
|
-
# belongs_to :portfolio
|
10
|
-
# has_many :milestones
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# The project class now has the following methods:
|
14
|
-
# * Project#portfolio, Project#portfolio=
|
15
|
-
# * Project#milestones, Project#add_milestone, Project#remove_milestone
|
16
|
-
#
|
17
|
-
# By default the classes for the associations are inferred from the association
|
18
|
-
# name, so for example the Project#portfolio will return an instance of
|
19
|
-
# Portfolio, and Project#milestones will return a dataset of Milestone
|
20
|
-
# instances, in similar fashion to how ActiveRecord infers class names.
|
21
|
-
#
|
22
|
-
# Association definitions are also reflected by the class, e.g.:
|
23
|
-
#
|
24
|
-
# >> Project.associations
|
25
|
-
# => [:portfolio, :milestones]
|
26
|
-
# >> Project.association_reflection(:portfolio)
|
27
|
-
# => {:kind => :many_to_one, :name => :portfolio, :class_name => "Portfolio"}
|
28
|
-
#
|
29
|
-
# The following association kinds are supported:
|
30
|
-
# * :many_to_one - Foreign key in current model's table points to
|
31
|
-
# associated model's primary key. Each associated model object can
|
32
|
-
# be associated with more than one current model objects. Each current
|
33
|
-
# model object can be associated with only one associated model object.
|
34
|
-
# Similar to ActiveRecord/DataMapper's belongs_to.
|
35
|
-
# * :one_to_many - Foreign key in associated model's table points to this
|
36
|
-
# model's primary key. Each current model object can be associated with
|
37
|
-
# more than one associated model objects. Each associated model object
|
38
|
-
# can be associated with only one current model object.
|
39
|
-
# Similar to ActiveRecord/DataMapper's has_many.
|
40
|
-
# * :many_to_many - A join table is used that has a foreign key that points
|
41
|
-
# to this model's primary key and a foreign key that points to the
|
42
|
-
# associated model's primary key. Each current model object can be
|
43
|
-
# associated with many associated model objects, and each associated
|
44
|
-
# model object can be associated with many current model objects.
|
45
|
-
# Similar to ActiveRecord/DataMapper's has_and_belongs_to_many.
|
46
|
-
#
|
47
|
-
# Associations can be defined by either using the associate method, or by
|
48
|
-
# calling one of the three methods: many_to_one, one_to_many, many_to_many.
|
49
|
-
# Sequel::Model also provides aliases for these methods that conform to
|
50
|
-
# ActiveRecord conventions: belongs_to, has_many, has_and_belongs_to_many.
|
51
|
-
# For example, the following two statements are equivalent:
|
52
|
-
#
|
53
|
-
# associate :one_to_many, :attributes
|
54
|
-
# one_to_many :attributes
|
55
|
-
module Sequel::Model::Associations
|
56
|
-
# Array of all association reflections
|
57
|
-
def all_association_reflections
|
58
|
-
association_reflections.values
|
59
|
-
end
|
60
|
-
|
61
|
-
# Associates a related model with the current model. The following types are
|
62
|
-
# supported:
|
63
|
-
#
|
64
|
-
# * :many_to_one - Foreign key in current model's table points to
|
65
|
-
# associated model's primary key. Each associated model object can
|
66
|
-
# be associated with more than one current model objects. Each current
|
67
|
-
# model object can be associated with only one associated model object.
|
68
|
-
# Similar to ActiveRecord/DataMapper's belongs_to.
|
69
|
-
# * :one_to_many - Foreign key in associated model's table points to this
|
70
|
-
# model's primary key. Each current model object can be associated with
|
71
|
-
# more than one associated model objects. Each associated model object
|
72
|
-
# can be associated with only one current model object.
|
73
|
-
# Similar to ActiveRecord/DataMapper's has_many.
|
74
|
-
# * :many_to_many - A join table is used that has a foreign key that points
|
75
|
-
# to this model's primary key and a foreign key that points to the
|
76
|
-
# associated model's primary key. Each current model object can be
|
77
|
-
# associated with many associated model objects, and each associated
|
78
|
-
# model object can be associated with many current model objects.
|
79
|
-
# Similar to ActiveRecord/DataMapper's has_and_belongs_to_many.
|
80
|
-
#
|
81
|
-
# The following options can be supplied:
|
82
|
-
# * *ALL types*:
|
83
|
-
# - :class - The associated class or its name. If not
|
84
|
-
# given, uses the association's name, which is camelized (and
|
85
|
-
# singularized if type is :{one,many}_to_many)
|
86
|
-
# * :many_to_one:
|
87
|
-
# - :key - foreign_key in current model's table that references
|
88
|
-
# associated model's primary key, as a symbol. Defaults to :"#{name}_id".
|
89
|
-
# * :one_to_many:
|
90
|
-
# - :key - foreign key in associated model's table that references
|
91
|
-
# current model's primary key, as a symbol. Defaults to
|
92
|
-
# :"#{self.name.underscore}_id".
|
93
|
-
# - :order - the column by which to order the association dataset.
|
94
|
-
# - :cache - set to true to cache and return an array of objects instead of a dataset.
|
95
|
-
# * :many_to_many:
|
96
|
-
# - :join_table - name of table that includes the foreign keys to both
|
97
|
-
# the current model and the associated model, as a symbol. Defaults to the name
|
98
|
-
# of current model and name of associated model, pluralized,
|
99
|
-
# underscored, sorted, and joined with '_'.
|
100
|
-
# - :left_key - foreign key in join table that points to current model's
|
101
|
-
# primary key, as a symbol.
|
102
|
-
# - :right_key - foreign key in join table that points to associated
|
103
|
-
# model's primary key, as a symbol.
|
104
|
-
# - :order - the column by which to order the association dataset.
|
105
|
-
# - :cache - set to true to cache and return an array of objects instead of a dataset.
|
106
|
-
def associate(type, name, opts = {}, &block)
|
107
|
-
# check arguments
|
108
|
-
raise ArgumentError unless [:many_to_one, :one_to_many, :many_to_many].include?(type) && Symbol === name
|
109
|
-
|
110
|
-
# merge early so we don't modify opts
|
111
|
-
opts = opts.merge(:type => type, :name => name, :block => block)
|
112
|
-
|
113
|
-
# deprecation
|
114
|
-
if opts[:from]
|
115
|
-
STDERR << "The :from option is deprecated, please use the :class option instead.\r\n"
|
116
|
-
opts[:class] = opts[:from]
|
117
|
-
end
|
118
|
-
|
119
|
-
# find class
|
120
|
-
case opts[:class]
|
121
|
-
when String, Symbol
|
122
|
-
# Delete :class to allow late binding
|
123
|
-
opts[:class_name] ||= opts.delete(:class).to_s
|
124
|
-
when Class
|
125
|
-
opts[:class_name] ||= opts[:class].name
|
126
|
-
end
|
127
|
-
|
128
|
-
send(:"def_#{type}", name, opts)
|
129
|
-
|
130
|
-
# don't add to association_reflections until we are sure there are no errors
|
131
|
-
association_reflections[name] = opts
|
132
|
-
end
|
133
|
-
|
134
|
-
# The association reflection hash for the association of the given name.
|
135
|
-
def association_reflection(name)
|
136
|
-
association_reflections[name]
|
137
|
-
end
|
138
|
-
|
139
|
-
# Array of association name symbols
|
140
|
-
def associations
|
141
|
-
association_reflections.keys
|
142
|
-
end
|
143
|
-
|
144
|
-
# Shortcut for adding a many_to_one association, see associate
|
145
|
-
def many_to_one(*args, &block)
|
146
|
-
associate(:many_to_one, *args, &block)
|
147
|
-
end
|
148
|
-
alias_method :belongs_to, :many_to_one
|
149
|
-
|
150
|
-
# Shortcut for adding a one_to_many association, see associate
|
151
|
-
def one_to_many(*args, &block)
|
152
|
-
associate(:one_to_many, *args, &block)
|
153
|
-
end
|
154
|
-
alias_method :has_many, :one_to_many
|
155
|
-
|
156
|
-
# deprecated, please use many_to_one instead
|
157
|
-
def one_to_one(*args, &block)
|
158
|
-
STDERR << "one_to_one relation definitions are deprecated, please use many_to_one instead.\r\n"
|
159
|
-
many_to_one(*args, &block)
|
160
|
-
end
|
161
|
-
|
162
|
-
# Shortcut for adding a many_to_many association, see associate
|
163
|
-
def many_to_many(*args, &block)
|
164
|
-
associate(:many_to_many, *args, &block)
|
165
|
-
end
|
166
|
-
alias_method :has_and_belongs_to_many, :many_to_many
|
167
|
-
|
168
|
-
private
|
169
|
-
def association_ivar(name)
|
170
|
-
:"@#{name}"
|
171
|
-
end
|
172
|
-
|
173
|
-
def association_add_method_name(name)
|
174
|
-
:"add_#{name.to_s.singularize}"
|
175
|
-
end
|
176
|
-
|
177
|
-
def association_remove_method_name(name)
|
178
|
-
:"remove_#{name.to_s.singularize}"
|
179
|
-
end
|
180
|
-
|
181
|
-
def default_remote_key
|
182
|
-
:"#{name.demodulize.underscore}_id"
|
183
|
-
end
|
184
|
-
|
185
|
-
# The class related to the given association reflection
|
186
|
-
def associated_class(opts)
|
187
|
-
opts[:class] ||= opts[:class_name].constantize
|
188
|
-
end
|
189
|
-
|
190
|
-
# Hash storing the association reflections. Keys are association name
|
191
|
-
# symbols, values are association reflection hashes.
|
192
|
-
def association_reflections
|
193
|
-
@association_reflections ||= {}
|
194
|
-
end
|
195
|
-
|
196
|
-
def def_many_to_one(name, opts)
|
197
|
-
assoc_class = method(:associated_class) # late binding of association dataset
|
198
|
-
ivar = association_ivar(name)
|
199
|
-
|
200
|
-
key = (opts[:key] ||= :"#{name}_id")
|
201
|
-
opts[:class_name] ||= name.to_s.camelize
|
202
|
-
|
203
|
-
def_association_getter(name) {(fk = send(key)) ? assoc_class[opts][fk] : nil}
|
204
|
-
class_def(:"#{name}=") do |o|
|
205
|
-
instance_variable_set(ivar, o)
|
206
|
-
send(:"#{key}=", (o.pk if o))
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def def_one_to_many(name, opts)
|
211
|
-
assoc_class = method(:associated_class) # late binding of association dataset
|
212
|
-
ivar = association_ivar(name)
|
213
|
-
key = (opts[:key] ||= default_remote_key)
|
214
|
-
opts[:class_name] ||= name.to_s.singularize.camelize
|
215
|
-
|
216
|
-
def_association_dataset_methods(name, opts) {assoc_class[opts].filter(key => pk)}
|
217
|
-
|
218
|
-
# define add_xxx, remove_xxx methods
|
219
|
-
class_def(association_add_method_name(name)) do |o|
|
220
|
-
o.send(:"#{key}=", pk); o.save!
|
221
|
-
if arr = instance_variable_get(ivar)
|
222
|
-
arr.push(o)
|
223
|
-
end
|
224
|
-
o
|
225
|
-
end
|
226
|
-
class_def(association_remove_method_name(name)) do |o|
|
227
|
-
o.send(:"#{key}=", nil); o.save!
|
228
|
-
if arr = instance_variable_get(ivar)
|
229
|
-
arr.delete(o)
|
230
|
-
end
|
231
|
-
o
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
def default_join_table_name(opts)
|
236
|
-
([opts[:class_name], self.name.demodulize]. \
|
237
|
-
map{|i| i.pluralize.underscore}.sort.join('_')).to_sym
|
238
|
-
end
|
239
|
-
|
240
|
-
def def_many_to_many(name, opts)
|
241
|
-
assoc_class = method(:associated_class) # late binding of association dataset
|
242
|
-
ivar = association_ivar(name)
|
243
|
-
left = (opts[:left_key] ||= default_remote_key)
|
244
|
-
right = (opts[:right_key] ||= :"#{name.to_s.singularize}_id")
|
245
|
-
opts[:class_name] ||= name.to_s.singularize.camelize
|
246
|
-
join_table = (opts[:join_table] ||= default_join_table_name(opts))
|
247
|
-
database = db
|
248
|
-
|
249
|
-
def_association_dataset_methods(name, opts) do
|
250
|
-
klass = assoc_class[opts]
|
251
|
-
key = (opts[:right_primary_key] ||= :"#{klass.table_name}__#{klass.primary_key}")
|
252
|
-
klass.inner_join(join_table, right => key, left => pk)
|
253
|
-
end
|
254
|
-
|
255
|
-
class_def(association_add_method_name(name)) do |o|
|
256
|
-
database[join_table].insert(left => pk, right => o.pk)
|
257
|
-
if arr = instance_variable_get(ivar)
|
258
|
-
arr.push(o)
|
259
|
-
end
|
260
|
-
o
|
261
|
-
end
|
262
|
-
class_def(association_remove_method_name(name)) do |o|
|
263
|
-
database[join_table].filter(left => pk, right => o.pk).delete
|
264
|
-
if arr = instance_variable_get(ivar)
|
265
|
-
arr.delete(o)
|
266
|
-
end
|
267
|
-
o
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# Defines an association getter method, caching the block result in an
|
272
|
-
# instance variable. The defined method takes an optional reload parameter
|
273
|
-
# that can be set to true in order to bypass the cache.
|
274
|
-
def def_association_getter(name, &block)
|
275
|
-
ivar = association_ivar(name)
|
276
|
-
class_def(name) do |*reload|
|
277
|
-
if !reload[0] && obj = instance_variable_get(ivar)
|
278
|
-
obj
|
279
|
-
else
|
280
|
-
instance_variable_set(ivar, instance_eval(&block))
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
# Defines an association
|
286
|
-
def def_association_dataset_methods(name, opts, &block)
|
287
|
-
dataset_method = :"#{name}_dataset"
|
288
|
-
dataset_block = opts[:block]
|
289
|
-
ivar = association_ivar(name)
|
290
|
-
|
291
|
-
# define a method returning the association dataset (with optional order)
|
292
|
-
if order = opts[:order]
|
293
|
-
class_def(dataset_method) {instance_eval(&block).order(order)}
|
294
|
-
else
|
295
|
-
class_def(dataset_method, &block)
|
296
|
-
end
|
297
|
-
|
298
|
-
if opts[:cache]
|
299
|
-
# if the :cache option is set to true, the association method should return
|
300
|
-
# an array of association objects
|
301
|
-
class_def(name) do |*reload|
|
302
|
-
if !reload[0] && obj = instance_variable_get(ivar)
|
303
|
-
obj
|
304
|
-
else
|
305
|
-
ds = send(dataset_method)
|
306
|
-
# if the a dataset block was specified, we need to call it and use
|
307
|
-
# the result as the dataset to fetch records from.
|
308
|
-
if dataset_block
|
309
|
-
ds = dataset_block[ds]
|
310
|
-
end
|
311
|
-
instance_variable_set(ivar, ds.all)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
elsif dataset_block
|
315
|
-
# no cache, but we still need to check if a dataset block was given.
|
316
|
-
# define helper so the supplied block will be instance_eval'ed
|
317
|
-
class_def(:"#{name}_helper", &dataset_block)
|
318
|
-
class_def(name) {send(:"#{name}_helper", send(dataset_method))}
|
319
|
-
else
|
320
|
-
# otherwise (by default), the association method is an alias to the
|
321
|
-
# association dataset method.
|
322
|
-
alias_method name, dataset_method
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
data/lib/sequel_model/base.rb
DELETED
@@ -1,119 +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
|
-
if @dataset
|
13
|
-
set_dataset(db[table_name])
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# Called when a database is opened in order to automatically associate the
|
18
|
-
# first opened database with model classes.
|
19
|
-
def self.database_opened(db)
|
20
|
-
@db = db if (self == Model) && !@db
|
21
|
-
end
|
22
|
-
|
23
|
-
# Returns the implicit table name for the model class.
|
24
|
-
def self.implicit_table_name
|
25
|
-
name.demodulize.underscore.pluralize.to_sym
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns the dataset associated with the Model class.
|
29
|
-
def self.dataset
|
30
|
-
unless @dataset
|
31
|
-
if ds = super_dataset
|
32
|
-
set_dataset(ds.clone)
|
33
|
-
elsif !name.empty?
|
34
|
-
set_dataset(db[implicit_table_name])
|
35
|
-
else
|
36
|
-
raise Error, "No dataset associated with #{self}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
@dataset
|
40
|
-
end
|
41
|
-
|
42
|
-
# def self.dataset
|
43
|
-
# @dataset ||= super_dataset ||
|
44
|
-
# (!(n = name).empty? && db[n.underscore.pluralize.to_sym]) ||
|
45
|
-
# (raise Error, "No dataset associated with #{self}")
|
46
|
-
# end
|
47
|
-
|
48
|
-
def self.super_dataset # :nodoc:
|
49
|
-
superclass.dataset if (superclass != Sequel::Model) && superclass.respond_to?(:dataset)
|
50
|
-
end
|
51
|
-
|
52
|
-
# Returns the columns in the result set in their original order.
|
53
|
-
#
|
54
|
-
# See Dataset#columns for more information.
|
55
|
-
def self.columns
|
56
|
-
@columns ||= dataset.columns or
|
57
|
-
raise Error, "Could not fetch columns for #{self}"
|
58
|
-
end
|
59
|
-
|
60
|
-
# Sets the dataset associated with the Model class.
|
61
|
-
def self.set_dataset(ds)
|
62
|
-
@db = ds.db
|
63
|
-
@dataset = ds
|
64
|
-
@dataset.set_model(self)
|
65
|
-
@dataset.transform(@transform) if @transform
|
66
|
-
end
|
67
|
-
|
68
|
-
# Returns the database assoiated with the object's Model class.
|
69
|
-
def db
|
70
|
-
@db ||= model.db
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the dataset assoiated with the object's Model class.
|
74
|
-
#
|
75
|
-
# See Dataset for more information.
|
76
|
-
def dataset
|
77
|
-
model.dataset
|
78
|
-
end
|
79
|
-
|
80
|
-
# Returns the columns associated with the object's Model class.
|
81
|
-
def columns
|
82
|
-
model.columns
|
83
|
-
end
|
84
|
-
|
85
|
-
# Serializes column with YAML or through marshalling.
|
86
|
-
def self.serialize(*columns)
|
87
|
-
format = columns.pop[:format] if Hash === columns.last
|
88
|
-
format ||= :yaml
|
89
|
-
|
90
|
-
@transform = columns.inject({}) do |m, c|
|
91
|
-
m[c] = format
|
92
|
-
m
|
93
|
-
end
|
94
|
-
@dataset.transform(@transform) if @dataset
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Lets you create a Model class with its table name already set or reopen
|
99
|
-
# an existing Model.
|
100
|
-
#
|
101
|
-
# Makes given dataset inherited.
|
102
|
-
#
|
103
|
-
# === Example:
|
104
|
-
# class Comment < Sequel::Model(:something)
|
105
|
-
# table_name # => :something
|
106
|
-
#
|
107
|
-
# # ...
|
108
|
-
#
|
109
|
-
# end
|
110
|
-
def self.Model(source)
|
111
|
-
@models ||= {}
|
112
|
-
@models[source] ||= Class.new(Sequel::Model) do
|
113
|
-
meta_def(:inherited) do |c|
|
114
|
-
c.set_dataset(source.is_a?(Dataset) ? source : c.db[source])
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
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
|