Class: Mongoose::Table

Inherits:
Object show all
Defined in:
lib/mongoose/table.rb

Overview


Table class


Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*values) ⇒ Table


initialize




369
370
371
# File 'lib/mongoose/table.rb', line 369

def initialize(*values)
  self.class.columns.zip(values).each { |c,v| send("#{c.name}=", v) }
end

Class Method Details

.add_column(col_name, col_def, col_class = Column) ⇒ Object


Table.add_column




204
205
206
207
208
209
210
211
212
213
# File 'lib/mongoose/table.rb', line 204

def self.add_column(col_name, col_def, col_class=Column)
  self.init_column(col_name, col_def, col_class)

  tbl_header = self.read_header

  tbl_header[:columns] << { :name => col_name, :data_type => col_def, 
                        :class => col_class.to_s }

  self.write_header(tbl_header)
end

.add_indexed_column(col_name, col_def) ⇒ Object


Table.add_indexed




218
219
220
# File 'lib/mongoose/table.rb', line 218

def self.add_indexed_column(col_name, col_def)
  self.add_column(col_name, col_def, SkipListIndexColumn)
end

.anyObject


Table.any




324
325
326
327
328
# File 'lib/mongoose/table.rb', line 324

def self.any
  self.query << :any_begin
  yield
  self.query << :any_end
end

.belongs_to(kind) ⇒ Object


Table.belongs_to




97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/mongoose/table.rb', line 97

def self.belongs_to(kind)
  table_name = kind.to_sym
  class_name = Util.us_case_to_class_case(table_name)

  define_method(kind) do
    klass = Object.const_get(class_name.to_s)
    klass.find(send("#{kind}_id".to_sym))
  end

  define_method("#{kind}=".to_sym) do |other|
    other.save
    send("#{kind}_id=".to_sym, other.id.to_i)
    save
  end
end

.closeObject


Table.close




180
181
182
# File 'lib/mongoose/table.rb', line 180

def self.close
  self.columns.each { |c| c.close }
end

.column_namesObject


Table.column_names




45
46
47
# File 'lib/mongoose/table.rb', line 45

def self.column_names
  self.db.tables[self][:columns].collect { |c| c.name }
end

.columnsObject


Table.columns




38
39
40
# File 'lib/mongoose/table.rb', line 38

def self.columns
  self.db.tables[self][:columns]
end

.dbObject


Table.db




10
11
12
# File 'lib/mongoose/table.rb', line 10

def self.db
  @@db
end

.db=(db) ⇒ Object


Table.db=




17
18
19
# File 'lib/mongoose/table.rb', line 17

def self.db=(db)
  @@db = db
end

.deleted_recs_counterObject


Table.deleted_recs_counter




123
124
125
# File 'lib/mongoose/table.rb', line 123

def self.deleted_recs_counter
  self.class.read_header[:deleted_recs_counter]
end

.find(*args) ⇒ Object


Table.find




225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/mongoose/table.rb', line 225

def self.find(*args)
  # If searching for just one id or a group of ids...
  if args[0].is_a?(Integer)
    if args.size == 1
      self.get_rec(args[0])
    else
      args.collect { |a| self.get_rec(a) }
    end
  else
    result = []
    # If passed a query block...
    if block_given?
      self.query.clear
      query_start = true
      sub_q = false
      sub_q_start = false
      sub_q_result = []

      # Grab the query block
      yield self 

      # Step through the query block...
      self.query.each_with_index do |q,i|
        # If this is the start of an #any sub-block within the main block,
        # mark it as so and start grabbing the sub-block query.
        if q == :any_begin
          sub_q = :true
          sub_q_start = true
          sub_q_result = []
          next
        end
        # If this is the end of an #any sub-block within the main block,
        # mark it as so, and add the sub-block's query results to the current
        # results of the main query.
        if q == :any_end
          sub_q = false
          if query_start
            query_start = false
            result = sub_q_result
          else
            result = result & sub_q_result
          end
          sub_q_result = nil
          next
        end

        # If currently within a sub-block query, execute and add it's results
        # to the current sub-block query results.
        if sub_q
          if sub_q_start
            sub_q_start = false
            sub_q_result = q[0].send(q[1], *q[2]) 
          else        
            sub_q_result = sub_q_result | q[0].send(q[1], *q[2])
          end
          next
        end

        # If this is the beginning of the query start a new result set, 
        # otherwise, add the results of the current line of the query to
        # the existing result set.
        if query_start
          query_start = false
          result = q[0].send(q[1], *q[2]) 
        else        
          result = result & q[0].send(q[1], *q[2])
        end
      end
    # If did not pass a query block, just grab all of the ids in the table...
    else  
      result = self.id.keys
    end

    # If no matching records found, return nil.
    if result.nil?
      nil
    # If user just wants first record, return first record.
    elsif args[0] == :first 
      self.get_rec(result.first)
    # If user specified a paramaters hash, see if they specified a limit and
    # return that many records.
    elsif args[1].is_a?(Hash)
      if args[1].has_key?(:limit)
        if args[1][:limit] == 1
          self.get_rec(result.first)
        else
          result[0...args[1][:limit]].collect { |r| self.get_rec(r) }
        end
      end
    # Otherwise, just return the whole damn result set.
    else
      result.collect { |r| self.get_rec(r) }
    end
  end
end

.get_all_recsObject


Table.get_all_recs




187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/mongoose/table.rb', line 187

def self.get_all_recs
  self.with_table do |fptr|
    begin
      while true
        fpos = fptr.tell
        rec_arr = Marshal.load(fptr)

        yield rec_arr[1..-1], fpos unless rec_arr[0]
      end
    rescue EOFError
    end
  end
end

.get_rec(id) ⇒ Object


Table.get_rec


Raises:

  • (IndexCorruptError)


333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/mongoose/table.rb', line 333

def self.get_rec(id)
  fpos = self.id[id]

  return nil if fpos.nil?
  rec_arr = []

  self.with_table(File::RDONLY) do |fptr|
    fptr.seek(fpos)
    rec_arr = Marshal.load(fptr)
  end      

  raise IndexCorruptError, "Index references deleted record!", caller \
   if rec_arr[0]

  raise IndexCorruptError, "Index ID does not match table ID!", caller \
   unless rec_arr[1] == id

  rec = self.new(*rec_arr[1..-1])
  return rec
end

.has_many(kind) ⇒ Object


Table.has_many




68
69
70
71
72
73
74
75
76
77
78
# File 'lib/mongoose/table.rb', line 68

def self.has_many(kind)
  table_name = Util.singularize(kind.to_s).to_sym
  class_name = Util.us_case_to_class_case(table_name)
  col = Util.col_name_for_class(self.to_s)

  define_method(kind.to_sym) do 
    klass = Object.const_get(class_name)
    Collection.new(self, klass.find { |r|
     r.send(col) == self.instance_eval { @id } })
  end
end

.has_one(kind) ⇒ Object


Table.has_one




83
84
85
86
87
88
89
90
91
92
# File 'lib/mongoose/table.rb', line 83

def self.has_one(kind)
  table_name = kind.to_sym
  class_name = Util.us_case_to_class_case(table_name)
  col = Util.col_name_for_class(self.to_s)

  define_method(kind.to_sym) do
    klass = Object.const_get(class_name)
    klass.find(:first) { |r| r.send(col) == self.instance_eval { @id } }
  end
end

.init_column(col_name, col_def, col_class) ⇒ Object


Table.init_column




158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/mongoose/table.rb', line 158

def self.init_column(col_name, col_def, col_class)
  col = col_class.create(self, col_name, col_def)

  self.columns << col

  meth = <<-END_OF_STRING
  def self.#{col_name}
    self.columns.detect { |c| c.name == "#{col_name}".to_sym } 
  end
  END_OF_STRING
  self.class_eval(meth)

  self.class_eval do
    attr_accessor col_name
  end

  col.init_index if col.indexed?
end

.init_tableObject


Table.init_table




146
147
148
149
150
151
152
153
# File 'lib/mongoose/table.rb', line 146

def self.init_table
  tbl_header = self.read_header

  self.read_header[:columns].each do |c| 
    self.init_column(c[:name], c[:data_type], Object.full_const_get(c[:class])
     ) 
  end
end

.last_id_usedObject


Table.last_id_used




116
117
118
# File 'lib/mongoose/table.rb', line 116

def self.last_id_used
  self.class.read_header[:last_id_used]
end

.pathObject


Table.path




52
53
54
# File 'lib/mongoose/table.rb', line 52

def self.path
  self.db.path
end

.queryObject


Table.query




24
25
26
# File 'lib/mongoose/table.rb', line 24

def self.query
  self.db.tables[self][:query]
end

.read_headerObject


Table.read_header




130
131
132
133
# File 'lib/mongoose/table.rb', line 130

def self.read_header
  YAML.load(File.open(File.join(self.path, self.table_name.to_s +
   TBL_HDR_EXT), 'r'))
end

.table_nameObject


Table.table_name




31
32
33
# File 'lib/mongoose/table.rb', line 31

def self.table_name
  self.db.tables[self][:table_name]
end

.validates_presence_of(*col_names) ⇒ Object


Table.validates_presence_of




59
60
61
62
63
# File 'lib/mongoose/table.rb', line 59

def self.validates_presence_of(*col_names)
  define_method(:required?) do |col_name|
    col_names.include?(col_name)
  end
end

.with_table(access = 'r') ⇒ Object


Table.with_table




357
358
359
360
361
362
363
364
# File 'lib/mongoose/table.rb', line 357

def self.with_table(access='r')
  begin
    yield fptr = open(File.join(self.db.path, self.table_name.to_s + TBL_EXT), 
     access)
  ensure
    fptr.close
  end
end

.write_header(header) ⇒ Object


Table.write_header




138
139
140
141
# File 'lib/mongoose/table.rb', line 138

def self.write_header(header)
  File.open(File.join(self.path, self.table_name.to_s + TBL_HDR_EXT), 'w'
   ) { |f| YAML.dump(header, f) }
end

Instance Method Details

#destroyObject


destroy




401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/mongoose/table.rb', line 401

def destroy
  fpos_rec_start = self.class.id[@id]

  self.class.with_table(File::RDWR) do |fptr|
    fptr.seek(fpos_rec_start)

    rec = Marshal.load(fptr)
    
    raise IndexCorruptError, "Index ID does not match table ID!", caller \
     unless rec[1] == @id

    # First array position of record is the deleted flag:  true means deleted
    rec[0] = true

    # Record is not actually deleted; it just has its deleted flag set to
    # true.
    write_record(fptr, fpos_rec_start, Marshal.dump(rec))
    increment_deleted_recs_counter
  end

  # Remove all index recs pointing to this record.
  self.class.columns.each_with_index do |c,i|
    if i == 0
      c.remove_index_rec(@id)
    elsif c.indexed?
      c.remove_index_rec(send(c.name), @id)
    end
  end

  # Don't allow any more changes to this record.
  freeze
end

#saveObject


save




376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/mongoose/table.rb', line 376

def save
  self.class.columns.each do |c|
    # First checks to see if validates_presence_of was set in class def.
    raise "Value required for #{c.name}!" if respond_to?('required?') and \
     required?(c.name) and send(c.name).nil?
    # Next checks to see if validates_presence_of was set in #add_column.
    raise "Value required for #{c.name}!" if c.required? and send(c.name).nil?
  end

  # Add new record.
  if @id.nil?
    id = increment_last_id_used
    append_record(id, self.class.column_names[1..-1].collect { |col_name| 
     send(col_name) })
    @id = id
  # Update existing record.
  else
    update_record(@id, self.class.columns[1..-1].collect { |c| send(c.name) })
  end
  return true
end