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