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