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